← Mike Higgins Home
Custom Code Experiment •

SCP API - Demo Mode

Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SCP FOUNDATION - DATA TERMINAL</title>
    <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@300;400;500;600&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@300;400;500&display=swap" rel="stylesheet">
    <style>
        :root {
            --primary-bg: #0a0f12;
            --secondary-bg: #141a1f;
            --terminal-cyan: #2ee7d6;
            --accent-green: #00ff88;
            --warn-red: #ff4757;
            --info-blue: #3b9eff;
            --border-color: #25313a;
            --text-primary: #e8f1f8;
            --text-muted: #8fa3b3;
            --card-bg: rgba(20, 26, 31, 0.8);
            --glow: 0 0 20px rgba(46, 231, 214, 0.15);
            --border-glow: 0 0 15px rgba(46, 231, 214, 0.1);
        }

        /* Styling for the main page scrollbar (when viewed in iframe) */
        html {
            scrollbar-width: thin;
            scrollbar-color: var(--terminal-cyan) var(--primary-bg);
        }

        /* For WebKit browsers (Chrome, Safari, Edge) */
        ::-webkit-scrollbar {
            width: 12px;
        }

        ::-webkit-scrollbar-track {
            background: var(--primary-bg);
            border-left: 1px solid var(--border-color);
        }

        ::-webkit-scrollbar-thumb {
            background: linear-gradient(to bottom, var(--terminal-cyan), var(--accent-green));
            border-radius: 6px;
            border: 2px solid var(--primary-bg);
        }

        ::-webkit-scrollbar-thumb:hover {
            background: linear-gradient(to bottom, var(--accent-green), var(--terminal-cyan));
            box-shadow: 0 0 10px rgba(46, 231, 214, 0.5);
        }

        ::-webkit-scrollbar-corner {
            background: var(--primary-bg);
        }

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

        body {
            font-family: 'IBM Plex Mono', monospace;
            background: var(--primary-bg);
            color: var(--text-primary);
            overflow-x: hidden;
            line-height: 1.6;
            font-weight: 300;
            /* Add padding to account for scrollbar on some systems */
            padding-right: 0;
        }

        /* Subtle grid background */
        body::before {
            content: '';
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-image:
                linear-gradient(rgba(46, 231, 214, 0.03) 1px, transparent 1px),
                linear-gradient(90deg, rgba(46, 231, 214, 0.03) 1px, transparent 1px);
            background-size: 40px 40px;
            pointer-events: none;
            z-index: -1;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 2rem;
            position: relative;
        }

        /* Add a subtle scroll indicator in the corner */
        .scroll-indicator {
            position: fixed;
            bottom: 20px;
            right: 20px;
            width: 40px;
            height: 40px;
            background: var(--card-bg);
            border: 1px solid var(--border-color);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--terminal-cyan);
            font-size: 1.2rem;
            opacity: 0.7;
            z-index: 100;
            transition: all 0.3s ease;
            cursor: pointer;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
        }

        .scroll-indicator:hover {
            opacity: 1;
            transform: scale(1.1);
            box-shadow: 0 0 15px rgba(46, 231, 214, 0.3);
        }

        .header {
            background: linear-gradient(135deg, rgba(20, 26, 31, 0.9), rgba(20, 26, 31, 0.7));
            backdrop-filter: blur(10px);
            border: 1px solid var(--border-color);
            border-radius: 12px;
            padding: 2rem 3rem;
            margin-bottom: 2rem;
            position: relative;
            overflow: hidden;
            box-shadow: var(--glow);
        }

        .header::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 1px;
            background: linear-gradient(90deg, transparent, var(--terminal-cyan), transparent);
        }

        .classified-badge {
            display: inline-block;
            background: linear-gradient(135deg, var(--warn-red), #ff2e5e);
            color: white;
            padding: 0.4rem 1.2rem;
            font-weight: 600;
            font-family: 'Inter', sans-serif;
            font-size: 0.8rem;
            letter-spacing: 2px;
            margin-bottom: 1rem;
            border-radius: 20px;
            box-shadow: 0 4px 15px rgba(255, 71, 87, 0.2);
        }

        h1 {
            font-family: 'Inter', sans-serif;
            font-size: 2.8rem;
            font-weight: 700;
            background: linear-gradient(135deg, var(--terminal-cyan), var(--accent-green));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            margin-bottom: 0.5rem;
            letter-spacing: -0.5px;
        }

        .subtitle {
            color: var(--text-muted);
            font-size: 1rem;
            letter-spacing: 1px;
            font-weight: 300;
        }

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

        .stat-box {
            background: var(--card-bg);
            border: 1px solid var(--border-color);
            border-radius: 10px;
            padding: 1.8rem;
            text-align: center;
            position: relative;
            overflow: hidden;
            transition: all 0.3s ease;
        }

        .stat-box:hover {
            border-color: var(--terminal-cyan);
            transform: translateY(-5px);
            box-shadow: var(--glow);
        }

        .stat-box::after {
            content: '';
            position: absolute;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 2px;
            background: linear-gradient(90deg, var(--terminal-cyan), transparent);
            opacity: 0;
            transition: opacity 0.3s ease;
        }

        .stat-box:hover::after {
            opacity: 1;
        }

        .stat-value {
            font-size: 2.2rem;
            font-weight: 600;
            color: var(--terminal-cyan);
            font-family: 'Inter', sans-serif;
            margin-bottom: 0.5rem;
        }

        .stat-label {
            color: var(--text-muted);
            font-size: 0.85rem;
            text-transform: uppercase;
            letter-spacing: 2px;
            font-weight: 400;
        }

        .terminal-window {
            background: var(--card-bg);
            border: 1px solid var(--border-color);
            border-radius: 12px;
            margin-bottom: 2rem;
            overflow: hidden;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
            transition: all 0.3s ease;
        }

        .terminal-window:hover {
            border-color: var(--terminal-cyan);
            box-shadow: var(--glow);
        }

        .terminal-header {
            background: linear-gradient(90deg, rgba(20, 26, 31, 0.9), rgba(30, 40, 48, 0.9));
            padding: 1.2rem 2rem;
            border-bottom: 1px solid var(--border-color);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .terminal-title {
            color: var(--terminal-cyan);
            font-weight: 500;
            letter-spacing: 1px;
            text-transform: uppercase;
            font-size: 0.9rem;
            font-family: 'Inter', sans-serif;
        }

        .terminal-indicators {
            display: flex;
            gap: 0.6rem;
        }

        .indicator {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: var(--terminal-cyan);
            opacity: 0.7;
            transition: all 0.3s ease;
        }

        .terminal-content {
            padding: 2.5rem;
        }

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

        .control-group {
            position: relative;
        }

        label {
            display: block;
            color: var(--text-muted);
            font-size: 0.85rem;
            text-transform: uppercase;
            letter-spacing: 1.5px;
            margin-bottom: 0.8rem;
            font-weight: 500;
            font-family: 'Inter', sans-serif;
        }

        select, input[type="text"], input[type="number"] {
            width: 100%;
            background: rgba(10, 15, 18, 0.6);
            border: 1px solid var(--border-color);
            border-radius: 6px;
            color: var(--text-primary);
            padding: 1rem 1.2rem;
            font-family: 'JetBrains Mono', monospace;
            font-size: 0.95rem;
            transition: all 0.3s ease;
            font-weight: 300;
        }

        select:focus, input:focus {
            outline: none;
            border-color: var(--terminal-cyan);
            box-shadow: 0 0 0 3px rgba(46, 231, 214, 0.1);
        }

        select:hover, input:hover {
            border-color: rgba(46, 231, 214, 0.5);
        }

        button {
            background: linear-gradient(135deg, var(--terminal-cyan), var(--accent-green));
            border: none;
            border-radius: 8px;
            color: #0a0f12;
            padding: 1.2rem 2.5rem;
            font-family: 'Inter', sans-serif;
            font-weight: 600;
            font-size: 1rem;
            text-transform: uppercase;
            letter-spacing: 2px;
            cursor: pointer;
            transition: all 0.3s ease;
            position: relative;
            overflow: hidden;
            box-shadow: 0 4px 20px rgba(46, 231, 214, 0.3);
            margin-top: 1rem;
        }

        button:hover {
            transform: translateY(-3px);
            box-shadow: 0 8px 30px rgba(46, 231, 214, 0.4);
        }

        button:active {
            transform: translateY(-1px);
        }

        button:disabled {
            background: #2a2a2a;
            color: #666;
            cursor: not-allowed;
            box-shadow: none;
            transform: none;
        }

        .results {
            background: rgba(10, 15, 18, 0.6);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 2rem;
            min-height: 400px;
            max-height: 600px;
            overflow-y: auto;
            font-size: 0.9rem;
            position: relative;
            font-weight: 300;
        }

        /* Style for the results container scrollbar */
        .results::-webkit-scrollbar {
            width: 8px;
        }

        .results::-webkit-scrollbar-track {
            background: rgba(20, 26, 31, 0.5);
            border-radius: 4px;
        }

        .results::-webkit-scrollbar-thumb {
            background: var(--terminal-cyan);
            border-radius: 4px;
            opacity: 0.5;
        }

        .loading {
            display: none;
            text-align: center;
            color: var(--terminal-cyan);
            font-size: 1.2rem;
            padding: 3rem;
            font-weight: 300;
        }

        .loading.active {
            display: block;
        }

        .error {
            color: var(--warn-red);
            padding: 1.5rem;
            border: 1px solid var(--warn-red);
            background: rgba(255, 71, 87, 0.05);
            border-radius: 8px;
            margin: 1rem 0;
            display: none;
            font-weight: 300;
        }

        .error.active {
            display: block;
        }

        .item-card {
            background: var(--card-bg);
            border: 1px solid var(--border-color);
            border-radius: 10px;
            padding: 1.8rem;
            margin-bottom: 1.2rem;
            transition: all 0.3s ease;
            position: relative;
        }

        .item-card:hover {
            border-color: var(--terminal-cyan);
            transform: translateX(5px);
            box-shadow: var(--glow);
        }

        .item-title {
            color: var(--terminal-cyan);
            font-size: 1.2rem;
            font-weight: 500;
            margin-bottom: 0.8rem;
            font-family: 'Inter', sans-serif;
        }

        .item-meta {
            color: var(--text-muted);
            font-size: 0.85rem;
            margin-bottom: 1rem;
            display: flex;
            align-items: center;
            gap: 1rem;
            flex-wrap: wrap;
        }

        .item-rating {
            background: linear-gradient(135deg, var(--info-blue), #6d8bff);
            color: white;
            padding: 0.3rem 0.8rem;
            font-size: 0.8rem;
            font-weight: 600;
            border-radius: 4px;
        }

        .item-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 0.5rem;
            margin-top: 1rem;
        }

        .tag {
            background: rgba(46, 231, 214, 0.1);
            border: 1px solid rgba(46, 231, 214, 0.3);
            color: var(--terminal-cyan);
            padding: 0.3rem 0.8rem;
            font-size: 0.75rem;
            text-transform: uppercase;
            letter-spacing: 1px;
            border-radius: 4px;
            font-weight: 400;
        }

        .footer {
            text-align: center;
            padding: 3rem 2rem 2rem;
            color: var(--text-muted);
            font-size: 0.85rem;
            border-top: 1px solid var(--border-color);
            margin-top: 3rem;
            font-weight: 300;
        }

        .type-indicator {
            position: absolute;
            top: 1.8rem;
            right: 1.8rem;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: var(--terminal-cyan);
            opacity: 0.7;
        }

        @media (max-width: 768px) {
            .container {
                padding: 1.5rem;
            }

            h1 {
                font-size: 2rem;
            }

            .header {
                padding: 1.5rem;
            }

            .terminal-content {
                padding: 1.5rem;
            }

            .controls {
                grid-template-columns: 1fr;
                gap: 1.5rem;
            }

            .stats {
                grid-template-columns: repeat(2, 1fr);
            }

            /* Adjust scrollbar width on mobile */
            ::-webkit-scrollbar {
                width: 8px;
            }
        }

        @media (max-width: 480px) {
            .stats {
                grid-template-columns: 1fr;
            }

            h1 {
                font-size: 1.8rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <div class="classified-badge">CLASSIFIED • LEVEL 4</div>
            <h1>SCP FOUNDATION</h1>
            <div class="subtitle">SECURE • CONTAIN • PROTECT | DATA TERMINAL v3.0</div>
        </div>

        <div class="stats" id="stats">
            <div class="stat-box">
                <div class="stat-value" id="totalItems">---</div>
                <div class="stat-label">Total Items</div>
            </div>
            <div class="stat-box">
                <div class="stat-value" id="totalTales">---</div>
                <div class="stat-label">Tales</div>
            </div>
            <div class="stat-box">
                <div class="stat-value" id="totalGOI">---</div>
                <div class="stat-label">GOI Entries</div>
            </div>
            <div class="stat-box">
                <div class="stat-value" id="totalHubs">---</div>
                <div class="stat-label">Hub Collections</div>
            </div>
        </div>

        <div class="terminal-window">
            <div class="terminal-header">
                <span class="terminal-title">Query Interface</span>
                <div class="terminal-indicators">
                    <div class="indicator"></div>
                    <div class="indicator" style="animation-delay: 0.2s;"></div>
                    <div class="indicator" style="animation-delay: 0.4s;"></div>
                </div>
            </div>
            <div class="terminal-content">
                <div class="controls">
                    <div class="control-group">
                        <label for="dataType">Data Type</label>
                        <select id="dataType">
                            <option value="items">SCP Items</option>
                            <option value="tales">Tales</option>
                            <option value="goi">Groups of Interest</option>
                            <option value="hubs">Hubs</option>
                        </select>
                    </div>
                    <div class="control-group">
                        <label for="series">Series Filter</label>
                        <select id="series">
                            <option value="">All Series</option>
                            <option value="series-1">Series I (SCP-001 to SCP-999)</option>
                            <option value="series-2">Series II (SCP-1000 to SCP-1999)</option>
                            <option value="series-3">Series III (SCP-2000 to SCP-2999)</option>
                            <option value="series-4">Series IV (SCP-3000 to SCP-3999)</option>
                            <option value="series-5">Series V (SCP-4000 to SCP-4999)</option>
                            <option value="series-6">Series VI (SCP-5000 to SCP-5999)</option>
                            <option value="series-7">Series VII (SCP-6000 to SCP-6999)</option>
                            <option value="series-8">Series VIII (SCP-7000 to SCP-7999)</option>
                            <option value="joke">Joke SCPs</option>
                        </select>
                    </div>
                    <div class="control-group">
                        <label for="scpNumber">SCP Number (Optional)</label>
                        <input type="number" id="scpNumber" placeholder="e.g., 173">
                    </div>
                    <div class="control-group">
                        <label for="searchTerm">Search Term</label>
                        <input type="text" id="searchTerm" placeholder="Search titles, tags...">
                    </div>
                </div>
                <button id="fetchBtn" onclick="fetchData()">INITIATE QUERY</button>
            </div>
        </div>

        <div class="terminal-window">
            <div class="terminal-header">
                <span class="terminal-title">Query Results</span>
                <div class="terminal-indicators">
                    <div class="indicator"></div>
                </div>
            </div>
            <div class="terminal-content">
                <div class="loading" id="loading">
                    ⚡ ACCESSING DATABASE ⚡<br>
                    <small style="font-size: 0.9rem; margin-top: 0.5rem; display: block;">Decrypting classified records...</small>
                </div>
                <div class="error" id="error"></div>
                <div class="results" id="results">
                    <div style="text-align: center; color: var(--text-muted); padding: 3rem;">
                        SELECT QUERY PARAMETERS AND INITIATE TO ACCESS DATABASE
                    </div>
                </div>
            </div>
        </div>

        <div class="footer">
            ⚠ UNAUTHORIZED ACCESS PROHIBITED ⚠<br>
            SCP Foundation Database Terminal • All data subject to Creative Commons licensing<br>
            Data sourced from SCP Data API • Not affiliated with SCP Wiki
        </div>
    </div>

    <!-- Scroll indicator (only shows on larger screens) -->
    <div class="scroll-indicator" id="scrollIndicator" onclick="scrollToTop()">
        ↑
    </div>

    <script>
        // DEMO MODE: Using sample data for demonstration
        const DEMO_MODE = true;
        const BASE_URL = 'https://cdn.jsdelivr.net/gh/rossjrw/scp@master/data/scp';

        let cachedData = {
            items: null,
            tales: null,
            goi: null,
            hubs: null
        };

        // Scroll indicator functionality
        const scrollIndicator = document.getElementById('scrollIndicator');

        window.addEventListener('scroll', () => {
            if (window.scrollY > 300) {
                scrollIndicator.style.display = 'flex';
            } else {
                scrollIndicator.style.display = 'none';
            }
        });

        function scrollToTop() {
            window.scrollTo({
                top: 0,
                behavior: 'smooth'
            });
        }

        // Demo data generator
        function generateDemoData(type, count) {
            const data = {};
            const series = ['series-1', 'series-2', 'series-3', 'series-4', 'series-5', 'joke'];
            const tags = ['euclid', 'keter', 'safe', 'neutralized', 'humanoid', 'sentient', 'artifact', 'location', 'online', 'autonomous'];

            for (let i = 0; i < count; i++) {
                const num = Math.floor(Math.random() * 7000) + 1;
                const key = type === 'items' ? `scp-${num.toString().padStart(3, '0')}` : `tale-${i}`;

                data[key] = {
                    title: type === 'items'
                        ? `SCP-${num} - ${generateTitle()}`
                        : generateTaleTitle(),
                    link: key,
                    url: `https://scp-wiki.wikidot.com/${key}`,
                    rating: Math.floor(Math.random() * 500) - 50,
                    created_at: `20${10 + Math.floor(Math.random() * 15)}-${String(Math.floor(Math.random() * 12) + 1).padStart(2, '0')}-${String(Math.floor(Math.random() * 28) + 1).padStart(2, '0')}T12:00:00`,
                    tags: Array.from({length: Math.floor(Math.random() * 6) + 2}, () =>
                        tags[Math.floor(Math.random() * tags.length)]
                    ),
                    ...(type === 'items' && {
                        scp: `SCP-${num}`,
                        scp_number: num,
                        series: series[Math.floor(Math.random() * series.length)]
                    })
                };
            }
            return data;
        }

        function generateTitle() {
            const titles = [
                'The Sculpture', 'Hard-to-Destroy Reptile', 'Plague Doctor', 'Old AI',
                'Door to Another World', 'Coffee Machine', 'Endless Staircase', 'Living Shadow',
                'Anomalous Duck', 'Reality Bender', 'Temporal Anomaly', 'Sentient Gas',
                'Living Mannequin', 'Impossible Geometry', 'Cognitohazard', 'Memory Parasite',
                'Antimemetic Entity', 'Recursive Phenomenon', 'Spatial Distortion', 'Time Loop'
            ];
            return titles[Math.floor(Math.random() * titles.length)];
        }

        function generateTaleTitle() {
            const titles = [
                'Day at the Foundation', 'Containment Breach', 'Interview Log',
                'The Last Mission', 'Researcher Notes', 'Incident Report',
                'Field Report: Gone Wrong', 'Ethics Committee Meeting', 'O5 Council Decision',
                'Mobile Task Force Delta', 'Site-19 Chronicles', 'D-Class Diary'
            ];
            return titles[Math.floor(Math.random() * titles.length)];
        }

        // Initialize and load stats
        async function loadStats() {
            if (DEMO_MODE) {
                // Generate demo data
                cachedData.items = generateDemoData('items', 100);
                cachedData.tales = generateDemoData('tales', 80);
                cachedData.goi = generateDemoData('goi', 40);
                cachedData.hubs = generateDemoData('hubs', 25);

                document.getElementById('totalItems').textContent = '7000+';
                document.getElementById('totalTales').textContent = '5000+';
                document.getElementById('totalGOI').textContent = '800+';
                document.getElementById('totalHubs').textContent = '150+';

                // Show demo notice
                setTimeout(() => {
                    const notice = document.createElement('div');
                    notice.style.cssText = 'position: fixed; top: 20px; right: 20px; background: rgba(255, 71, 87, 0.9); color: white; padding: 1rem 1.5rem; border-radius: 8px; z-index: 10000; font-size: 0.85rem; max-width: 300px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); font-family: Inter, sans-serif;';
                    notice.innerHTML = '<strong>⚠ DEMO MODE</strong><br>Showing sample data. See console for API connection instructions.';
                    document.body.appendChild(notice);
                    setTimeout(() => notice.remove(), 5000);
                }, 1000);

                console.log('%c🔒 SCP API DEMO MODE 🔒', 'font-size: 16px; font-weight: bold; color: #2ee7d6;');
                console.log('%cTo connect to the real SCP API:', 'font-size: 14px; color: #3b9eff;');
                console.log('1. Set DEMO_MODE = false in the code');
                console.log('2. Options for avoiding CORS:');
                console.log('   - Download JSON files from https://github.com/rossjrw/scp and host locally');
                console.log('   - Create a backend proxy server');
                console.log('   - Use a browser extension to disable CORS (development only)');
                return;
            }

            try {
                // Real API calls (will fail in browser due to CORS)
                const [items, tales, goi, hubs] = await Promise.all([
                    fetch(`${BASE_URL}/items/index.json`).then(r => r.json()),
                    fetch(`${BASE_URL}/tales/index.json`).then(r => r.json()),
                    fetch(`${BASE_URL}/goi/index.json`).then(r => r.json()),
                    fetch(`${BASE_URL}/hubs/index.json`).then(r => r.json())
                ]);

                document.getElementById('totalItems').textContent = Object.keys(items).length;
                document.getElementById('totalTales').textContent = Object.keys(tales).length;
                document.getElementById('totalGOI').textContent = Object.keys(goi).length;
                document.getElementById('totalHubs').textContent = Object.keys(hubs).length;

                cachedData.items = items;
                cachedData.tales = tales;
                cachedData.goi = goi;
                cachedData.hubs = hubs;
            } catch (error) {
                console.error('Error loading stats:', error);
                document.getElementById('totalItems').textContent = 'ERR';
                document.getElementById('totalTales').textContent = 'ERR';
                document.getElementById('totalGOI').textContent = 'ERR';
                document.getElementById('totalHubs').textContent = 'ERR';
            }
        }

        async function fetchData() {
            const dataType = document.getElementById('dataType').value;
            const series = document.getElementById('series').value;
            const scpNumber = document.getElementById('scpNumber').value;
            const searchTerm = document.getElementById('searchTerm').value.toLowerCase();

            const loading = document.getElementById('loading');
            const error = document.getElementById('error');
            const results = document.getElementById('results');
            const fetchBtn = document.getElementById('fetchBtn');

            // Show loading
            loading.classList.add('active');
            error.classList.remove('active');
            results.innerHTML = '';
            fetchBtn.disabled = true;

            try {
                let data;

                if (DEMO_MODE) {
                    // Use demo data
                    data = cachedData[dataType];
                    if (!data) {
                        throw new Error('Demo data not available for this type');
                    }
                } else {
                    // Use cached data if available, otherwise fetch
                    if (cachedData[dataType]) {
                        data = cachedData[dataType];
                    } else {
                        const response = await fetch(`${BASE_URL}/${dataType}/index.json`);
                        if (!response.ok) {
                            throw new Error(`Failed to fetch data (HTTP ${response.status}). The API may be unavailable or there may be CORS restrictions.`);
                        }
                        data = await response.json();
                        cachedData[dataType] = data;
                    }
                }

                // Convert object to array for easier filtering
                let entries = Object.entries(data).map(([key, value]) => ({
                    ...value,
                    link: key
                }));

                // Apply filters
                if (series && dataType === 'items') {
                    entries = entries.filter(item => item.series === series);
                }

                if (scpNumber && dataType === 'items') {
                    entries = entries.filter(item => item.scp_number === parseInt(scpNumber));
                }

                if (searchTerm) {
                    entries = entries.filter(item => {
                        const titleMatch = item.title?.toLowerCase().includes(searchTerm);
                        const tagsMatch = item.tags?.some(tag => tag.toLowerCase().includes(searchTerm));
                        const scpMatch = item.scp?.toLowerCase().includes(searchTerm);
                        return titleMatch || tagsMatch || scpMatch;
                    });
                }

                // Sort by rating (highest first)
                entries.sort((a, b) => (b.rating || 0) - (a.rating || 0));

                // Limit to first 50 results
                entries = entries.slice(0, 50);

                // Display results
                if (entries.length === 0) {
                    results.innerHTML = '<div style="text-align: center; color: var(--text-muted); padding: 2rem;">NO RECORDS FOUND</div>';
                } else {
                    results.innerHTML = `
                        <div style="text-align: center; color: var(--terminal-cyan); margin-bottom: 1.5rem; font-size: 0.85rem; font-weight: 500;">
                            ▶ DISPLAYING ${entries.length} RECORD${entries.length !== 1 ? 'S' : ''}
                        </div>
                        ${entries.map(item => createItemCard(item, dataType)).join('')}
                    `;
                }

            } catch (err) {
                error.textContent = `⚠ ERROR: ${err.message}`;
                error.classList.add('active');
                results.innerHTML = '<div style="text-align: center; color: var(--text-muted); padding: 2rem;">QUERY FAILED - CHECK CONSOLE FOR DETAILS</div>';
            } finally {
                loading.classList.remove('active');
                fetchBtn.disabled = false;
            }
        }

        function createItemCard(item, dataType) {
            const title = item.title || item.link;
            const rating = item.rating !== undefined ? item.rating : 'N/A';
            const tags = item.tags || [];
            const url = item.url || '#';

            let meta = '';
            if (dataType === 'items' && item.scp) {
                meta = `<span class="item-rating">★ ${rating}</span> ${item.scp}`;
            } else if (item.created_at) {
                meta = `<span class="item-rating">★ ${rating}</span> Created: ${item.created_at.split('T')[0]}`;
            } else {
                meta = `<span class="item-rating">★ ${rating}</span>`;
            }

            const tagHtml = tags.slice(0, 8).map(tag =>
                `<span class="tag">${tag}</span>`
            ).join('');

            return `
                <div class="item-card">
                    <div class="type-indicator"></div>
                    <div class="item-title">${escapeHtml(title)}</div>
                    <div class="item-meta">${meta}</div>
                    ${tagHtml ? `<div class="item-tags">${tagHtml}</div>` : ''}
                    <div style="margin-top: 1.2rem;">
                        <a href="${url}" target="_blank" style="color: var(--terminal-cyan); text-decoration: none; font-size: 0.85rem; font-weight: 500; display: inline-flex; align-items: center; gap: 0.5rem;">
                            <span style="font-size: 1.2rem;">→</span> VIEW DOCUMENT
                        </a>
                    </div>
                </div>
            `;
        }

        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        // Load stats on page load
        loadStats();
    </script>
</body>
</html>