← back to Watches

public/search-interface.html

909 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Omega Watch Search - Advanced Search & Filters</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            overflow: hidden;
        }

        header {
            background: linear-gradient(135deg, #1e3c72, #2a5298);
            color: white;
            padding: 40px;
            text-align: center;
        }

        h1 {
            font-size: 2.5em;
            font-weight: 700;
            margin-bottom: 10px;
        }

        .subtitle {
            opacity: 0.9;
            font-size: 1.1em;
        }

        /* Search Section */
        .search-section {
            background: #f8f9fa;
            padding: 30px;
            border-bottom: 2px solid #e9ecef;
        }

        .search-container {
            display: flex;
            gap: 15px;
            margin-bottom: 20px;
        }

        .search-input-wrapper {
            flex: 1;
            position: relative;
        }

        .search-input {
            width: 100%;
            padding: 15px 20px 15px 50px;
            font-size: 16px;
            border: 2px solid #dee2e6;
            border-radius: 10px;
            transition: all 0.3s;
        }

        .search-input:focus {
            outline: none;
            border-color: #667eea;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
        }

        .search-icon {
            position: absolute;
            left: 15px;
            top: 50%;
            transform: translateY(-50%);
            color: #6c757d;
        }

        .search-btn {
            padding: 15px 30px;
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border: none;
            border-radius: 10px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: transform 0.2s;
        }

        .search-btn:hover {
            transform: translateY(-2px);
        }

        .clear-btn {
            padding: 15px 30px;
            background: #6c757d;
            color: white;
            border: none;
            border-radius: 10px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
        }

        /* Sort Section */
        .sort-section {
            display: flex;
            align-items: center;
            gap: 20px;
            margin-bottom: 20px;
        }

        .sort-label {
            font-weight: 600;
            color: #495057;
        }

        .sort-select {
            padding: 10px 20px;
            font-size: 15px;
            border: 2px solid #dee2e6;
            border-radius: 8px;
            background: white;
            cursor: pointer;
            min-width: 200px;
        }

        /* Filter Buttons */
        .filter-section {
            margin-bottom: 20px;
        }

        .filter-group {
            margin-bottom: 15px;
        }

        .filter-label {
            font-weight: 600;
            color: #495057;
            margin-bottom: 10px;
            display: block;
        }

        .filter-buttons {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }

        .filter-btn {
            padding: 10px 20px;
            border: 2px solid #dee2e6;
            background: white;
            border-radius: 25px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s;
        }

        .filter-btn:hover {
            border-color: #667eea;
            background: #f8f9ff;
        }

        .filter-btn.active {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border-color: transparent;
        }

        /* Price Range */
        .price-range-container {
            display: flex;
            gap: 15px;
            align-items: center;
        }

        .price-input {
            width: 150px;
            padding: 10px;
            border: 2px solid #dee2e6;
            border-radius: 8px;
            font-size: 14px;
        }

        /* Year Range */
        .year-range-container {
            display: flex;
            gap: 15px;
            align-items: center;
        }

        .year-input {
            width: 100px;
            padding: 10px;
            border: 2px solid #dee2e6;
            border-radius: 8px;
            font-size: 14px;
        }

        /* Quick Filters */
        .quick-filters {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }

        .quick-filter-btn {
            padding: 8px 16px;
            background: #e3f2fd;
            border: 1px solid #90caf9;
            border-radius: 20px;
            color: #1976d2;
            font-size: 13px;
            cursor: pointer;
            transition: all 0.2s;
        }

        .quick-filter-btn:hover {
            background: #bbdefb;
        }

        /* Results Section */
        .results-section {
            padding: 30px;
        }

        .results-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }

        .results-count {
            font-size: 18px;
            font-weight: 600;
            color: #495057;
        }

        .view-toggle {
            display: flex;
            gap: 10px;
        }

        .view-btn {
            padding: 8px 15px;
            border: 1px solid #dee2e6;
            background: white;
            cursor: pointer;
            transition: all 0.2s;
        }

        .view-btn.active {
            background: #667eea;
            color: white;
            border-color: #667eea;
        }

        /* Watch Grid */
        :root { --watch-card-min: 300px; }
        .watch-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(var(--watch-card-min), 1fr));
            gap: 20px;
        }

        .watch-card {
            background: white;
            border: 1px solid #e9ecef;
            border-radius: 15px;
            padding: 20px;
            transition: all 0.3s;
            cursor: pointer;
        }

        .watch-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 30px rgba(0,0,0,0.1);
        }

        .watch-name {
            font-size: 18px;
            font-weight: 600;
            color: #212529;
            margin-bottom: 10px;
        }

        .watch-collection {
            display: inline-block;
            padding: 4px 10px;
            background: #e3f2fd;
            border-radius: 15px;
            font-size: 12px;
            color: #1976d2;
            margin-bottom: 10px;
        }

        .watch-price {
            font-size: 24px;
            font-weight: 700;
            color: #667eea;
            margin-bottom: 5px;
        }

        .watch-appreciation {
            font-size: 14px;
            color: #28a745;
        }

        .watch-year {
            font-size: 13px;
            color: #6c757d;
        }

        /* Loading */
        .loading {
            text-align: center;
            padding: 50px;
            color: #6c757d;
        }

        .spinner {
            border: 3px solid #f3f3f3;
            border-top: 3px solid #667eea;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            animation: spin 1s linear infinite;
            margin: 0 auto 20px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        /* Facets Sidebar */
        .facets-sidebar {
            background: #f8f9fa;
            border-radius: 10px;
            padding: 20px;
            margin-bottom: 20px;
        }

        .facet-group {
            margin-bottom: 20px;
        }

        .facet-title {
            font-weight: 600;
            color: #495057;
            margin-bottom: 10px;
        }

        .facet-item {
            display: flex;
            justify-content: space-between;
            padding: 8px 0;
            cursor: pointer;
            transition: color 0.2s;
        }

        .facet-item:hover {
            color: #667eea;
        }

        .facet-count {
            background: #e9ecef;
            padding: 2px 8px;
            border-radius: 10px;
            font-size: 12px;
        }

        /* Responsive */
        @media (max-width: 768px) {
            .filter-buttons {
                flex-direction: column;
            }
            
            .search-container {
                flex-direction: column;
            }

            .watch-grid {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>🔍 Omega Watch Advanced Search</h1>
            <p class="subtitle">Deep Search with SQLite FTS5 - Find Any Watch Instantly</p>
        </header>

        <div class="search-section">
            <!-- Main Search Bar -->
            <div class="search-container">
                <div class="search-input-wrapper">
                    <span class="search-icon">🔍</span>
                    <input type="text" id="searchInput" class="search-input" 
                           placeholder="Search watches, collections, references..." 
                           autocomplete="off">
                </div>
                <button class="search-btn" onclick="performSearch()">Search</button>
                <button class="clear-btn" onclick="clearAll()">Clear All</button>
            </div>

            <!-- Sort + Density Controls -->
            <div class="sort-section" style="display:flex; gap:24px; align-items:center; flex-wrap:wrap;">
                <div style="display:flex; gap:8px; align-items:center;">
                    <label class="sort-label" for="sortSelect">Sort by:</label>
                    <select id="sortSelect" class="sort-select" onchange="onSortChange()">
                        <option value="year_desc">Newest (Year)</option>
                        <option value="year_asc">Oldest (Year)</option>
                        <option value="relevance">Relevance</option>
                        <option value="name_asc">Model A→Z</option>
                        <option value="name_desc">Model Z→A</option>
                        <option value="price_asc">Price: Low to High</option>
                        <option value="price_desc">Price: High to Low</option>
                        <option value="appreciation_desc">Highest Appreciation</option>
                    </select>
                </div>
                <div style="display:flex; gap:8px; align-items:center;">
                    <label class="sort-label" for="densityRange">Density:</label>
                    <input type="range" id="densityRange" min="200" max="480" step="20" value="300" oninput="onDensityChange(this.value)" style="width:160px;">
                    <span id="densityVal" style="font-size:12px; color:#6c757d; min-width:48px;">300px</span>
                </div>
            </div>

            <!-- Collection Filter Buttons -->
            <div class="filter-group">
                <label class="filter-label">Collections:</label>
                <div class="filter-buttons" id="collectionButtons">
                    <button class="filter-btn active" data-collection="all" onclick="filterCollection('all')">All Collections</button>
                    <button class="filter-btn" data-collection="Speedmaster" onclick="filterCollection('Speedmaster')">Speedmaster</button>
                    <button class="filter-btn" data-collection="Seamaster" onclick="filterCollection('Seamaster')">Seamaster</button>
                    <button class="filter-btn" data-collection="Constellation" onclick="filterCollection('Constellation')">Constellation</button>
                    <button class="filter-btn" data-collection="De Ville" onclick="filterCollection('De Ville')">De Ville</button>
                    <button class="filter-btn" data-collection="Railmaster" onclick="filterCollection('Railmaster')">Railmaster</button>
                    <button class="filter-btn" data-collection="Flightmaster" onclick="filterCollection('Flightmaster')">Flightmaster</button>
                </div>
            </div>

            <!-- Price Range Filter -->
            <div class="filter-group">
                <label class="filter-label">Price Range:</label>
                <div class="price-range-container">
                    <input type="number" id="minPrice" class="price-input" placeholder="Min $">
                    <span>to</span>
                    <input type="number" id="maxPrice" class="price-input" placeholder="Max $">
                    <button class="filter-btn" onclick="performSearch()">Apply</button>
                </div>
            </div>

            <!-- Year Range Filter -->
            <div class="filter-group">
                <label class="filter-label">Year Introduced:</label>
                <div class="year-range-container">
                    <input type="number" id="yearFrom" class="year-input" placeholder="From" min="1950" max="2024">
                    <span>to</span>
                    <input type="number" id="yearTo" class="year-input" placeholder="To" min="1950" max="2024">
                    <button class="filter-btn" onclick="performSearch()">Apply</button>
                </div>
            </div>

            <!-- Quick Filters -->
            <div class="filter-group">
                <label class="filter-label">Quick Filters:</label>
                <div class="quick-filters">
                    <button class="quick-filter-btn" onclick="quickFilter('under5k')">Under $5,000</button>
                    <button class="quick-filter-btn" onclick="quickFilter('5kto10k')">$5,000 - $10,000</button>
                    <button class="quick-filter-btn" onclick="quickFilter('over10k')">Over $10,000</button>
                    <button class="quick-filter-btn" onclick="quickFilter('vintage')">Vintage (Pre-1980)</button>
                    <button class="quick-filter-btn" onclick="quickFilter('modern')">Modern (2000+)</button>
                    <button class="quick-filter-btn" onclick="quickFilter('highAppreciation')">High Appreciation (50%+)</button>
                    <button class="quick-filter-btn" onclick="quickFilter('moonwatch')">Moonwatch</button>
                    <button class="quick-filter-btn" onclick="quickFilter('chronograph')">Chronograph</button>
                </div>
            </div>
        </div>

        <!-- Facets Sidebar (populated dynamically) -->
        <div id="facetsSidebar" class="facets-sidebar" style="display: none;">
            <!-- Facets will be populated here -->
        </div>

        <!-- Results Section -->
        <div class="results-section">
            <div class="results-header">
                <div class="results-count">
                    <span id="resultCount">0</span> watches found
                </div>
                <div class="view-toggle">
                    <button class="view-btn active" onclick="setView('grid')">Grid</button>
                    <button class="view-btn" onclick="setView('list')">List</button>
                    <button class="view-btn" onclick="setView('chart')">Chart</button>
                </div>
            </div>

            <!-- Loading Spinner -->
            <div id="loading" class="loading" style="display: none;">
                <div class="spinner"></div>
                <p>Searching watches...</p>
            </div>

            <!-- Results Grid -->
            <div id="watchGrid" class="watch-grid">
                <!-- Watch cards will be populated here -->
            </div>

            <!-- Chart Container -->
            <canvas id="priceChart" style="display: none; margin-top: 20px;"></canvas>
        </div>
    </div>

    <script>
        const API_BASE = 'http://45.61.58.125:7600';
        let currentCollection = 'all';
        let currentView = 'grid';
        let chart = null;

        // ---- Sort + density persistence (run BEFORE first performSearch) ----
        const LS_SORT = 'watches.search.sort';
        const LS_DENSITY = 'watches.search.density';
        function hydrateControls() {
            try {
                const savedSort = localStorage.getItem(LS_SORT);
                if (savedSort) {
                    const sel = document.getElementById('sortSelect');
                    if ([...sel.options].some(o => o.value === savedSort)) sel.value = savedSort;
                }
                const savedDensity = parseInt(localStorage.getItem(LS_DENSITY), 10);
                if (savedDensity >= 200 && savedDensity <= 480) {
                    document.getElementById('densityRange').value = String(savedDensity);
                    applyDensity(savedDensity);
                } else {
                    applyDensity(parseInt(document.getElementById('densityRange').value, 10));
                }
            } catch (e) { /* localStorage may be unavailable */ }
        }
        function applyDensity(px) {
            document.documentElement.style.setProperty('--watch-card-min', px + 'px');
            const lbl = document.getElementById('densityVal');
            if (lbl) lbl.textContent = px + 'px';
        }
        function onSortChange() {
            try { localStorage.setItem(LS_SORT, document.getElementById('sortSelect').value); } catch (e) {}
            performSearch();
        }
        function onDensityChange(v) {
            const px = parseInt(v, 10);
            applyDensity(px);
            try { localStorage.setItem(LS_DENSITY, String(px)); } catch (e) {}
        }

        // Initialize on page load
        document.addEventListener('DOMContentLoaded', () => {
            hydrateControls();
            performSearch();
            setupAutocomplete();
        });

        // Perform search with all filters
        async function performSearch() {
            const searchQuery = document.getElementById('searchInput').value;
            const sort = document.getElementById('sortSelect').value;
            const minPrice = document.getElementById('minPrice').value;
            const maxPrice = document.getElementById('maxPrice').value;
            const yearFrom = document.getElementById('yearFrom').value;
            const yearTo = document.getElementById('yearTo').value;

            // Build query parameters
            const params = new URLSearchParams();
            if (searchQuery) params.append('q', searchQuery);
            if (currentCollection !== 'all') params.append('collection', currentCollection);
            if (minPrice) params.append('minPrice', minPrice);
            if (maxPrice) params.append('maxPrice', maxPrice);
            if (yearFrom) params.append('yearFrom', yearFrom);
            if (yearTo) params.append('yearTo', yearTo);
            params.append('sort', sort);
            params.append('facets', 'true');
            params.append('limit', '50');

            // Show loading
            showLoading();

            try {
                const response = await fetch(`${API_BASE}/api/advanced-search?${params}`);
                const data = await response.json();
                
                displayResults(data.results);
                updateResultCount(data.total);
                
                if (data.facets) {
                    displayFacets(data.facets);
                }
            } catch (error) {
                console.error('Search error:', error);
                showError('Failed to search watches. Please try again.');
            } finally {
                hideLoading();
            }
        }

        // Filter by collection
        function filterCollection(collection) {
            currentCollection = collection;
            
            // Update button states
            document.querySelectorAll('#collectionButtons .filter-btn').forEach(btn => {
                btn.classList.remove('active');
                if (btn.dataset.collection === collection) {
                    btn.classList.add('active');
                }
            });
            
            performSearch();
        }

        // Quick filters
        function quickFilter(type) {
            const minPriceInput = document.getElementById('minPrice');
            const maxPriceInput = document.getElementById('maxPrice');
            const yearFromInput = document.getElementById('yearFrom');
            const yearToInput = document.getElementById('yearTo');
            const searchInput = document.getElementById('searchInput');

            // Reset inputs
            minPriceInput.value = '';
            maxPriceInput.value = '';
            yearFromInput.value = '';
            yearToInput.value = '';
            searchInput.value = '';

            switch(type) {
                case 'under5k':
                    maxPriceInput.value = 5000;
                    break;
                case '5kto10k':
                    minPriceInput.value = 5000;
                    maxPriceInput.value = 10000;
                    break;
                case 'over10k':
                    minPriceInput.value = 10000;
                    break;
                case 'vintage':
                    yearToInput.value = 1979;
                    break;
                case 'modern':
                    yearFromInput.value = 2000;
                    break;
                case 'highAppreciation':
                    // This would need a backend filter for appreciation rate
                    searchInput.value = 'high appreciation';
                    break;
                case 'moonwatch':
                    searchInput.value = 'moonwatch';
                    break;
                case 'chronograph':
                    searchInput.value = 'chronograph';
                    break;
            }

            performSearch();
        }

        // Display results
        function displayResults(watches) {
            const grid = document.getElementById('watchGrid');
            grid.innerHTML = '';

            if (watches.length === 0) {
                grid.innerHTML = '<p style="text-align: center; color: #6c757d; padding: 40px;">No watches found. Try adjusting your filters.</p>';
                return;
            }

            watches.forEach(watch => {
                const card = document.createElement('div');
                card.className = 'watch-card';
                card.innerHTML = `
                    <div class="watch-name">${watch.highlighted_name || watch.name}</div>
                    <div class="watch-collection">${watch.collection || 'N/A'}</div>
                    <div class="watch-price">$${watch.avg_price ? watch.avg_price.toLocaleString() : 'N/A'}</div>
                    ${watch.appreciation_rate ? `<div class="watch-appreciation">+${watch.appreciation_rate.toFixed(1)}% appreciation</div>` : ''}
                    <div class="watch-year">Introduced: ${watch.year_introduced || 'Unknown'}</div>
                    ${watch.text_snippet ? `<div style="margin-top: 10px; font-size: 13px; color: #6c757d;">${watch.text_snippet}</div>` : ''}
                `;
                grid.appendChild(card);
            });

            // Update chart if in chart view
            if (currentView === 'chart' && watches.length > 0) {
                updateChart(watches);
            }
        }

        // Display facets
        function displayFacets(facets) {
            const sidebar = document.getElementById('facetsSidebar');
            
            if (!facets || Object.keys(facets).length === 0) {
                sidebar.style.display = 'none';
                return;
            }

            let facetsHTML = '';

            // Collections facet
            if (facets.collections && facets.collections.length > 0) {
                facetsHTML += '<div class="facet-group">';
                facetsHTML += '<div class="facet-title">Collections</div>';
                facets.collections.forEach(item => {
                    facetsHTML += `
                        <div class="facet-item" onclick="filterCollection('${item.collection}')">
                            <span>${item.collection || 'Unknown'}</span>
                            <span class="facet-count">${item.count}</span>
                        </div>
                    `;
                });
                facetsHTML += '</div>';
            }

            // Price ranges facet
            if (facets.priceRanges && facets.priceRanges.length > 0) {
                facetsHTML += '<div class="facet-group">';
                facetsHTML += '<div class="facet-title">Price Ranges</div>';
                facets.priceRanges.forEach(range => {
                    if (range.count > 0) {
                        facetsHTML += `
                            <div class="facet-item" onclick="setPriceRange(${range.min}, ${range.max})">
                                <span>${range.label}</span>
                                <span class="facet-count">${range.count}</span>
                            </div>
                        `;
                    }
                });
                facetsHTML += '</div>';
            }

            // Decades facet
            if (facets.decades && facets.decades.length > 0) {
                facetsHTML += '<div class="facet-group">';
                facetsHTML += '<div class="facet-title">Decades</div>';
                facets.decades.forEach(item => {
                    facetsHTML += `
                        <div class="facet-item">
                            <span>${item.decade}</span>
                            <span class="facet-count">${item.count}</span>
                        </div>
                    `;
                });
                facetsHTML += '</div>';
            }

            sidebar.innerHTML = facetsHTML;
            sidebar.style.display = facetsHTML ? 'block' : 'none';
        }

        // Set price range from facet
        function setPriceRange(min, max) {
            document.getElementById('minPrice').value = min;
            document.getElementById('maxPrice').value = max < 999999999 ? max : '';
            performSearch();
        }

        // Update result count
        function updateResultCount(count) {
            document.getElementById('resultCount').textContent = count;
        }

        // Clear all filters
        function clearAll() {
            document.getElementById('searchInput').value = '';
            document.getElementById('minPrice').value = '';
            document.getElementById('maxPrice').value = '';
            document.getElementById('yearFrom').value = '';
            document.getElementById('yearTo').value = '';
            document.getElementById('sortSelect').value = 'relevance';
            filterCollection('all');
        }

        // Set view type
        function setView(view) {
            currentView = view;
            
            // Update button states
            document.querySelectorAll('.view-btn').forEach(btn => {
                btn.classList.remove('active');
            });
            event.target.classList.add('active');

            // Toggle displays
            const grid = document.getElementById('watchGrid');
            const chartCanvas = document.getElementById('priceChart');

            if (view === 'chart') {
                grid.style.display = 'none';
                chartCanvas.style.display = 'block';
                performSearch(); // Refresh to update chart
            } else {
                grid.style.display = 'grid';
                chartCanvas.style.display = 'none';
            }
        }

        // Update chart
        function updateChart(watches) {
            const ctx = document.getElementById('priceChart').getContext('2d');
            
            if (chart) {
                chart.destroy();
            }

            chart = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: watches.map(w => w.name),
                    datasets: [{
                        label: 'Average Price ($)',
                        data: watches.map(w => w.avg_price || 0),
                        backgroundColor: 'rgba(102, 126, 234, 0.6)',
                        borderColor: 'rgba(102, 126, 234, 1)',
                        borderWidth: 1
                    }]
                },
                options: {
                    responsive: true,
                    scales: {
                        y: {
                            beginAtZero: true,
                            ticks: {
                                callback: function(value) {
                                    return '$' + value.toLocaleString();
                                }
                            }
                        }
                    }
                }
            });
        }

        // Setup autocomplete
        async function setupAutocomplete() {
            const input = document.getElementById('searchInput');
            let timeout;

            input.addEventListener('input', async (e) => {
                clearTimeout(timeout);
                const query = e.target.value;

                if (query.length < 2) return;

                timeout = setTimeout(async () => {
                    try {
                        const response = await fetch(`${API_BASE}/api/search/suggestions?q=${query}`);
                        const data = await response.json();
                        // You could display suggestions in a dropdown here
                        console.log('Suggestions:', data.suggestions);
                    } catch (error) {
                        console.error('Autocomplete error:', error);
                    }
                }, 300);
            });

            // Search on Enter key
            input.addEventListener('keypress', (e) => {
                if (e.key === 'Enter') {
                    performSearch();
                }
            });
        }

        // Loading states
        function showLoading() {
            document.getElementById('loading').style.display = 'block';
            document.getElementById('watchGrid').style.display = 'none';
        }

        function hideLoading() {
            document.getElementById('loading').style.display = 'none';
            document.getElementById('watchGrid').style.display = 'grid';
        }

        function showError(message) {
            const grid = document.getElementById('watchGrid');
            grid.innerHTML = `<p style="text-align: center; color: #dc3545; padding: 40px;">${message}</p>`;
        }
    </script>
</body>
</html>