Nitro Type - Leaderboards

This script adds a custom Startrack Leaderboards tab to Nitro Type, providing advanced leaderboard functionality with multiple timeframes, intelligent caching, and a polished UI that closely matches the original Nitro Type leaderboard design.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Nitro Type - Leaderboards
// @namespace    https://nitrotype.info
// @version      11.0.2
// @description  This script adds a custom Startrack Leaderboards tab to Nitro Type, providing advanced leaderboard functionality with multiple timeframes, intelligent caching, and a polished UI that closely matches the original Nitro Type leaderboard design.
// @author       Combined Logic (SuperJoelzy + Captain.Loveridge)
// @license      MIT
// @match        https://www.nitrotype.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        unsafeWindow
// @connect      ntstartrack.org
// @run-at       document-start
// ==/UserScript==
(function() {
    'use strict';
    const pageWindow = (typeof unsafeWindow !== 'undefined' && unsafeWindow) ? unsafeWindow : window;
    const SCRIPT_SINGLETON_KEY = '__ntLeaderboardsSingleton';
    if (pageWindow[SCRIPT_SINGLETON_KEY]) {
        try { console.info('[LDB] Duplicate instance detected; skipping.'); } catch (e) { }
        return;
    }
    pageWindow[SCRIPT_SINGLETON_KEY] = true;
    // ─── Mod Menu Manifest Bridge ───────────────────────────────────────────────
    const NTCFG_LEADERBOARDS_MANIFEST_ID = "leaderboards";
    const NTCFG_LEADERBOARDS_MANIFEST_KEY = `ntcfg:manifest:${NTCFG_LEADERBOARDS_MANIFEST_ID}`;
    const NTCFG_LEADERBOARDS_VALUE_PREFIX = `ntcfg:${NTCFG_LEADERBOARDS_MANIFEST_ID}:`;
    const NTCFG_LEADERBOARDS_BRIDGE_VERSION = "1.0.0-bridge.1";
    const SETTINGS_STORAGE_VERSION = 1;
    const STORAGE_VERSION_KEY = `${NTCFG_LEADERBOARDS_VALUE_PREFIX}__storage_version`;
    const STORAGE_MIGRATED_AT_KEY = `${NTCFG_LEADERBOARDS_VALUE_PREFIX}__migrated_at`;
    const STORAGE_CLEANUP_AFTER_KEY = `${NTCFG_LEADERBOARDS_VALUE_PREFIX}__cleanup_after`;
    const LEGACY_CLEANUP_GRACE_MS = 30 * 24 * 60 * 60 * 1000;
    const LEADERBOARDS_SHARED_SETTINGS = {
        DEFAULT_VIEW: {
            type: 'select',
            label: 'Default View',
            default: 'individual',
            group: 'Views',
            description: 'Which leaderboard view to show by default.',
            options: [
                { label: 'Individual', value: 'individual' },
                { label: 'Team', value: 'team' }
            ]
        },
        DEFAULT_TIMEFRAME: {
            type: 'select',
            label: 'Default Timeframe',
            default: 'season',
            group: 'Views',
            description: 'Which timeframe to show by default.',
            options: [
                { label: 'Season', value: 'season' },
                { label: 'Last 24 Hours', value: '24hr' },
                { label: 'Last 7 Days', value: '7day' }
            ]
        },
        HIGHLIGHT_POSITION_CHANGE: {
            type: 'boolean',
            label: 'Highlight Position Changes',
            default: true,
            group: 'Views',
            description: 'Show position change arrows next to leaderboard entries.'
        },
        SHOW_MANUAL_REFRESH: {
            type: 'boolean',
            label: 'Show Manual Refresh Button',
            default: true,
            group: 'Views',
            description: 'Display the manual refresh button on the leaderboard.'
        },
        CACHE_DURATION_MINUTES: {
            type: 'number',
            label: 'Cache Duration (minutes)',
            default: 60,
            group: 'Sync',
            description: 'How long to keep cached leaderboard data before re-fetching.',
            min: 1,
            max: 120,
            step: 1
        },
        ANTI_FLICKER_MODE: {
            type: 'boolean',
            label: 'Anti-Flicker Mode',
            default: true,
            group: 'Presentation',
            description: 'Hide page content briefly while the leaderboard loads to prevent flickering.'
        },
        SHOW_ROUTE_TAB: {
            type: 'boolean',
            label: 'Show Leaderboards Nav Tab',
            default: true,
            group: 'Presentation',
            description: 'Display the Leaderboards tab in the top navigation bar.'
        },
        SHOW_DROPDOWN_LINK: {
            type: 'boolean',
            label: 'Show Leaderboards in Dropdown',
            default: false,
            group: 'Presentation',
            description: 'Add a Leaderboards link to the account dropdown menu.'
        },
        HIDE_CLASS_TAB: {
            type: 'boolean',
            label: 'Hide Class Tab',
            default: false,
            group: 'Presentation',
            description: 'Hide the Class/Classes tab from the navigation bar.'
        },
        DEBUG_LOGGING: {
            type: 'boolean',
            label: 'Debug Logging',
            default: false,
            group: 'Advanced',
            description: 'Enable verbose console logging for troubleshooting.'
        }
    };
    const getNtcfgLeaderboardsStorageKey = (settingKey) => `${NTCFG_LEADERBOARDS_VALUE_PREFIX}${settingKey}`;
    const canUseGMStorage = () => typeof GM_getValue === 'function' && typeof GM_setValue === 'function';
    const readCanonicalValue = (storageKey) => {
        if (!canUseGMStorage()) return undefined;
        try {
            return GM_getValue(storageKey);
        } catch {
            return undefined;
        }
    };
    const writeCanonicalValue = (storageKey, value) => {
        if (!canUseGMStorage()) return;
        try {
            GM_setValue(storageKey, value);
        } catch {
            // ignore storage sync failures
        }
    };
    const readStorageMetaNumber = (storageKey, fallback = 0) => {
        const raw = readCanonicalValue(storageKey);
        const parsed = Number(raw);
        return Number.isFinite(parsed) ? parsed : fallback;
    };
    const dispatchLeaderboardsActionResult = (requestId, status, error = '') => {
        if (!requestId) return;
        try {
            document.dispatchEvent(new CustomEvent('ntcfg:action-result', {
                detail: {
                    requestId,
                    script: NTCFG_LEADERBOARDS_MANIFEST_ID,
                    status,
                    error
                }
            }));
        } catch {
            // ignore dispatch failures
        }
    };
    const coerceNtcfgLeaderboardsValue = (settingKey, value) => {
        const meta = LEADERBOARDS_SHARED_SETTINGS[settingKey];
        if (!meta) return value;
        if (meta.type === 'boolean') {
            if (typeof value === 'string') {
                const raw = value.trim().toLowerCase();
                if (raw === 'false' || raw === '0' || raw === 'off') return false;
                if (raw === 'true' || raw === '1' || raw === 'on') return true;
            }
            return !!value;
        }
        if (meta.type === 'number') {
            const fallback = Number(meta.default);
            const parsed = Number(value);
            let normalized = Number.isFinite(parsed) ? parsed : fallback;
            const min = Number(meta.min);
            const max = Number(meta.max);
            const step = Number(meta.step);
            if (Number.isFinite(step) && step >= 1) {
                normalized = Math.round(normalized);
            }
            if (Number.isFinite(min)) {
                normalized = Math.max(min, normalized);
            }
            if (Number.isFinite(max)) {
                normalized = Math.min(max, normalized);
            }
            return normalized;
        }
        if (meta.type === 'select') {
            const raw = String(value ?? '').trim();
            const options = Array.isArray(meta.options) ? meta.options : [];
            return options.some((option) => String(option.value) === raw) ? raw : meta.default;
        }
        return String(value ?? meta.default);
    };
    const readNtcfgLeaderboardsValue = (settingKey) => {
        const meta = LEADERBOARDS_SHARED_SETTINGS[settingKey];
        if (!meta) return undefined;
        try {
            const canonical = readCanonicalValue(getNtcfgLeaderboardsStorageKey(settingKey));
            if (canonical !== undefined) {
                return coerceNtcfgLeaderboardsValue(settingKey, canonical);
            }
            const raw = localStorage.getItem(getNtcfgLeaderboardsStorageKey(settingKey));
            if (raw == null) return meta.default;
            const parsed = JSON.parse(raw);
            return coerceNtcfgLeaderboardsValue(settingKey, parsed);
        } catch {
            return meta.default;
        }
    };
    const writeNtcfgLeaderboardsValue = (settingKey, value) => {
        try {
            writeCanonicalValue(getNtcfgLeaderboardsStorageKey(settingKey), value);
            const serialized = JSON.stringify(value);
            if (localStorage.getItem(getNtcfgLeaderboardsStorageKey(settingKey)) !== serialized) {
                localStorage.setItem(getNtcfgLeaderboardsStorageKey(settingKey), serialized);
            }
        } catch {
            // ignore storage sync failures
        }
    };
    const registerNtcfgLeaderboardsManifest = () => {
        try {
            const manifest = {
                id: NTCFG_LEADERBOARDS_MANIFEST_ID,
                name: 'Startrack Leaderboard Integration',
                version: NTCFG_LEADERBOARDS_BRIDGE_VERSION,
                scriptVersion: typeof GM_info !== 'undefined' ? GM_info.script.version : '',
                storageVersion: SETTINGS_STORAGE_VERSION,
                supportsGlobalReset: true,
                description: 'Custom Startrack-powered leaderboards with multiple timeframes and caching.',
                sections: [
                    { id: 'views', title: 'Views', subtitle: 'Default view and display preferences.', resetButton: true },
                    { id: 'sync', title: 'Sync', subtitle: 'Cache and data refresh settings.', resetButton: true },
                    { id: 'presentation', title: 'Presentation', subtitle: 'Visual behavior and navigation controls.', resetButton: true },
                    { id: 'advanced', title: 'Advanced', subtitle: 'Debug and diagnostic controls.', resetButton: true }
                ],
                settings: LEADERBOARDS_SHARED_SETTINGS
            };
            const serialized = JSON.stringify(manifest);
            if (localStorage.getItem(NTCFG_LEADERBOARDS_MANIFEST_KEY) !== serialized) {
                localStorage.setItem(NTCFG_LEADERBOARDS_MANIFEST_KEY, serialized);
            }
        } catch {
            // ignore manifest registration failures
        }
    };
    // Direct apply: writes to localStorage (used for same-tab ntcfg:change events from mod menu)
    const applyNtcfgLeaderboardsValueDirect = (settingKey, value) => {
        const meta = LEADERBOARDS_SHARED_SETTINGS[settingKey];
        if (!meta) return;
        const normalized = coerceNtcfgLeaderboardsValue(settingKey, value);
        writeNtcfgLeaderboardsValue(settingKey, normalized);
        // Sync DEBUG_LOGGING bidirectionally with the existing ntStartrackDebug localStorage key
        if (settingKey === 'DEBUG_LOGGING') {
            syncLeaderboardsDebugToggle(normalized);
        }
        applyLeaderboardsSettingSideEffects(settingKey);
    };
    // Deduped apply: compares before writing (used for cross-tab storage events)
    const applyNtcfgLeaderboardsValueIfChanged = (settingKey, value) => {
        const meta = LEADERBOARDS_SHARED_SETTINGS[settingKey];
        if (!meta) return;
        const normalized = coerceNtcfgLeaderboardsValue(settingKey, value);
        const currentValue = readNtcfgLeaderboardsValue(settingKey);
        if (JSON.stringify(currentValue) !== JSON.stringify(normalized)) {
            writeNtcfgLeaderboardsValue(settingKey, normalized);
            if (settingKey === 'DEBUG_LOGGING') {
                syncLeaderboardsDebugToggle(normalized);
            }
            applyLeaderboardsSettingSideEffects(settingKey);
        }
    };
    // Bidirectional sync for DEBUG_LOGGING <-> ntStartrackDebug localStorage key
    const syncLeaderboardsDebugToggle = (enabled) => {
        try {
            localStorage.setItem('ntStartrackDebug', enabled ? '1' : '0');
        } catch {
            // ignore storage errors
        }
    };
    // Helper to check if a specific leaderboards feature is enabled via ntcfg
    function isNtcfgLeaderboardsFeatureEnabled(settingKey) {
        const val = readNtcfgLeaderboardsValue(settingKey);
        return val !== false;
    }
    // Seed localStorage values from defaults for any settings not yet stored
    const seedNtcfgLeaderboardsDefaults = () => {
        Object.keys(LEADERBOARDS_SHARED_SETTINGS).forEach((settingKey) => {
            const meta = LEADERBOARDS_SHARED_SETTINGS[settingKey];
            const storageKey = getNtcfgLeaderboardsStorageKey(settingKey);
            if (readCanonicalValue(storageKey) === undefined && localStorage.getItem(storageKey) == null) {
                writeNtcfgLeaderboardsValue(settingKey, meta.default);
            }
            // Sync DEBUG_LOGGING bidirectionally with existing ntStartrackDebug key
            if (settingKey === 'DEBUG_LOGGING') {
                const debugVal = localStorage.getItem('ntStartrackDebug');
                if (debugVal === '1') {
                    writeNtcfgLeaderboardsValue(settingKey, true);
                }
            }
        });
    };
    const syncAllNtcfgLeaderboardsSettings = () => {
        Object.keys(LEADERBOARDS_SHARED_SETTINGS).forEach((settingKey) => {
            writeNtcfgLeaderboardsValue(settingKey, readNtcfgLeaderboardsValue(settingKey));
        });
    };
    // Listen for mod menu changes (same tab)
    document.addEventListener('ntcfg:change', (event) => {
        if (event?.detail?.script !== NTCFG_LEADERBOARDS_MANIFEST_ID) return;
        applyNtcfgLeaderboardsValueDirect(event.detail.key, event.detail.value);
    });
    document.addEventListener('ntcfg:action', (event) => {
        const detail = event?.detail || {};
        if (detail.script !== '*') return;
        if (detail.key !== 'clear-settings' || detail.scope !== 'prefs+caches') return;
        try {
            Object.keys(LEADERBOARDS_SHARED_SETTINGS).forEach((settingKey) => {
                writeNtcfgLeaderboardsValue(settingKey, LEADERBOARDS_SHARED_SETTINGS[settingKey].default);
            });
            try {
                Object.keys(localStorage).forEach((key) => {
                    if (key.startsWith('ntStartrackCache_') ||
                        key.startsWith('ntStartrackCacheExp_') ||
                        key.startsWith('ntStartrackCacheTime_') ||
                        key === 'ntStartrackSeasonCache' ||
                        key === 'ntStartrackDailySyncCT' ||
                        key === 'ntStartrackHourlySyncCT' ||
                        key === 'ntCarDataMap' ||
                        key === 'ntCarDataMapTimestamp' ||
                        key === 'ntStartrackDebug' ||
                        key === 'ntStartrackDebugSeasonOnly') {
                        localStorage.removeItem(key);
                    }
                });
            } catch {
                // ignore localStorage cleanup failures
            }
            clearLeaderboardsSharedCache();
            writeCanonicalValue(STORAGE_VERSION_KEY, SETTINGS_STORAGE_VERSION);
            writeCanonicalValue(STORAGE_MIGRATED_AT_KEY, Date.now());
            writeCanonicalValue(STORAGE_CLEANUP_AFTER_KEY, Date.now() + LEGACY_CLEANUP_GRACE_MS);
            registerNtcfgLeaderboardsManifest();
            syncAllNtcfgLeaderboardsSettings();
            applyAllLeaderboardsSettingSideEffects();
            document.dispatchEvent(new CustomEvent('ntcfg:manifest-updated', {
                detail: { script: NTCFG_LEADERBOARDS_MANIFEST_ID }
            }));
            dispatchLeaderboardsActionResult(detail.requestId, 'success');
        } catch (error) {
            dispatchLeaderboardsActionResult(detail.requestId, 'error', error?.message || String(error));
        }
    });
    // Listen for cross-tab changes
    window.addEventListener('storage', (event) => {
        const storageKey = String(event?.key || '');
        if (!storageKey.startsWith(NTCFG_LEADERBOARDS_VALUE_PREFIX) || event.newValue == null) return;
        const settingKey = storageKey.slice(NTCFG_LEADERBOARDS_VALUE_PREFIX.length);
        if (!LEADERBOARDS_SHARED_SETTINGS[settingKey]) return;
        try {
            applyNtcfgLeaderboardsValueIfChanged(settingKey, JSON.parse(event.newValue));
        } catch {
            // ignore invalid synced payloads
        }
    });
    const ensureLeaderboardsStorageMigration = () => {
        const currentVersion = readStorageMetaNumber(STORAGE_VERSION_KEY, 0);
        const migratedAt = readStorageMetaNumber(STORAGE_MIGRATED_AT_KEY, 0);
        const now = Date.now();
        if (currentVersion < SETTINGS_STORAGE_VERSION) {
            syncAllNtcfgLeaderboardsSettings();
            writeCanonicalValue(STORAGE_VERSION_KEY, SETTINGS_STORAGE_VERSION);
            if (!migratedAt) {
                writeCanonicalValue(STORAGE_MIGRATED_AT_KEY, now);
                writeCanonicalValue(STORAGE_CLEANUP_AFTER_KEY, now + LEGACY_CLEANUP_GRACE_MS);
            }
        }
        const cleanupAfter = readStorageMetaNumber(STORAGE_CLEANUP_AFTER_KEY, 0);
        if (cleanupAfter && now >= cleanupAfter) {
            try {
                localStorage.removeItem('ntStartrackDebug');
            } catch {
                // ignore legacy cleanup failures
            }
        }
    };
    const publishLeaderboardsManifestHeartbeat = () => {
        try { localStorage.setItem('ntcfg:alive:' + NTCFG_LEADERBOARDS_MANIFEST_ID, String(Date.now())); } catch { /* ignore */ }
        try {
            document.dispatchEvent(new CustomEvent('ntcfg:manifest-updated', {
                detail: { script: NTCFG_LEADERBOARDS_MANIFEST_ID }
            }));
        } catch {
            // ignore event dispatch failures
        }
    };
    // Register manifest and seed defaults
    ensureLeaderboardsStorageMigration();
    registerNtcfgLeaderboardsManifest();
    seedNtcfgLeaderboardsDefaults();
    syncAllNtcfgLeaderboardsSettings();
    publishLeaderboardsManifestHeartbeat();

    function initNTRouteHelper(targetWindow = window) {
        const hostWindow = targetWindow || window;
        const existing = hostWindow.NTRouteHelper;
        if (existing && existing.__ntRouteHelperReady && typeof existing.subscribe === 'function') {
            return existing;
        }

        const helper = existing || {};
        const listeners = helper.listeners instanceof Set ? helper.listeners : new Set();
        let currentKey = `${hostWindow.location.pathname}${hostWindow.location.search}${hostWindow.location.hash}`;

        const notify = (reason) => {
            const nextKey = `${hostWindow.location.pathname}${hostWindow.location.search}${hostWindow.location.hash}`;
            if (reason !== 'init' && nextKey === currentKey) return;
            const previousKey = currentKey;
            currentKey = nextKey;
            listeners.forEach((listener) => {
                try {
                    listener({
                        reason,
                        previous: previousKey,
                        current: nextKey,
                        pathname: hostWindow.location.pathname
                    });
                } catch (error) {
                    console.error('[NTRouteHelper] listener error', error);
                }
            });
            helper.currentKey = currentKey;
        };

        if (!helper.__ntRouteHelperWrapped) {
            const wrapHistoryMethod = (methodName) => {
                const current = hostWindow.history[methodName];
                if (typeof current !== 'function' || current.__ntRouteHelperWrapped) return;
                const wrapped = function() {
                    const result = current.apply(this, arguments);
                    queueMicrotask(() => notify(methodName));
                    return result;
                };
                wrapped.__ntRouteHelperWrapped = true;
                wrapped.__ntRouteHelperOriginal = current;
                hostWindow.history[methodName] = wrapped;
            };
            wrapHistoryMethod('pushState');
            wrapHistoryMethod('replaceState');
            hostWindow.addEventListener('popstate', () => queueMicrotask(() => notify('popstate')));
            helper.__ntRouteHelperWrapped = true;
        }

        helper.listeners = listeners;
        helper.currentKey = currentKey;
        helper.version = '1.0.0';
        helper.__ntRouteHelperReady = true;
        helper.subscribe = function(listener, options = {}) {
            if (typeof listener !== 'function') return () => {};
            listeners.add(listener);
            if (options.immediate !== false) {
                try {
                    listener({
                        reason: 'init',
                        previous: currentKey,
                        current: currentKey,
                        pathname: hostWindow.location.pathname
                    });
                } catch (error) {
                    console.error('[NTRouteHelper] immediate listener error', error);
                }
            }
            return () => listeners.delete(listener);
        };
        helper.notify = notify;

        hostWindow.NTRouteHelper = helper;
        return helper;
    }

    // ─── End Mod Menu Manifest Bridge ───────────────────────────────────────────
    // =================================================================
    // 1. CSS & ANTI-FLICKER SYSTEM
    // =================================================================
    const globalStyles = `
        /* Curtain Logic */
        html.is-leaderboard-route main.structure-content {
            opacity: 0 !important;
            visibility: hidden !important;
        }
        html.is-leaderboard-route main.structure-content.custom-loaded {
            opacity: 1 !important;
            visibility: visible !important;
            transition: opacity 0.15s ease-in;
        }
        /* Spin Animation for Refresh Icon */
        @keyframes nt-spin { 100% { transform: rotate(360deg); } }
        .icon-spin { animation: nt-spin 1s linear infinite; }
        /* Refresh Button Styling */
        #manual-refresh-btn {
            transition: opacity 0.2s, color 0.2s;
            cursor: pointer;
            vertical-align: middle;
        }
        #manual-refresh-btn:hover {
            opacity: 1 !important;
            color: #fff !important;
        }
        /* Position Change Arrows */
        .position-change {
            font-size: 10px;
            font-weight: bold;
            margin-right: 4px;
            vertical-align: middle;
            display: inline-flex;
            align-items: center;
            gap: 1px;
        }
        .position-change--up {
            color: #28a745;
        }
        .position-change--down {
            color: #dc3545;
        }
    `;
    const styleEl = document.createElement('style');
    styleEl.textContent = globalStyles;
    (document.head || document.documentElement).appendChild(styleEl);
    // Hide Class Tab CSS — injected at document-start to prevent flicker
    const hideClassTabStyle = document.createElement('style');
    hideClassTabStyle.id = 'ntcfg-hide-class-tab';
    hideClassTabStyle.textContent = `
        a[href*="/class"], a[href*="/classes"] {
            display: none !important;
            opacity: 0 !important;
        }
        .nav-list-item:has(a[href*="/class"]),
        .nav-list-item:has(a[href*="/classes"]) {
            display: none !important;
            width: 0 !important;
            margin: 0 !important;
            padding: 0 !important;
            border: none !important;
        }
    `;
    function syncHideClassTabState() {
        if (readNtcfgLeaderboardsValue('HIDE_CLASS_TAB')) {
            if (!document.getElementById('ntcfg-hide-class-tab')) {
                (document.head || document.documentElement).appendChild(hideClassTabStyle);
            }
        } else {
            document.getElementById('ntcfg-hide-class-tab')?.remove();
        }
    }
    syncHideClassTabState();
    function updateRouteStatus() {
        if (location.pathname === '/leaderboards' && isNtcfgLeaderboardsFeatureEnabled('ANTI_FLICKER_MODE')) {
            document.documentElement.classList.add('is-leaderboard-route');
        } else {
            document.documentElement.classList.remove('is-leaderboard-route');
            const main = document.querySelector('main.structure-content');
            if (main) main.classList.remove('custom-loaded');
        }
    }
    updateRouteStatus();
    const ntRouteHelper = initNTRouteHelper(window);
    // =================================================================
    // 2. SHARED OPTIMIZATION LAYER (Compat across scripts)
    // =================================================================
    function initNTShared() {
        const shared = window.NTShared || {};
        shared.version = '2.0.0';
        const existingCache = shared.cache;
        const hasMapCache = existingCache instanceof Map;
        const cacheMap = shared.cacheMap || (hasMapCache ? existingCache : new Map());
        const cacheObj = shared.cacheObj || (!hasMapCache && existingCache && typeof existingCache === 'object' ? existingCache : {
            individual: { data: null, timestamp: 0, expiresAt: 0 },
            team: { data: null, timestamp: 0, expiresAt: 0 },
            isbot: new Map()
        });
        if (!cacheObj.individual) cacheObj.individual = { data: null, timestamp: 0, expiresAt: 0 };
        if (!cacheObj.team) cacheObj.team = { data: null, timestamp: 0, expiresAt: 0 };
        if (!cacheObj.isbot) cacheObj.isbot = new Map();
        const isbot = shared.isbot || cacheObj.isbot || new Map();
        cacheObj.isbot = isbot;
        if (!shared.cache) shared.cache = cacheObj;
        shared.cacheMap = cacheMap;
        shared.cacheObj = cacheObj;
        shared.isbot = isbot;
        shared._makeKey = shared._makeKey || function(view, timeframe, startKey, endKey) {
            return `${view}_${timeframe}_${startKey}_${endKey}`;
        };
        const BOT_STATUS_TTL_MS = shared.BOT_STATUS_TTL_MS || (24 * 60 * 60 * 1000);
        const BOT_STATUS_MAX_ENTRIES = shared.BOT_STATUS_MAX_ENTRIES || 5000;
        shared.BOT_STATUS_TTL_MS = BOT_STATUS_TTL_MS;
        shared.BOT_STATUS_MAX_ENTRIES = BOT_STATUS_MAX_ENTRIES;
        const syncState = shared._syncState || {
            initialized: false,
            tabId: `tab_${Math.random().toString(36).slice(2)}_${Date.now()}`,
            broadcastChannel: null,
            storageKey: 'ntSharedCacheSyncV1'
        };
        shared._syncState = syncState;
        function emitLocalUpdate(detail) {
            window.dispatchEvent(new CustomEvent('nt-cache-updated', { detail: detail }));
        }
        function normalizeBotEntry(raw, now) {
            if (!raw) return null;
            if (raw && typeof raw === 'object' && raw.status && typeof raw.ts === 'number') return raw;
            return { status: raw, ts: now };
        }
        function pruneBotCache() {
            const now = Date.now();
            isbot.forEach((value, key) => {
                const entry = normalizeBotEntry(value, now);
                if (!entry) {
                    isbot.delete(key);
                    return;
                }
                if (now - entry.ts > BOT_STATUS_TTL_MS) {
                    isbot.delete(key);
                    return;
                }
                if (entry !== value) isbot.set(key, entry);
            });
            if (isbot.size <= BOT_STATUS_MAX_ENTRIES) return;
            const entries = Array.from(isbot.entries())
                .map(([key, value]) => [key, normalizeBotEntry(value, now)])
                .filter(([, value]) => !!value)
                .sort((a, b) => a[1].ts - b[1].ts);
            const overflow = entries.length - BOT_STATUS_MAX_ENTRIES;
            for (let i = 0; i < overflow; i++) {
                isbot.delete(entries[i][0]);
            }
        }
        function postCrossTabUpdate(payload) {
            if (!payload || !syncState.initialized) return;
            const message = Object.assign({}, payload, {
                __ntSharedSync: true,
                origin: syncState.tabId,
                sentAt: Date.now()
            });
            if (syncState.broadcastChannel) {
                try { syncState.broadcastChannel.postMessage(message); } catch (e) {}
            }
            try {
                localStorage.setItem(syncState.storageKey, JSON.stringify(message));
                localStorage.removeItem(syncState.storageKey);
            } catch (e) {}
        }
        function setLegacyCacheInternal(type, data, expiresAt, options = {}) {
            if (!cacheObj[type]) cacheObj[type] = { data: null, timestamp: 0, expiresAt: 0 };
            const now = options.timestamp || Date.now();
            cacheObj[type].data = data;
            cacheObj[type].timestamp = now;
            cacheObj[type].expiresAt = expiresAt || (now + 3600000);
            emitLocalUpdate({ type, data, expiresAt: cacheObj[type].expiresAt, source: options.source || 'local' });
            if (options.broadcast !== false) {
                postCrossTabUpdate({
                    kind: 'legacy',
                    type: type,
                    data: data,
                    expiresAt: cacheObj[type].expiresAt,
                    timestamp: now
                });
            }
        }
        function setKeyedCacheInternal(key, data, expiresAt, options = {}) {
            const now = options.timestamp || Date.now();
            const ttl = expiresAt || (now + 3600000);
            cacheMap.set(key, { data: data, timestamp: now, expiresAt: ttl });
            emitLocalUpdate({ key, data, expiresAt: ttl, source: options.source || 'local' });
            if (options.broadcast !== false) {
                postCrossTabUpdate({
                    kind: 'keyed',
                    key: key,
                    data: data,
                    expiresAt: ttl,
                    timestamp: now
                });
            }
        }
        function setBotStatusInternal(username, status, options = {}) {
            if (!username) return;
            const key = username.toLowerCase();
            const now = options.timestamp || Date.now();
            isbot.set(key, { status: status, ts: now });
            pruneBotCache();
            emitLocalUpdate({ type: 'isbot', username: key, data: status, source: options.source || 'local' });
            if (options.broadcast !== false) {
                postCrossTabUpdate({
                    kind: 'isbot',
                    username: key,
                    status: status,
                    timestamp: now
                });
            }
        }
        function applyCrossTabMessage(message, source) {
            if (!message || message.origin === syncState.tabId) return;
            if (message.kind === 'legacy' && message.type) {
                setLegacyCacheInternal(message.type, message.data, message.expiresAt, {
                    broadcast: false,
                    timestamp: message.timestamp,
                    source: source || 'remote'
                });
                return;
            }
            if (message.kind === 'keyed' && message.key) {
                setKeyedCacheInternal(message.key, message.data, message.expiresAt, {
                    broadcast: false,
                    timestamp: message.timestamp,
                    source: source || 'remote'
                });
                return;
            }
            if (message.kind === 'isbot' && message.username) {
                setBotStatusInternal(message.username, message.status, {
                    broadcast: false,
                    timestamp: message.timestamp,
                    source: source || 'remote'
                });
            }
        }
        if (!syncState.initialized) {
            syncState.initialized = true;
            if (typeof BroadcastChannel !== 'undefined') {
                try {
                    syncState.broadcastChannel = new BroadcastChannel('ntSharedCacheSyncV1');
                    syncState.broadcastChannel.onmessage = function(event) {
                        const message = event ? event.data : null;
                        if (!message || !message.__ntSharedSync) return;
                        applyCrossTabMessage(message, 'broadcast');
                    };
                } catch (e) {}
            }
            window.addEventListener('storage', (event) => {
                if (!event || event.key !== syncState.storageKey || !event.newValue) return;
                try {
                    const message = JSON.parse(event.newValue);
                    if (!message || !message.__ntSharedSync) return;
                    applyCrossTabMessage(message, 'storage');
                } catch (e) {}
            });
        }
        shared.setLegacyCache = function(type, data, expiresAt) {
            setLegacyCacheInternal(type, data, expiresAt);
        };
        shared.setCache = function(key, data, expiresAt) {
            if (typeof key === 'string' && cacheObj[key]) {
                setLegacyCacheInternal(key, data, expiresAt);
                return;
            }
            setKeyedCacheInternal(key, data, expiresAt);
        };
        shared.getCache = function(key, maxAge) {
            const now = Date.now();
            const maxAgeMs = typeof maxAge === 'number' ? maxAge : Number.POSITIVE_INFINITY;
            if (typeof key === 'string' && cacheObj[key]) {
                const cached = cacheObj[key];
                if (!cached || !cached.data) return null;
                const age = now - (cached.timestamp || 0);
                if (age < maxAgeMs && now < (cached.expiresAt || 0)) return cached.data;
                return null;
            }
            const cached = cacheMap.get(key);
            if (!cached || !cached.data) return null;
            const age = now - (cached.timestamp || 0);
            if (age < maxAgeMs && now < cached.expiresAt) return cached.data;
            return null;
        };
        shared.getTimestamp = function(key) {
            if (typeof key === 'string' && cacheObj[key]) return cacheObj[key].timestamp || null;
            const cached = cacheMap.get(key);
            return cached?.timestamp || null;
        };
        shared.getLegacyCache = function(type, maxAge) {
            return shared.getCache(type, maxAge);
        };
        shared.getBotStatus = function(username) {
            if (!username) return null;
            const key = username.toLowerCase();
            const value = isbot.get(key);
            if (!value) return null;
            const entry = normalizeBotEntry(value, Date.now());
            if (!entry) return null;
            if (Date.now() - entry.ts > BOT_STATUS_TTL_MS) {
                isbot.delete(key);
                return null;
            }
            if (entry !== value) isbot.set(key, entry);
            return entry.status;
        };
        shared.setBotStatus = function(username, status) {
            setBotStatusInternal(username, status);
        };
        window.NTShared = shared;
        return shared;
    }
    initNTShared();
    // --- CONFIGURATION ---
    const TAB_CLASS = 'nt-custom-leaderboards';
    const LEADERBOARD_PATH = '/leaderboards';
    const CACHE_KEY = 'ntStartrackCache_';
    const SHARED_CACHE_PREFIX = 'ntStartrackCacheExp_';
    const CACHE_TIMESTAMP_KEY = 'ntStartrackCacheTime_';
    const SEASON_CACHE_KEY = 'ntStartrackSeasonCache';
    const SHARED_LEADERBOARD_SCHEMA_VERSION = 1;
    const SHARED_LEADERBOARD_BOT_POLICY = 'server_filtered';
    function clearLeaderboardsSharedCache() {
        const shared = window.NTShared;
        if (!shared || typeof shared !== 'object') return;
        const cacheObj = shared.cacheObj || shared.cache;
        if (cacheObj && typeof cacheObj === 'object') {
            ['individual', 'team'].forEach((type) => {
                if (cacheObj[type] && typeof cacheObj[type] === 'object') {
                    cacheObj[type] = { data: null, timestamp: 0, expiresAt: 0 };
                }
            });
        }
        const cacheMap = shared.cacheMap instanceof Map ? shared.cacheMap : null;
        if (!cacheMap) return;
        Array.from(cacheMap.keys()).forEach((key) => {
            if (typeof key !== 'string') return;
            if (key.startsWith(CACHE_KEY) || key.startsWith(SHARED_CACHE_PREFIX)) {
                cacheMap.delete(key);
            }
        });
    }
    function getCacheDurationMs() {
        const minutes = readNtcfgLeaderboardsValue('CACHE_DURATION_MINUTES');
        return (typeof minutes === 'number' && minutes > 0 ? minutes : 60) * 60 * 1000;
    }
    const DAILY_SYNC_KEY = 'ntStartrackDailySyncCT';
    const HOURLY_SYNC_KEY = 'ntStartrackHourlySyncCT';
    const SEASON_REFRESH_MS = 6 * 60 * 60 * 1000;
    const DEBUG_FLAG_KEY = 'ntStartrackDebug';
    const DEBUG_SEASON_ONLY_KEY = 'ntStartrackDebugSeasonOnly';
    const BOOTSTRAP_SCRIPT_PATH_FALLBACK = '/index/faf5993982a8235165748e39847ee73ba1dea710-1847/bootstrap.js';
    const BACKGROUND_SYNC_DELAY_MS = 350;
    const ASYNC_DELAY = 50;
    const DEBUG_PARAM_ENABLED = /(?:\?|&)ntdebug=1(?:&|$)/.test(window.location.search);
    const DEBUG_ENABLED = DEBUG_PARAM_ENABLED || localStorage.getItem(DEBUG_FLAG_KEY) === '1' || readNtcfgLeaderboardsValue('DEBUG_LOGGING') === true;
    const DEBUG_SEASON_ONLY = localStorage.getItem(DEBUG_SEASON_ONLY_KEY) === '1';
    // --- STATE ---
    let cacheQueue = [];
    let isCaching = false;
    let forceBackgroundUpdate = false;
    let initialCacheComplete = false;
    let carDataMap = {};
    let carDataLoaded = false;
    let carDataLoadAttempts = 0;
    const MAX_CAR_LOAD_ATTEMPTS = 10;
    let lastCheckedHour = null;
    let hourlyCheckInterval = null;
    let pageRenderInProgress = false;
    let leaderboardsPageSyncTimer = null;
    let pendingLeaderboardsRouteSync = false;
    let leaderboardPageRefreshTimer = null;
    let pendingLeaderboardForceRefresh = false;
    let leaderboardShellObserver = null;
    let navigationChromeObserver = null;
    let seasonBootstrapFetchInFlight = false;
    let seasonBootstrapLastAttemptMs = 0;
    // Dynamic season data (loaded from NTBOOTSTRAP)
    let currentSeason = {
        seasonID: null,
        name: 'Season',
        startCT: null,
        endCT: null,
        startStampUTC: null,
        endStampUTC: null
    };
    let state = {
        view: readNtcfgLeaderboardsValue('DEFAULT_VIEW') || 'individual',
        timeframe: readNtcfgLeaderboardsValue('DEFAULT_TIMEFRAME') || 'season',
        currentDate: getCurrentCT(),
        dateRange: { start: null, end: null }
    };
    function shouldLogDebug(payload) {
        if (!DEBUG_ENABLED) return false;
        if (!DEBUG_SEASON_ONLY) return true;
        if (!payload || typeof payload !== 'object') return false;
        const tf = payload.timeframe || payload.targetTimeframe || payload.stateTimeframe;
        return tf === 'season' || payload.reason === 'season';
    }
    function debugLog(event, payload) {
        if (!shouldLogDebug(payload)) return;
        const ts = new Date().toISOString();
        if (payload === undefined) {
            console.log(`[Startrack][DBG ${ts}] ${event}`);
            return;
        }
        console.log(`[Startrack][DBG ${ts}] ${event}`, payload);
    }
    function shortCacheKey(cacheKey) {
        if (!cacheKey) return '';
        return cacheKey.replace(CACHE_KEY, '');
    }
    function getCacheFreshness(cacheKey, ttlMs = getCacheDurationMs()) {
        const timestampKey = CACHE_TIMESTAMP_KEY + cacheKey;
        const rawTimestamp = localStorage.getItem(timestampKey);
        if (!rawTimestamp) {
            return { hasTimestamp: false, fresh: false, ageMs: null, ttlMs: ttlMs };
        }
        const timestamp = parseInt(rawTimestamp, 10);
        const ageMs = Date.now() - timestamp;
        return { hasTimestamp: true, fresh: ageMs < ttlMs, ageMs: ageMs, ttlMs: ttlMs, timestamp: timestamp };
    }
    const timeframes = [
        { key: 'season', label: 'Season', hasNav: false },
        { key: '24hr', label: 'Last 24 Hours', hasNav: false },
        { key: '60min', label: '60 Minutes', hasNav: false },
        { key: '7day', label: 'Last 7 Days', hasNav: false },
        { key: 'daily', label: 'Daily', hasNav: true },
        { key: 'weekly', label: 'Weekly', hasNav: true },
        { key: 'monthly', label: 'Monthly', hasNav: true }
        // { key: 'custom', label: 'Custom', hasNav: false }  // TODO: Add custom date picker UI
    ];
    // =================================================================
    // 3. TIMEZONE & SEASON UTILITIES
    // =================================================================
    function getCurrentCT() {
        const now = new Date();
        const ctString = now.toLocaleString("en-US", { timeZone: "America/Chicago" });
        return new Date(ctString);
    }
    function getCTDateKey() {
        const ct = getCurrentCT();
        const y = ct.getFullYear();
        const m = String(ct.getMonth() + 1).padStart(2, '0');
        const d = String(ct.getDate()).padStart(2, '0');
        return `${y}-${m}-${d}`;
    }
    function getCTHourKey() {
        const ct = getCurrentCT();
        const y = ct.getFullYear();
        const m = String(ct.getMonth() + 1).padStart(2, '0');
        const d = String(ct.getDate()).padStart(2, '0');
        const h = String(ct.getHours()).padStart(2, '0');
        return `${y}-${m}-${d}-${h}`;
    }
    function getMsUntilNextCTMidnight() {
        const ct = getCurrentCT();
        const next = new Date(ct);
        next.setHours(24, 0, 0, 0);
        return Math.max(0, next.getTime() - ct.getTime());
    }
    function getCacheTTLForTimeframe(timeframe) {
        if (timeframe === 'season') return SEASON_REFRESH_MS;
        if (timeframe === 'daily' || timeframe === 'weekly' || timeframe === 'monthly') {
            return getMsUntilNextCTMidnight();
        }
        if (timeframe === '60min' || timeframe === '24hr' || timeframe === '7day') {
            return getCacheDurationMs();
        }
        return getCacheDurationMs();
    }
    function getCurrentHour() { return new Date().getHours(); }
    function getSharedRowLimit(view) { return view === 'individual' ? 300 : 50; }
    function getSharedCacheKey(tempState) {
        return getCacheKey(tempState).replace(CACHE_KEY, SHARED_CACHE_PREFIX);
    }
    function sanitizeLeaderboardRows(view, rows) {
        if (!Array.isArray(rows)) return [];
        let removedBotRows = 0;
        const sanitized = rows.filter((entry) => {
            if (!entry || typeof entry !== 'object') return false;
            if (entry.bot === 1) {
                removedBotRows += 1;
                return false;
            }
            if (view === 'individual') {
                return !!String(entry.Username || entry.username || '').trim();
            }
            return !!String(entry.TeamTag || entry.teamTag || entry.TeamName || entry.teamName || '').trim();
        });
        if (removedBotRows > 0) {
            debugLog('Unexpected bot rows removed from payload', {
                view,
                removedBotRows
            });
        }
        return sanitized;
    }
    function buildSharedLeaderboardPayload(view, timeframe, rows, writtenAt, expiresAt) {
        return {
            schemaVersion: SHARED_LEADERBOARD_SCHEMA_VERSION,
            source: `leaderboards@${typeof GM_info !== 'undefined' ? GM_info.script.version : 'unknown'}`,
            botPolicy: SHARED_LEADERBOARD_BOT_POLICY,
            view,
            timeframe,
            rowLimit: getSharedRowLimit(view),
            writtenAt,
            expiresAt,
            rows: Array.isArray(rows) ? rows.slice(0, getSharedRowLimit(view)) : []
        };
    }
    function saveSharedPayloadToLocalStorage(cacheKey, payload) {
        if (!cacheKey || !payload) return;
        try {
            localStorage.setItem(cacheKey, JSON.stringify(payload));
            localStorage.setItem(CACHE_TIMESTAMP_KEY + cacheKey, String(payload.writtenAt || Date.now()));
        } catch (error) {
            debugLog('Shared payload localStorage write failed', {
                key: shortCacheKey(cacheKey),
                message: error && error.message ? error.message : String(error)
            });
        }
    }
    // Convert Unix timestamp (UTC seconds) to CT-formatted string for Startrack API
    function utcToCTString(unixTimestamp) {
        const date = new Date(unixTimestamp * 1000);
        const ctString = date.toLocaleString("en-US", { timeZone: "America/Chicago" });
        const ctDate = new Date(ctString);
        return formatDate(ctDate);
    }
    // Convert Unix timestamp to user's local timezone for display
    function utcToLocalDate(unixTimestamp) {
        return new Date(unixTimestamp * 1000);
    }
    // Check if cache should be refreshed (within 1 week of season end)
    function shouldRefreshSeasonCache(endStampUTC) {
        if (!endStampUTC) return true;
        const oneWeekMs = 7 * 24 * 60 * 60 * 1000;
        const endMs = endStampUTC * 1000;
        const now = Date.now();
        return (endMs - now) < oneWeekMs;
    }
    function hasValidSeasonData() {
        return !!(
            currentSeason &&
            currentSeason.startCT &&
            currentSeason.endCT &&
            currentSeason.startStampUTC &&
            currentSeason.endStampUTC
        );
    }
    function parseActiveSeasonsFromBootstrapPayload(bootstrapData) {
        if (!Array.isArray(bootstrapData)) return null;
        const seasonsData = bootstrapData.find(item => Array.isArray(item) && item[0] === 'ACTIVE_SEASONS');
        if (!seasonsData || !Array.isArray(seasonsData[1]) || seasonsData[1].length === 0) return null;
        return seasonsData[1];
    }
    function parseActiveSeasonsFromBootstrapScript(scriptText) {
        if (!scriptText || typeof scriptText !== 'string') return null;
        const match = scriptText.match(/\["ACTIVE_SEASONS",(\[[\s\S]*?\])\],\["ACTIVE_EVENTS"/);
        if (!match || !match[1]) return null;
        try {
            const parsed = JSON.parse(match[1]);
            return Array.isArray(parsed) && parsed.length > 0 ? parsed : null;
        } catch (e) {
            return null;
        }
    }
    function pickActiveSeason(seasons) {
        if (!Array.isArray(seasons) || seasons.length === 0) return null;
        const now = Math.floor(Date.now() / 1000);
        const active = seasons.find(s => now >= s.startStamp && now < s.endStamp);
        if (active) return active;
        return seasons.slice().sort((a, b) => (b.startStamp || 0) - (a.startStamp || 0))[0] || null;
    }
    function applySeasonFromRecord(seasonRecord, source) {
        if (!seasonRecord) return false;
        currentSeason = {
            seasonID: seasonRecord.seasonID,
            name: seasonRecord.name,
            startStampUTC: seasonRecord.startStamp,
            endStampUTC: seasonRecord.endStamp,
            startCT: utcToCTString(seasonRecord.startStamp),
            endCT: utcToCTString(seasonRecord.endStamp)
        };
        localStorage.setItem(SEASON_CACHE_KEY, JSON.stringify(currentSeason));
        debugLog('Season loaded', {
            source: source,
            seasonName: currentSeason.name,
            targetTimeframe: 'season'
        });
        debugLog('Season source selected', {
            source: source,
            seasonName: currentSeason.name,
            seasonID: currentSeason.seasonID,
            startStampUTC: currentSeason.startStampUTC,
            endStampUTC: currentSeason.endStampUTC,
            targetTimeframe: 'season'
        });
        return true;
    }
    function getBootstrapScriptUrl() {
        const scriptEl = document.querySelector('script[src*="/bootstrap.js"]');
        if (scriptEl && scriptEl.src) return scriptEl.src;
        return new URL(BOOTSTRAP_SCRIPT_PATH_FALLBACK, window.location.origin).href;
    }
    function fetchSeasonDataFromBootstrapScript() {
        const now = Date.now();
        const retryDelayMs = 5 * 60 * 1000;
        if (seasonBootstrapFetchInFlight) return;
        if (now - seasonBootstrapLastAttemptMs < retryDelayMs) return;
        seasonBootstrapFetchInFlight = true;
        seasonBootstrapLastAttemptMs = now;
        const bootstrapUrl = getBootstrapScriptUrl();
        debugLog('Season bootstrap script fetch start', {
            source: 'bootstrap-script',
            url: bootstrapUrl,
            targetTimeframe: 'season'
        });
        GM_xmlhttpRequest({
            method: 'GET',
            url: bootstrapUrl,
            onload: function(response) {
                seasonBootstrapFetchInFlight = false;
                if (response.status !== 200 || !response.responseText) {
                    debugLog('Season bootstrap script fetch failed', {
                        source: 'bootstrap-script',
                        status: response.status,
                        targetTimeframe: 'season'
                    });
                    return;
                }
                const seasons = parseActiveSeasonsFromBootstrapScript(response.responseText);
                const activeSeason = pickActiveSeason(seasons || []);
                if (!activeSeason) {
                    debugLog('Season bootstrap script parse returned no seasons', {
                        source: 'bootstrap-script',
                        targetTimeframe: 'season'
                    });
                    return;
                }
                if (!applySeasonFromRecord(activeSeason, 'bootstrap-script')) {
                    return;
                }
                if (location.pathname === LEADERBOARD_PATH && state.timeframe === 'season') {
                    fetchLeaderboardData(true);
                } else {
                    startBackgroundSync('season-bootstrap', {
                        timeframes: ['season'],
                        includeNav: false,
                        includeCurrent: true,
                        force: true
                    });
                }
            },
            onerror: function() {
                seasonBootstrapFetchInFlight = false;
                debugLog('Season bootstrap script fetch transport error', {
                    source: 'bootstrap-script',
                    targetTimeframe: 'season'
                });
            }
        });
    }
    // Load season data from NTBOOTSTRAP, cache, or bootstrap script fallback
    function loadSeasonData() {
        // 1. Check localStorage cache first
        try {
            const cached = localStorage.getItem(SEASON_CACHE_KEY);
            if (cached) {
                const seasonCache = JSON.parse(cached);
                const shouldRefresh = shouldRefreshSeasonCache(seasonCache.endStampUTC);
                debugLog('Season cache check', {
                    source: 'localStorage',
                    seasonName: seasonCache.name,
                    endStampUTC: seasonCache.endStampUTC,
                    shouldRefresh: shouldRefresh,
                    targetTimeframe: 'season'
                });
                if (!shouldRefresh) {
                    currentSeason = seasonCache;
                    debugLog('Season cache hit (fresh)', {
                        source: 'localStorage',
                        seasonName: currentSeason.name,
                        targetTimeframe: 'season'
                    });
                    debugLog('Season source selected', {
                        source: 'localStorage-fresh',
                        seasonName: currentSeason.name,
                        targetTimeframe: 'season'
                    });
                    return true;
                }
            }
        } catch (e) {
            console.warn('[Startrack] Error reading season cache:', e);
            debugLog('Season cache read error', {
                message: e && e.message ? e.message : String(e),
                targetTimeframe: 'season'
            });
        }
        // 2. Parse NTBOOTSTRAP for ACTIVE_SEASONS
        if (typeof NTBOOTSTRAP === 'function') {
            try {
                const bootstrapData = NTBOOTSTRAP();
                const seasons = parseActiveSeasonsFromBootstrapPayload(bootstrapData);
                const activeSeason = pickActiveSeason(seasons || []);
                if (activeSeason && applySeasonFromRecord(activeSeason, 'NTBOOTSTRAP')) {
                    return true;
                }
            } catch (e) {
                console.warn('[Startrack] Error parsing NTBOOTSTRAP for seasons:', e);
                debugLog('Season bootstrap parse error', {
                    message: e && e.message ? e.message : String(e),
                    targetTimeframe: 'season'
                });
            }
        }
        // 3. Fallback: use expired cache if available while we request fresh season metadata
        try {
            const cached = localStorage.getItem(SEASON_CACHE_KEY);
            if (cached) {
                currentSeason = JSON.parse(cached);
                debugLog('Season cache hit (expired fallback)', {
                    source: 'localStorage',
                    seasonName: currentSeason.name,
                    targetTimeframe: 'season'
                });
                debugLog('Season source selected', {
                    source: 'localStorage-expired-fallback',
                    seasonName: currentSeason.name,
                    targetTimeframe: 'season'
                });
                fetchSeasonDataFromBootstrapScript();
                return hasValidSeasonData();
            }
        } catch (e) {}
        // 4. No season metadata available yet, fetch asynchronously from bootstrap script
        fetchSeasonDataFromBootstrapScript();
        console.warn('[Startrack] Season metadata not available yet; skipping season fetch until bootstrap data is loaded');
        debugLog('Season source selected', {
            source: 'pending-bootstrap-script',
            seasonName: currentSeason.name || 'Season',
            targetTimeframe: 'season'
        });
        return hasValidSeasonData();
    }
    // Format season dates for display (user's local timezone)
    function getSeasonDisplayDates() {
        if (!currentSeason.startStampUTC || !currentSeason.endStampUTC) {
            return { startDisplay: 'Unknown', endDisplay: 'Unknown' };
        }
        const startLocal = utcToLocalDate(currentSeason.startStampUTC);
        const endLocal = utcToLocalDate(currentSeason.endStampUTC);
        const options = { month: 'short', day: 'numeric', year: 'numeric' };
        return {
            startDisplay: startLocal.toLocaleDateString(undefined, options),
            endDisplay: endLocal.toLocaleDateString(undefined, options)
        };
    }
    // =================================================================
    // 4. POSITION CHANGE HELPER
    // =================================================================
    function getPositionChangeHTML(change) {
        if (!isNtcfgLeaderboardsFeatureEnabled('HIGHLIGHT_POSITION_CHANGE')) return '';
        const parsedChange = parseInt(change, 10);
        if (isNaN(parsedChange) || parsedChange === 0) {
            return `<div class="rank-change rank-change--none"><svg class="icon icon-arrow-up"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/dist/site/images/icons/icons.css.svg#icon-arrow-up"></use></svg><div>-</div><svg class="icon icon-arrow-down"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/dist/site/images/icons/icons.css.svg#icon-arrow-down"></use></svg></div>`;
        }
        if (parsedChange > 0) {
            return `<div class="rank-change rank-change--up"><svg class="icon icon-arrow-up"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/dist/site/images/icons/icons.css.svg#icon-arrow-up"></use></svg><div>${parsedChange}</div><svg class="icon icon-arrow-down"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/dist/site/images/icons/icons.css.svg#icon-arrow-down"></use></svg></div>`;
        } else {
            return `<div class="rank-change rank-change--down"><svg class="icon icon-arrow-up"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/dist/site/images/icons/icons.css.svg#icon-arrow-up"></use></svg><div>${Math.abs(parsedChange)}</div><svg class="icon icon-arrow-down"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/dist/site/images/icons/icons.css.svg#icon-arrow-down"></use></svg></div>`;
        }
    }
    // =================================================================
    // 5. CAR DATA LOADING (DEFERRED WITH RETRY)
    // =================================================================
    function loadCarData(callback) {
        // Check cache timestamp first
        const cacheTimestamp = localStorage.getItem('ntCarDataMapTimestamp');
        const cacheAge = cacheTimestamp ? Date.now() - parseInt(cacheTimestamp) : Infinity;
        const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1000;
        // Try loading from cache if it's less than 7 days old
        if (cacheAge < SEVEN_DAYS) {
            const cached = localStorage.getItem('ntCarDataMap');
            if (cached) {
                try {
                    carDataMap = JSON.parse(cached);
                    if (Object.keys(carDataMap).length > 0) {
                        carDataLoaded = true;
                        debugLog('Car data loaded from cache', {
                            count: Object.keys(carDataMap).length
                        });
                        if (callback) callback(true);
                        return;
                    }
                } catch (e) {
                    console.warn('[Startrack] Failed to parse cached car data');
                }
            }
        }
        // Try to load from NTBOOTSTRAP
        if (typeof NTBOOTSTRAP === 'function') {
            try {
                const bootstrapData = NTBOOTSTRAP();
                const carsData = bootstrapData.find(item => item[0] === 'CARS');
                if (carsData && carsData[1] && carsData[1].length > 0) {
                    carsData[1].forEach(car => {
                        if (car.carID && car.options && car.options.smallSrc) {
                            carDataMap[car.carID] = car.options.smallSrc;
                        }
                    });
                    if (Object.keys(carDataMap).length > 0) {
                        localStorage.setItem('ntCarDataMap', JSON.stringify(carDataMap));
                        localStorage.setItem('ntCarDataMapTimestamp', Date.now().toString());
                        carDataLoaded = true;
                        debugLog('Car data loaded from NTBOOTSTRAP', {
                            count: Object.keys(carDataMap).length
                        });
                        if (callback) callback(true);
                        return;
                    }
                }
            } catch (e) {
                console.warn('[Startrack] Error loading from NTBOOTSTRAP:', e);
            }
        }
        // NTBOOTSTRAP not available yet - retry
        carDataLoadAttempts++;
        if (carDataLoadAttempts < MAX_CAR_LOAD_ATTEMPTS) {
            debugLog('Car data retry scheduled', {
                attempt: carDataLoadAttempts,
                maxAttempts: MAX_CAR_LOAD_ATTEMPTS
            });
            setTimeout(() => loadCarData(callback), 500);
            return;
        }
        // Max attempts reached - try expired cache as fallback
        const cached = localStorage.getItem('ntCarDataMap');
        if (cached) {
            try {
                carDataMap = JSON.parse(cached);
                if (Object.keys(carDataMap).length > 0) {
                    carDataLoaded = true;
                    debugLog('Car data loaded from expired cache fallback', {
                        count: Object.keys(carDataMap).length
                    });
                    if (callback) callback(true);
                    return;
                }
            } catch (e) {}
        }
        console.error('[Startrack] Failed to load car data after all attempts');
        carDataLoaded = true; // Mark as loaded to prevent infinite retries
        if (callback) callback(false);
    }
    function getCarImage(carID, carHueAngle) {
        const smallSrc = carDataMap[carID];
        if (smallSrc) {
            // If car has a hue angle, use painted version
            if (carHueAngle !== null && carHueAngle !== undefined && carHueAngle !== 0) {
                // Format: /cars/painted/{smallSrc without .png}_{hue}.png
                const baseImage = smallSrc.replace('.png', '');
                const url = `https://www.nitrotype.com/cars/painted/${baseImage}_${carHueAngle}.png`;
                // Debug: uncomment to trace image URLs
                // console.log(`[Startrack] Car ${carID}: painted → ${url}`);
                return url;
            }
            // Unpainted car - use smallSrc directly
            // Debug: uncomment to trace image URLs
            // console.log(`[Startrack] Car ${carID}: unpainted → /cars/${smallSrc}`);
            return `https://www.nitrotype.com/cars/${smallSrc}`;
        }
        // Fallback - unpainted rental car (identifiable error state)
        debugLog('Unknown car ID fallback used', {
            carID: carID,
            loadedCars: Object.keys(carDataMap).length
        });
        return `https://www.nitrotype.com/cars/9_small_1.png`;
    }
    // =================================================================
    // 6. OTHER UTILITIES
    // =================================================================
    function startHourlyCheck() {
        lastCheckedHour = getCurrentHour();
        if (hourlyCheckInterval) clearInterval(hourlyCheckInterval);
        hourlyCheckInterval = setInterval(() => {
            const currentHour = getCurrentHour();
            if (currentHour !== lastCheckedHour && location.pathname === LEADERBOARD_PATH) {
                lastCheckedHour = currentHour;
                state.currentDate = getCurrentCT();
                if (['60min', '24hr', '7day'].includes(state.timeframe)) fetchLeaderboardData(true);
            } else if (currentHour !== lastCheckedHour) {
                lastCheckedHour = currentHour;
            }
        }, 60000);
    }
    function stopHourlyCheck() {
        if (hourlyCheckInterval) { clearInterval(hourlyCheckInterval); hourlyCheckInterval = null; }
    }
    function formatDate(date) {
        if (!date) return '';
        const d = new Date(date);
        const y = d.getFullYear();
        const m = ('0' + (d.getMonth() + 1)).slice(-2);
        const day = ('0' + d.getDate()).slice(-2);
        const h = ('0' + d.getHours()).slice(-2);
        const min = ('0' + d.getMinutes()).slice(-2);
        const s = ('0' + d.getSeconds()).slice(-2);
        return `${y}-${m}-${day} ${h}:${min}:${s}`;
    }
    function getStartOfDay(date) { const d = new Date(date); d.setHours(0, 0, 0, 0); return d; }
    function getEndOfDay(date) { const d = new Date(date); d.setHours(23, 59, 59, 999); return d; }
    function calculateDateRange(tempState) {
        let start, end;
        const current = new Date(tempState.currentDate);
        const timeframe = tempState.timeframe || state.timeframe;
        const now = getCurrentCT();
        if (timeframe === 'season') {
            // Use dynamic season data only when metadata is valid
            if (!hasValidSeasonData()) return { start: null, end: null };
            return { start: currentSeason.startCT, end: currentSeason.endCT };
        }
        else if (timeframe === '60min') { end = now; start = new Date(now.getTime() - (60 * 60 * 1000)); }
        else if (timeframe === '24hr') { end = now; start = new Date(now.getTime() - (24 * 60 * 60 * 1000)); }
        else if (timeframe === '7day') { end = now; start = new Date(now.getTime() - (7 * 24 * 60 * 60 * 1000)); }
        else if (timeframe === 'daily') { start = getStartOfDay(current); end = getEndOfDay(current); }
        else if (timeframe === 'weekly') {
            const dayOfWeek = current.getDay();
            start = getStartOfDay(current);
            start.setDate(start.getDate() - dayOfWeek);
            end = new Date(start);
            end.setDate(end.getDate() + 6);
            end = getEndOfDay(end);
        } else if (timeframe === 'monthly') {
            start = new Date(current.getFullYear(), current.getMonth(), 1);
            end = new Date(current.getFullYear(), current.getMonth() + 1, 0);
            end = getEndOfDay(end);
        } else if (timeframe === 'custom') {
            start = tempState.dateRange?.start || getStartOfDay(now);
            end = tempState.dateRange?.end || getEndOfDay(now);
        }
        return { start: formatDate(start), end: formatDate(end) };
    }
    function navigateDate(direction) {
        const current = state.currentDate;
        const date = new Date(current);
        if (state.timeframe === 'daily') date.setDate(current.getDate() + direction);
        else if (state.timeframe === 'weekly') date.setDate(current.getDate() + (7 * direction));
        else if (state.timeframe === 'monthly') date.setMonth(current.getMonth() + direction);
        state.currentDate = date;
        fetchLeaderboardData();
    }
    function setIndicator(message, isUpdating = true) {
        const indicatorEl = document.getElementById('update-indicator');
        if (indicatorEl) {
            indicatorEl.textContent = message;
            indicatorEl.style.color = isUpdating ? '#FFC107' : '#28A745';
            const refreshBtn = document.getElementById('manual-refresh-btn');
            if (refreshBtn && !isUpdating) {
                const svg = refreshBtn.querySelector('svg');
                if (svg) svg.classList.remove('icon-spin');
            }
            document.querySelectorAll('[data-timeframe]').forEach(btn => {
                btn.classList.remove('is-active', 'is-frozen');
                if (btn.dataset.timeframe === state.timeframe) btn.classList.add('is-active', 'is-frozen');
            });
            document.querySelectorAll('[data-view]').forEach(btn => {
                btn.classList.remove('is-active');
                if (btn.dataset.view === state.view) btn.classList.add('is-active');
            });
        }
    }
    // Generate a cache key for both localStorage and RAM cache
    function getCacheKey(tempState) {
        const s = tempState || state;
        const ranges = calculateDateRange(s);
        let startKey = ranges.start;
        let endKey = ranges.end;
        if (s.timeframe === '60min' || s.timeframe === '24hr' || s.timeframe === '7day') {
            const roundToHour = (dateStr) => {
                const date = new Date(dateStr.replace(' ', 'T'));
                date.setMinutes(0, 0, 0);
                return formatDate(date);
            };
            startKey = roundToHour(ranges.start);
            endKey = roundToHour(ranges.end);
        }
        return `${CACHE_KEY}${s.view}_${s.timeframe}_${startKey}_${endKey}`;
    }
    // --- HTML BUILDING ---
    function buildLeaderboardHTML() {
        const currentTF = timeframes.find(t => t.key === state.timeframe);
        const hasNav = currentTF?.hasNav || false;
        const isCustom = state.timeframe === 'custom';
        return `
            <section class="card card--b card--o card--shadow card--f card--grit well well--b well--l">
                <div class="card-cap bg--gradient">
                    <h1 class="h2 tbs">Startrack Leaderboards</h1>
                </div>
                <div class="well--p well--l_p">
                    <div class="row row--o well well--b well--l">
                        <div class="tabs tabs--a tabs--leaderboards">
                            <button class="tab" data-view="individual">
                                <div class="bucket bucket--c bucket--xs"><div class="bucket-media"><svg class="icon icon-racer"><use xlink:href="/dist/site/images/icons/icons.css.svg#icon-racer"></use></svg></div><div class="bucket-content">Top Racers</div></div>
                            </button>
                            <button class="tab" data-view="team">
                                <div class="bucket bucket--c bucket--xs"><div class="bucket-media"><svg class="icon icon-team"><use xlink:href="/dist/site/images/icons/icons.css.svg#icon-team"></use></svg></div><div class="bucket-content">Top Teams</div></div>
                            </button>
                        </div>
                        <div class="card card--d card--o card--sq card--f">
                            <div class="well--p well--pt">
                                <div class="row row--o has-btn">
                                    ${timeframes.map(tf => `<button type="button" class="btn btn--dark btn--outline btn--thin" data-timeframe="${tf.key}">${tf.label}</button>`).join('')}
                                </div>
                                <div class="divider divider--a mbf"></div>
                                <div class="seasonLeader seasonLeader--default" style="position: relative;">
                                    <div class="split split--start row">
                                        <div class="split-cell">
                                            <h1 class="seasonLeader-title" id="date-title">Loading...</h1>
                                            <p class="seasonLeader-date" id="date-range"></p>
                                        </div>
                                    </div>
                                    <div style="position: absolute; bottom: 10px; width: 100%; text-align: center; left: 0; pointer-events: none;">
                                        <div style="pointer-events: auto; display: inline-flex; align-items: center; justify-content: center;">
                                            <span id="update-indicator" class="mrxs tsm">Loading...</span>
                                            ${isNtcfgLeaderboardsFeatureEnabled('SHOW_MANUAL_REFRESH') ? `<button id="manual-refresh-btn" class="btn btn--bare" title="Manual Refresh - Syncs All Tabs" style="padding: 2px; opacity: 0.6; line-height: 0;">
                                                <svg style="width: 14px; height: 14px; fill: currentColor;" viewBox="0 0 24 24">
                                                    <path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 3.33-3.85 5.63-7.29 5.63-4.14 0-7.5-3.36-7.5-7.5s3.36-7.5 7.5-7.5c2.07 0 3.94.83 5.36 2.24L13 12h7V5l-2.35 1.35z"/>
                                                </svg>
                                            </button>` : ''}
                                        </div>
                                    </div>
                                </div>
                                ${hasNav ? `
                                    <div class="row row--o mtm">
                                        <button class="btn btn--secondary btn--thin" id="nav-prev">&lt; Previous</button>
                                        <button class="btn btn--secondary btn--thin" id="nav-today">Today</button>
                                        <button class="btn btn--secondary btn--thin" id="nav-next">Next &gt;</button>
                                    </div>
                                ` : ''}
                                ${isCustom ? `
                                    <div class="row row--o mtm">
                                        <label class="tsm tc-ts">Start:</label>
                                        <input type="date" id="start-date" class="input input--mini mlxs mrm" value="${state.dateRange.start ? state.dateRange.start.toISOString().split('T')[0] : ''}">
                                        <label class="tsm tc-ts">End:</label>
                                        <input type="date" id="end-date" class="input input--mini mlxs mrm" value="${state.dateRange.end ? state.dateRange.end.toISOString().split('T')[0] : ''}">
                                        <button class="btn btn--primary btn--thin" id="update-custom">Update</button>
                                    </div>
                                ` : ''}
                                <div id="leaderboard-table-container">
                                    <div class="tac pxl mtl">
                                        <div class="loading-spinner loading-spinner--ts" style="margin: 0 auto;"></div>
                                        <div class="mtm">Loading content...</div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </section>
        `;
    }
    // --- CACHE MGMT ---
    function cleanOldCache() {
        try {
            const keys = Object.keys(localStorage);
            const cacheKeys = keys.filter(k => k.startsWith(CACHE_KEY));
            if (cacheKeys.length > 50) {
                cacheKeys
                    .map((key) => ({
                        key,
                        timestamp: parseInt(localStorage.getItem(CACHE_TIMESTAMP_KEY + key) || '0', 10) || 0
                    }))
                    .sort((a, b) => a.timestamp - b.timestamp)
                    .slice(0, cacheKeys.length - 50)
                    .forEach(({ key }) => {
                        localStorage.removeItem(key);
                        localStorage.removeItem(CACHE_TIMESTAMP_KEY + key);
                    });
            }
            // Remove orphaned timestamp keys
            keys.filter(k => k.startsWith(CACHE_TIMESTAMP_KEY)).forEach(tsKey => {
                const baseKey = tsKey.slice(CACHE_TIMESTAMP_KEY.length);
                if (!localStorage.getItem(baseKey)) localStorage.removeItem(tsKey);
            });
        } catch (e) {}
    }
    function isCacheFresh(cacheKey, ttlMs = getCacheDurationMs()) {
        const timestampKey = CACHE_TIMESTAMP_KEY + cacheKey;
        const timestamp = localStorage.getItem(timestampKey);
        if (!timestamp) return false;
        return (Date.now() - parseInt(timestamp)) < ttlMs;
    }
    function saveToCache(cacheKey, data) {
        try {
            localStorage.setItem(cacheKey, data);
            const savedAt = Date.now();
            localStorage.setItem(CACHE_TIMESTAMP_KEY + cacheKey, savedAt.toString());
            debugLog('Cache write success', {
                key: shortCacheKey(cacheKey),
                size: typeof data === 'string' ? data.length : null
            });
        } catch (quotaError) {
            cleanOldCache();
            try {
                localStorage.setItem(cacheKey, data);
                localStorage.setItem(CACHE_TIMESTAMP_KEY + cacheKey, Date.now().toString());
                debugLog('Cache write success after cleanup', {
                    key: shortCacheKey(cacheKey),
                    size: typeof data === 'string' ? data.length : null
                });
            } catch (e2) {}
        }
    }
    // --- RENDER TABLE ---
    function renderTable(data, specificTime = null) {
        const container = document.getElementById('leaderboard-table-container');
        if (!container) return;
        if (!data || data.length === 0) {
            container.innerHTML = '<div class="tac pxl tsm tc-ts">No data available.</div>';
            setIndicator('Updated', false);
            return;
        }
        const top100 = data.slice(0, 100);
        const isIndividual = state.view === 'individual';
        const hasPositionData = top100.some(item => item.position_change !== null && item.position_change !== undefined);
        let html = '<table class="table table--selectable table--striped table--fixed table--leaderboard">';
        html += '<thead class="table-head"><tr class="table-row">';
        const rankChangeHeader = hasPositionData ? '<th scope="col" class="table-cell table-cell--rank-change"></th>' : '';
        html += isIndividual
            ? `${rankChangeHeader}<th scope="col" class="table-cell table-cell--place"></th><th scope="col" class="table-cell table-cell--racer">Racer</th><th scope="col" class="table-cell table-cell--speed">WPM</th><th scope="col" class="table-cell table-cell--races">Accuracy</th><th scope="col" class="table-cell table-cell--races">Races</th><th scope="col" class="table-cell table-cell--points">Points</th>`
            : `${rankChangeHeader}<th scope="col" class="table-cell table-cell--place"></th><th scope="col" class="table-cell table-cell--tag">Tag</th><th scope="col" class="table-cell table-cell--team">Team</th><th scope="col" class="table-cell table-cell--speed">WPM</th><th scope="col" class="table-cell table-cell--races">Accuracy</th><th scope="col" class="table-cell table-cell--races">Races</th><th scope="col" class="table-cell table-cell--points">Points</th>`;
        html += '</tr></thead><tbody class="table-body">';
        top100.forEach((item, index) => {
            const rank = index + 1;
            let rowClass = 'table-row';
            const posChangeHTML = getPositionChangeHTML(item.position_change);
            const rankChangeCellHtml = hasPositionData ? `<td class="table-cell tac table-cell--rank-change">${posChangeHTML}</td>` : '';
            // Rank visual HTML
            let medalHTML = `<div class="mhc"><span class="h3 tc-ts">${rank}</span></div>`;
            if (rank === 1) {
                rowClass = 'table-row table-row--gold';
                medalHTML = `<div class="mhc"><img class="db" src="/dist/site/images/medals/gold-sm.png"></div>`;
            } else if (rank === 2) {
                rowClass = 'table-row table-row--silver';
                medalHTML = `<div class="mhc"><img class="db" src="/dist/site/images/medals/silver-sm.png"></div>`;
            } else if (rank === 3) {
                rowClass = 'table-row table-row--bronze';
                medalHTML = `<div class="mhc"><img class="db" src="/dist/site/images/medals/bronze-sm.png"></div>`;
            }
            const wpm = parseFloat(item.WPM).toFixed(1);
            const acc = (parseFloat(item.Accuracy) * 100).toFixed(2);
            const points = Math.round(parseFloat(item.Points)).toLocaleString();
            if (isIndividual) {
                html += `<tr class="${rowClass}" data-username="${item.Username || ''}" style="cursor: pointer;">${rankChangeCellHtml}<td class="table-cell table-cell--place tac">${medalHTML}</td>`;
                const teamTag = item.TeamTag || '--';
                const displayName = item.CurrentDisplayName || item.Username;
                const tagColor = item.tagColor || 'fff';
                const isGold = item.membership === 'gold';
                const carImage = getCarImage(item.carID || 1, item.carHueAngle);
                const title = item.title || 'Untitled';
                html += `
                    <td class="table-cell table-cell--racer">
                        <div class="bucket bucket--s bucket--c">
                            <div class="bucket-media bucket-media--w90"><img class="db" src="${carImage}"></div>
                            <div class="bucket-content">
                                <div class="df df--align-center">
                                    ${isGold ? '<div class="prxxs"><img alt="NT Gold" class="icon icon-nt-gold-s" src="https://www.nitrotype.com/dist/site/images/themes/profiles/gold/nt-gold-icon.png"></div>' : ''}
                                    <div class="prxs df df--align-center" title="${displayName}">
                                        <a href="https://www.nitrotype.com/team/${teamTag}" class="link link--bare mrxxs twb" style="color: #${tagColor};">[${teamTag}]</a>
                                        <span class="type-ellip ${isGold ? 'type-gold' : ''} tss">${displayName}</span>
                                    </div>
                                </div>
                                <div class="tsxs tc-fuel tsi db">"${title}"</div>
                            </div>
                        </div>
                    </td>
                    <td class="table-cell table-cell--speed">${wpm}</td>
                    <td class="table-cell table-cell--races">${acc}%</td>
                    <td class="table-cell table-cell--races">${item.Races}</td>
                    <td class="table-cell table-cell--points">${points}</td>
                `;
            } else {
                const teamTag = item.TeamTag || '----';
                const teamName = item.TeamName || `${teamTag} Team`;
                const tagColor = item.tagColor || 'B3C8DD';
                html += `<tr class="${rowClass}" data-teamtag="${item.TeamTag || ''}" style="cursor: pointer;">${rankChangeCellHtml}<td class="table-cell table-cell--place tac">${medalHTML}</td>`;
                html += `
                    <td class="table-cell table-cell--tag"><span class="twb" style="color: #${tagColor};">[${teamTag}]</span></td>
                    <td class="table-cell table-cell--team"><span class="tc-lemon">"${teamName}"</span></td>
                    <td class="table-cell table-cell--speed">${wpm}</td>
                    <td class="table-cell table-cell--races">${acc}%</td>
                    <td class="table-cell table-cell--races">${item.Races}</td>
                    <td class="table-cell table-cell--points">${points}</td>
                `;
            }
            html += '</tr>';
        });
        html += '</tbody></table>';
        container.innerHTML = html;
        if (isIndividual) {
            document.querySelectorAll('.table-row[data-username]').forEach(row => {
                row.addEventListener('click', (e) => {
                    if (!e.target.closest('a[href*="/team/"]')) window.location.href = `https://www.nitrotype.com/racer/${row.dataset.username}`;
                });
            });
        } else {
            document.querySelectorAll('.table-row[data-teamtag]').forEach(row => {
                row.addEventListener('click', () => window.location.href = `https://www.nitrotype.com/team/${row.dataset.teamtag}`);
            });
        }
        const cacheKey = getCacheKey();
        let timeToDisplay = specificTime || localStorage.getItem(CACHE_TIMESTAMP_KEY + cacheKey);
        if (!timeToDisplay) timeToDisplay = Date.now().toString();
        const updateTime = new Date(parseInt(timeToDisplay));
        setIndicator(`Last updated: ${updateTime.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}`, false);
        if (!initialCacheComplete && !isCaching) {
            initialCacheComplete = true;
            isCaching = true;
            setTimeout(() => { populateCacheQueue(); cacheAllViews(); }, 1000);
        }
    }
    function updateDateDisplay() {
        const titleEl = document.getElementById('date-title');
        const rangeEl = document.getElementById('date-range');
        if (!titleEl || !rangeEl) return;
        const ranges = calculateDateRange(state);
        const hasRange = !!(ranges && ranges.start && ranges.end);
        const start = hasRange ? new Date(ranges.start.replace(' ', 'T')) : null;
        const end = hasRange ? new Date(ranges.end.replace(' ', 'T')) : null;
        if (state.timeframe === 'season') {
            // Use dynamic season name and dates
            titleEl.textContent = currentSeason.name || 'Season';
            const seasonDates = getSeasonDisplayDates();
            rangeEl.textContent = `${seasonDates.startDisplay} - ${seasonDates.endDisplay}`;
        }
        else if (state.timeframe === 'daily') { titleEl.textContent = 'Daily'; rangeEl.textContent = start ? start.toLocaleDateString() : 'Loading...'; }
        else if (state.timeframe === 'weekly') { titleEl.textContent = 'Weekly'; rangeEl.textContent = (start && end) ? `${start.toLocaleDateString()} - ${end.toLocaleDateString()}` : 'Loading...'; }
        else if (state.timeframe === 'monthly') { titleEl.textContent = 'Monthly'; rangeEl.textContent = start ? start.toLocaleDateString(undefined, { month: 'long', year: 'numeric' }) : 'Loading...'; }
        else if (state.timeframe === 'custom') { titleEl.textContent = 'Custom Range'; rangeEl.textContent = (start && end) ? `${start.toLocaleDateString()} - ${end.toLocaleDateString()}` : 'Loading...'; }
        else { titleEl.textContent = timeframes.find(t => t.key === state.timeframe)?.label || 'Leaderboards'; rangeEl.textContent = ''; }
    }
    function populateCacheQueue(options = {}) {
        cacheQueue = [];
        const views = options.views || ['individual', 'team'];
        const currentCT = getCurrentCT();
        const now = getCurrentCT();
        now.setMinutes(0, 0, 0);
        const timeframesFilter = Array.isArray(options.timeframes) ? new Set(options.timeframes) : null;
        const includeNav = options.includeNav !== false;
        const includeCurrent = options.includeCurrent === true ||
            (options.includeCurrent === undefined && location.pathname !== LEADERBOARD_PATH);
        const shouldInclude = (key) => !timeframesFilter || timeframesFilter.has(key);
        const priorityTimeframes = ['season', '24hr', '60min', '7day'];
        const prioritySet = new Set(priorityTimeframes);
        // Always fetch Season first: individual -> team
        if (shouldInclude('season')) {
            if (hasValidSeasonData()) {
                cacheQueue.push({ view: 'individual', timeframe: 'season', currentDate: now });
                cacheQueue.push({ view: 'team', timeframe: 'season', currentDate: now });
            } else {
                debugLog('Season queue skipped (metadata unavailable)', {
                    reason: options.reason || 'unknown',
                    targetTimeframe: 'season'
                });
            }
        }
        timeframes
            .filter(t => prioritySet.has(t.key) && t.key !== 'season' && shouldInclude(t.key))
            .forEach(tf => {
                views.forEach(view => cacheQueue.push({ view: view, timeframe: tf.key, currentDate: now }));
            });
        if (includeNav) {
            const dynamicTFs = timeframes.filter(t => t.hasNav && shouldInclude(t.key));
            views.forEach(view => {
                dynamicTFs.forEach(tf => {
                    let date = new Date(currentCT.getFullYear(), currentCT.getMonth(), currentCT.getDate());
                    cacheQueue.push({ view: view, timeframe: tf.key, currentDate: date });
                });
            });
        }
        if (!includeCurrent) {
            const currentKey = getCacheKey();
            cacheQueue = cacheQueue.filter(item => getCacheKey(item) !== currentKey);
        }
        debugLog('Cache queue prepared', {
            reason: options.reason || 'unknown',
            includeNav: includeNav,
            includeCurrent: includeCurrent,
            force: options.force === true,
            count: cacheQueue.length,
            order: cacheQueue.map(item => `${item.view}:${item.timeframe}`),
            targetTimeframe: cacheQueue[0]?.timeframe || null
        });
    }
    function cacheAllViews() {
        try {
            if (cacheQueue.length === 0) {
                debugLog('Cache queue drained', { reason: 'complete' });
                isCaching = false;
                forceBackgroundUpdate = false;
                return;
            }
            const nextItem = cacheQueue.shift();
            const nextKey = getCacheKey(nextItem);
            const ttl = getCacheTTLForTimeframe(nextItem.timeframe);
            const scheduleNext = () => setTimeout(cacheAllViews, BACKGROUND_SYNC_DELAY_MS);
            const freshness = getCacheFreshness(nextKey, ttl);
            if (!forceBackgroundUpdate && localStorage.getItem(nextKey) && isCacheFresh(nextKey, ttl)) {
                debugLog('Cache queue item skipped (fresh cache)', {
                    view: nextItem.view,
                    timeframe: nextItem.timeframe,
                    key: shortCacheKey(nextKey),
                    ageMs: freshness.ageMs,
                    ttlMs: ttl,
                    queueRemaining: cacheQueue.length,
                    targetTimeframe: nextItem.timeframe
                });
                scheduleNext();
                return;
            }
            debugLog('Cache queue item fetching', {
                view: nextItem.view,
                timeframe: nextItem.timeframe,
                key: shortCacheKey(nextKey),
                forceBackgroundUpdate: forceBackgroundUpdate,
                ageMs: freshness.ageMs,
                ttlMs: ttl,
                queueRemaining: cacheQueue.length,
                targetTimeframe: nextItem.timeframe
            });
            fetchFreshData(nextKey, nextItem.view, nextItem.timeframe, nextItem.currentDate, scheduleNext);
        } catch (error) {
            console.error('Error in cache queue:', error);
            debugLog('Cache queue error', { message: error && error.message ? error.message : String(error) });
            isCaching = false;
            forceBackgroundUpdate = false;
        }
    }
    function startBackgroundSync(reason, options = {}) {
        try {
            loadSeasonData();
            if (isCaching) {
                debugLog('Background sync skipped (already running)', { reason: reason });
                return false;
            }
            forceBackgroundUpdate = options.force === true;
            populateCacheQueue({ ...options, reason: reason });
            isCaching = true;
            cacheAllViews();
            debugLog('Background sync started', {
                reason: reason,
                force: forceBackgroundUpdate,
                options: options
            });
            return true;
        } catch (error) {
            console.error('[Startrack] Background sync failed to start:', error);
            debugLog('Background sync failed', {
                reason: reason,
                message: error && error.message ? error.message : String(error)
            });
            return false;
        }
    }
    function maybeStartDailyBackgroundSync() {
        try {
            const todayKey = getCTDateKey();
            const lastKey = localStorage.getItem(DAILY_SYNC_KEY);
            if (lastKey === todayKey) {
                debugLog('Daily sync skipped (already ran for CT day)', {
                    todayKey: todayKey,
                    lastKey: lastKey,
                    targetTimeframe: 'season'
                });
                return;
            }
            if (startBackgroundSync('daily', { includeCurrent: true })) {
                localStorage.setItem(DAILY_SYNC_KEY, todayKey);
                debugLog('Daily sync marker updated', {
                    todayKey: todayKey,
                    targetTimeframe: 'season'
                });
            }
        } catch (error) {
            console.warn('[Startrack] Daily sync check failed:', error);
            debugLog('Daily sync error', {
                message: error && error.message ? error.message : String(error),
                targetTimeframe: 'season'
            });
        }
    }
    function maybeStartHourlyBackgroundSync() {
        try {
            const hourKey = getCTHourKey();
            const lastKey = localStorage.getItem(HOURLY_SYNC_KEY);
            if (lastKey === hourKey) {
                debugLog('Hourly sync skipped (already ran for CT hour)', {
                    hourKey: hourKey,
                    lastKey: lastKey,
                    targetTimeframe: 'season'
                });
                return;
            }
            const started = startBackgroundSync('hourly', {
                timeframes: ['season', '60min', '24hr', '7day'],
                includeNav: false,
                includeCurrent: true,
                force: true
            });
            if (started) {
                localStorage.setItem(HOURLY_SYNC_KEY, hourKey);
                debugLog('Hourly sync marker updated', {
                    hourKey: hourKey,
                    targetTimeframe: 'season'
                });
            }
        } catch (error) {
            console.warn('[Startrack] Hourly sync check failed:', error);
            debugLog('Hourly sync error', {
                message: error && error.message ? error.message : String(error),
                targetTimeframe: 'season'
            });
        }
    }
    function startGlobalCacheMaintenance() {
        if (window.__ntGlobalCacheInterval) return;
        window.__ntGlobalCacheInterval = setInterval(() => {
            maybeStartDailyBackgroundSync();
            maybeStartHourlyBackgroundSync();
        }, 60000);
    }
    function stopGlobalCacheMaintenance() {
        if (!window.__ntGlobalCacheInterval) return;
        clearInterval(window.__ntGlobalCacheInterval);
        window.__ntGlobalCacheInterval = null;
    }
    function syncBackgroundWorkToRoute() {
        maybeStartDailyBackgroundSync();
        maybeStartHourlyBackgroundSync();
        startGlobalCacheMaintenance();
    }
    function initDebugAPI() {
        const debugApi = {
            enable: function() {
                localStorage.setItem(DEBUG_FLAG_KEY, '1');
                console.log('[Startrack][DBG] Enabled. Reload the page to start logging.');
            },
            disable: function() {
                localStorage.removeItem(DEBUG_FLAG_KEY);
                localStorage.removeItem(DEBUG_SEASON_ONLY_KEY);
                console.log('[Startrack][DBG] Disabled. Reload the page to stop logging.');
            },
            seasonOnly: function(enabled = true) {
                if (enabled) localStorage.setItem(DEBUG_SEASON_ONLY_KEY, '1');
                else localStorage.removeItem(DEBUG_SEASON_ONLY_KEY);
                console.log(`[Startrack][DBG] seasonOnly=${enabled ? 'ON' : 'OFF'}. Reload the page to apply.`);
            },
            status: function() {
                const status = {
                    debugEnabled: DEBUG_ENABLED,
                    debugSeasonOnly: DEBUG_SEASON_ONLY,
                    debugParamEnabled: DEBUG_PARAM_ENABLED,
                    currentState: { view: state.view, timeframe: state.timeframe },
                    ctDateKey: getCTDateKey(),
                    ctHourKey: getCTHourKey(),
                    lastDailySyncKey: localStorage.getItem(DAILY_SYNC_KEY),
                    lastHourlySyncKey: localStorage.getItem(HOURLY_SYNC_KEY),
                    seasonName: currentSeason.name,
                    seasonStartUTC: currentSeason.startStampUTC,
                    seasonEndUTC: currentSeason.endStampUTC
                };
                console.log('[Startrack][DBG] Status', status);
                return status;
            },
            inspect: function(view = 'individual', timeframe = 'season') {
                const tempState = { view: view, timeframe: timeframe, currentDate: getCurrentCT() };
                const key = getCacheKey(tempState);
                const ttl = getCacheTTLForTimeframe(timeframe);
                const freshness = getCacheFreshness(key, ttl);
                const result = {
                    view: view,
                    timeframe: timeframe,
                    key: shortCacheKey(key),
                    hasData: !!localStorage.getItem(key),
                    hasTimestamp: freshness.hasTimestamp,
                    ageMs: freshness.ageMs,
                    ttlMs: freshness.ttlMs,
                    fresh: freshness.fresh
                };
                console.log('[Startrack][DBG] Inspect', result);
                return result;
            }
        };
        window.NTStartrackDebug = debugApi;
        if (typeof unsafeWindow !== 'undefined' && unsafeWindow) {
            unsafeWindow.NTStartrackDebug = debugApi;
        }
        if (DEBUG_ENABLED) {
            console.log(`[Startrack][DBG] Enabled${DEBUG_SEASON_ONLY ? ' (season-only)' : ''}. API: window.NTStartrackDebug.status(), .inspect(), .seasonOnly(), .disable()`);
        }
    }
    function fetchLeaderboardData(forceRefresh = false) {
        if (state.timeframe === 'season' && !hasValidSeasonData()) {
            loadSeasonData();
            debugLog('Leaderboard fetch deferred (season metadata unavailable)', {
                view: state.view,
                timeframe: state.timeframe,
                forceRefresh: forceRefresh,
                stateTimeframe: state.timeframe
            });
            updateDateDisplay();
            setIndicator('Season metadata loading...', true);
            const container = document.getElementById('leaderboard-table-container');
            if (container) {
                container.innerHTML = `<div class="tac pxl mtl"><div class="loading-spinner loading-spinner--ts" style="margin: 0 auto;"></div><div class="mtm">Loading season metadata...</div></div>`;
            }
            return;
        }
        const cacheKey = getCacheKey();
        const ttl = getCacheTTLForTimeframe(state.timeframe);
        const freshness = getCacheFreshness(cacheKey, ttl);
        debugLog('Leaderboard fetch start', {
            view: state.view,
            timeframe: state.timeframe,
            forceRefresh: forceRefresh,
            key: shortCacheKey(cacheKey),
            hasLocalStorage: !!localStorage.getItem(cacheKey),
            hasTimestamp: freshness.hasTimestamp,
            ageMs: freshness.ageMs,
            ttlMs: ttl,
            freshByTimestamp: freshness.fresh,
            stateTimeframe: state.timeframe
        });
        // Check RAM cache first (now properly keyed)
        if (window.NTShared && window.NTShared.getCache && !forceRefresh) {
            const sharedData = window.NTShared.getCache(cacheKey);
            if (sharedData) {
                debugLog('Leaderboard served from RAM cache', {
                    view: state.view,
                    timeframe: state.timeframe,
                    key: shortCacheKey(cacheKey),
                    count: Array.isArray(sharedData) ? sharedData.length : null,
                    stateTimeframe: state.timeframe
                });
                updateDateDisplay();
                const sharedTS = window.NTShared.getTimestamp(cacheKey);
                renderTable(sharedData, sharedTS);
                return;
            }
        }
        // Check localStorage cache
        const cachedData = localStorage.getItem(cacheKey);
        updateDateDisplay();
        if (cachedData) {
            try {
                const data = JSON.parse(cachedData);
                renderTable(data);
                if (isCacheFresh(cacheKey, ttl) && !forceRefresh) {
                    debugLog('Leaderboard served from localStorage cache', {
                        view: state.view,
                        timeframe: state.timeframe,
                        key: shortCacheKey(cacheKey),
                        count: Array.isArray(data) ? data.length : null,
                        ageMs: freshness.ageMs,
                        ttlMs: ttl,
                        stateTimeframe: state.timeframe
                    });
                    return;
                }
                debugLog('Leaderboard localStorage cache stale, revalidating', {
                    view: state.view,
                    timeframe: state.timeframe,
                    key: shortCacheKey(cacheKey),
                    ageMs: freshness.ageMs,
                    ttlMs: ttl,
                    stateTimeframe: state.timeframe
                });
                setIndicator('Updating...', true);
                fetchFreshData(cacheKey);
                return;
            }
            catch (e) {
                debugLog('Leaderboard cache parse failed', {
                    key: shortCacheKey(cacheKey),
                    message: e && e.message ? e.message : String(e),
                    stateTimeframe: state.timeframe
                });
                localStorage.removeItem(cacheKey);
            }
        }
        debugLog('Leaderboard cache miss, fetching network', {
            view: state.view,
            timeframe: state.timeframe,
            key: shortCacheKey(cacheKey),
            stateTimeframe: state.timeframe
        });
        const container = document.getElementById('leaderboard-table-container');
        if (container) container.innerHTML = `<div class="tac pxl mtl"><div class="loading-spinner loading-spinner--ts" style="margin: 0 auto;"></div><div class="mtm">Loading data...</div></div>`;
        setIndicator('Updating...', true);
        fetchFreshData(cacheKey);
    }
    function fetchFreshData(cacheKey, view = state.view, timeframe = state.timeframe, currentDate = state.currentDate, callback) {
        const tempState = { view, timeframe, currentDate };
        const requestStartedAt = Date.now();
        let ranges = calculateDateRange(tempState);
        if (!ranges || !ranges.start || !ranges.end) {
            debugLog('Network fetch skipped (missing date range)', {
                view: view,
                timeframe: timeframe,
                key: shortCacheKey(cacheKey),
                stateTimeframe: state.timeframe
            });
            if (view === state.view && timeframe === state.timeframe) {
                setIndicator('Season metadata loading...', true);
            }
            if (callback) callback();
            return;
        }
        if (['60min', '24hr', '7day'].includes(timeframe)) {
            const roundToHour = (d) => { const date = new Date(d.replace(' ', 'T')); date.setMinutes(0, 0, 0); return formatDate(date); };
            ranges = { start: roundToHour(ranges.start), end: roundToHour(ranges.end) };
        }
        const apiUrl = view === 'individual' ? 'https://ntstartrack.org/api/individual-leaderboard' : 'https://ntstartrack.org/api/team-leaderboard';
        const url = `${apiUrl}?start_time=${encodeURIComponent(ranges.start)}&end_time=${encodeURIComponent(ranges.end)}&showbot=FALSE&include_position_change=true&cb=${new Date().getTime()}`;
        debugLog('Network fetch start', {
            view: view,
            timeframe: timeframe,
            key: shortCacheKey(cacheKey),
            start: ranges.start,
            end: ranges.end,
            url: url,
            stateTimeframe: state.timeframe
        });
        if (view === state.view && timeframe === state.timeframe) setIndicator('Updating...', true);
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: function(response) {
                if (response.status === 200) {
                    setTimeout(() => {
                        try {
                            if (!response.responseText || response.responseText.trim().length === 0) throw new Error('Empty');
                            let data = JSON.parse(response.responseText);
                            if (!Array.isArray(data)) throw new Error('Invalid format');
                            data = sanitizeLeaderboardRows(view, data);
                            const now = Date.now();
                            const ttl = getCacheTTLForTimeframe(timeframe);
                            const legacyLimit = view === 'individual' ? 300 : 50;
                            const legacyData = data.slice(0, legacyLimit);
                            const top100 = data.slice(0, 100);
                            saveToCache(cacheKey, JSON.stringify(top100));
                            // Save to RAM cache with proper key
                            if (window.NTShared && window.NTShared.setCache) {
                                window.NTShared.setCache(cacheKey, top100, now + ttl);
                            }
                            // Provide legacy 7-day cache for badge scripts
                            if (timeframe === '7day' && window.NTShared && window.NTShared.setLegacyCache) {
                                window.NTShared.setLegacyCache(view, legacyData, now + ttl);
                            }
                            if (timeframe === '7day') {
                                const sharedCacheKey = getSharedCacheKey(tempState);
                                const sharedPayload = buildSharedLeaderboardPayload(view, timeframe, legacyData, now, now + ttl);
                                if (window.NTShared && window.NTShared.setCache) {
                                    window.NTShared.setCache(sharedCacheKey, sharedPayload, sharedPayload.expiresAt);
                                }
                                saveSharedPayloadToLocalStorage(sharedCacheKey, sharedPayload);
                            }
                            debugLog('Network fetch success', {
                                view: view,
                                timeframe: timeframe,
                                key: shortCacheKey(cacheKey),
                                status: response.status,
                                rows: data.length,
                                renderedRows: top100.length,
                                elapsedMs: Date.now() - requestStartedAt,
                                ttlMs: ttl,
                                stateTimeframe: state.timeframe
                            });
                            if (view === state.view && timeframe === state.timeframe) renderTable(data, now);
                            if (callback) callback();
                        } catch (e) {
                            console.error(e);
                            debugLog('Network fetch parse/render error', {
                                view: view,
                                timeframe: timeframe,
                                key: shortCacheKey(cacheKey),
                                message: e && e.message ? e.message : String(e),
                                elapsedMs: Date.now() - requestStartedAt,
                                stateTimeframe: state.timeframe
                            });
                            if (view === state.view && timeframe === state.timeframe) {
                                setIndicator(`Update failed`, false);
                                document.documentElement.classList.remove('is-leaderboard-route');
                                const main = document.querySelector('main.structure-content');
                                if (main) main.classList.remove('custom-loaded');
                            }
                            if (callback) callback();
                        }
                    }, ASYNC_DELAY);
                } else {
                    debugLog('Network fetch failed status', {
                        view: view,
                        timeframe: timeframe,
                        key: shortCacheKey(cacheKey),
                        status: response.status,
                        elapsedMs: Date.now() - requestStartedAt,
                        stateTimeframe: state.timeframe
                    });
                    if (view === state.view && timeframe === state.timeframe) {
                        setIndicator(`Update failed`, false);
                        document.documentElement.classList.remove('is-leaderboard-route');
                        const main = document.querySelector('main.structure-content');
                        if (main) main.classList.remove('custom-loaded');
                    }
                    if (callback) callback();
                }
            },
            onerror: function() {
                debugLog('Network fetch transport error', {
                    view: view,
                    timeframe: timeframe,
                    key: shortCacheKey(cacheKey),
                    elapsedMs: Date.now() - requestStartedAt,
                    stateTimeframe: state.timeframe
                });
                if (view === state.view && timeframe === state.timeframe) {
                    setIndicator('Update failed', false);
                    document.documentElement.classList.remove('is-leaderboard-route');
                    const main = document.querySelector('main.structure-content');
                    if (main) main.classList.remove('custom-loaded');
                }
                if (callback) callback();
            }
        });
    }
    function attachListeners() {
        document.querySelectorAll('[data-view]').forEach(btn => {
            btn.addEventListener('click', (e) => {
                const view = e.currentTarget.dataset.view;
                if (state.view !== view) { state.view = view; fetchLeaderboardData(false); }
            });
        });
        document.querySelectorAll('[data-timeframe]').forEach(btn => {
            btn.addEventListener('click', (e) => {
                const tf = e.currentTarget.dataset.timeframe;
                if (state.timeframe !== tf) { state.timeframe = tf; state.currentDate = getCurrentCT(); fetchLeaderboardData(false); }
            });
        });
        document.getElementById('nav-prev')?.addEventListener('click', () => navigateDate(-1));
        document.getElementById('nav-next')?.addEventListener('click', () => navigateDate(1));
        document.getElementById('nav-today')?.addEventListener('click', () => { state.currentDate = getCurrentCT(); fetchLeaderboardData(true); });
        const refreshBtn = document.getElementById('manual-refresh-btn');
        if (refreshBtn) {
            refreshBtn.addEventListener('click', (e) => {
                const icon = e.currentTarget.querySelector('svg');
                if(icon) icon.classList.add('icon-spin');
                fetchLeaderboardData(true);
                debugLog('Manual refresh requested full background sync', {
                    reason: 'manual-refresh-button'
                });
                forceBackgroundUpdate = true;
                populateCacheQueue();
                if (!isCaching) {
                    isCaching = true;
                    cacheAllViews();
                }
            });
        }
        document.getElementById('update-custom')?.addEventListener('click', () => {
            const startVal = document.getElementById('start-date')?.value;
            const endVal = document.getElementById('end-date')?.value;
            if (startVal && endVal) {
                state.dateRange.start = getStartOfDay(new Date(startVal + 'T00:00:00'));
                state.dateRange.end = getEndOfDay(new Date(endVal + 'T00:00:00'));
                fetchLeaderboardData(true);
            }
        });
    }
    function renderLeaderboardPage(forceRefresh = false) {
        if (pageRenderInProgress) return;
        pageRenderInProgress = true;
        const mainContent = document.querySelector('main.structure-content');
        if (!mainContent) {
            pageRenderInProgress = false;
            return;
        }
        try {
            // Load season data first (this can use NTBOOTSTRAP or cache)
            loadSeasonData();
            // Build HTML immediately
            mainContent.innerHTML = buildLeaderboardHTML();
            requestAnimationFrame(() => { mainContent.classList.add('custom-loaded'); });
            attachListeners();
            setActiveTab();
            setTabTitle();
            startHourlyCheck();
            // Defer car loading with retry mechanism
            loadCarData((success) => {
                if (success) {
                    debugLog('Car data ready, fetching leaderboard', { success: true });
                }
                // Fetch leaderboard data after car data is loaded (or failed)
                fetchLeaderboardData(forceRefresh);
            });
        } catch (error) {
            console.error('Error rendering leaderboard page:', error);
            document.documentElement.classList.remove('is-leaderboard-route');
            if (mainContent) mainContent.classList.remove('custom-loaded');
        } finally {
            pageRenderInProgress = false;
        }
    }
    function setActiveTab() {
        document.querySelectorAll('.nav-list-item').forEach(li => li.classList.remove('is-current'));
        const tab = document.querySelector('.' + TAB_CLASS);
        if (tab) tab.classList.add('is-current');
    }
    function setTabTitle() {
        if (window.location.pathname === LEADERBOARD_PATH) document.title = 'Leaderboards | Nitro Type';
    }
    function insertLeaderboardDropdownLink() {
        if (!isNtcfgLeaderboardsFeatureEnabled('SHOW_DROPDOWN_LINK')) return;
        // Don't duplicate
        if (document.querySelector('.ntcfg-leaderboards-dropdown-item')) return;
        const dropdown = document.querySelector('.dropdown--account .dropdown-items');
        if (!dropdown) return;
        // Insert above "My Public Profile" (icon-eye), or fall back to end of list
        const profileItem = Array.from(dropdown.querySelectorAll('.dropdown-item')).find(
            li => li.querySelector('a[href*="/racer/"]')
        );
        const li = document.createElement('li');
        li.className = 'list-item dropdown-item ntcfg-leaderboards-dropdown-item';
        if (location.pathname === LEADERBOARD_PATH) li.classList.add('is-current');
        li.innerHTML = `<a class="dropdown-link" href="${LEADERBOARD_PATH}"><svg class="icon icon-trophy mrxs" style="width:16px;height:16px;vertical-align:middle;"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/dist/site/images/icons/icons.css.svg#icon-trophy"></use></svg>Leaderboards</a>`;
        if (profileItem) {
            profileItem.before(li);
        } else {
            dropdown.appendChild(li);
        }
    }
    function removeLeaderboardDropdownLink() {
        document.querySelectorAll('.ntcfg-leaderboards-dropdown-item').forEach((el) => el.remove());
    }
    function insertLeaderboardTab() {
        if (!isNtcfgLeaderboardsFeatureEnabled('SHOW_ROUTE_TAB')) return;
        if (document.querySelector(`.${TAB_CLASS}`) || document.querySelector(`.nav-list a[href="${LEADERBOARD_PATH}"]`)) return;
        const navList = document.querySelector('.nav-list');
        if (!navList) return;
        const li = document.createElement('li');
        li.className = `nav-list-item ${TAB_CLASS}`;
        li.innerHTML = `<a href="${LEADERBOARD_PATH}" class="nav-link"><span class="has-notify">Leaderboards</span></a>`;
        const news = Array.from(navList.children).find(li => li.textContent.trim().includes('News'));
        if (news) news.before(li);
        else navList.appendChild(li);
    }
    function removeLeaderboardTab() {
        document.querySelectorAll('.' + TAB_CLASS).forEach((el) => el.remove());
    }
    function syncLeaderboardsNavigationChrome() {
        if (isNtcfgLeaderboardsFeatureEnabled('SHOW_ROUTE_TAB')) insertLeaderboardTab();
        else removeLeaderboardTab();
        if (isNtcfgLeaderboardsFeatureEnabled('SHOW_DROPDOWN_LINK')) insertLeaderboardDropdownLink();
        else removeLeaderboardDropdownLink();
    }
    function shouldRenderLeaderboardPageShell(main) {
        return !!(main && (main.children.length === 0 || main.querySelector('.error') || main.textContent.includes("Page Not Found")));
    }
    function clearLeaderboardPageRefreshTimer() {
        if (!leaderboardPageRefreshTimer) return;
        clearTimeout(leaderboardPageRefreshTimer);
        leaderboardPageRefreshTimer = null;
    }
    function scheduleLeaderboardPageRefresh(forceRefresh = false, delay = 0) {
        if (location.pathname !== LEADERBOARD_PATH) return;
        pendingLeaderboardForceRefresh = pendingLeaderboardForceRefresh || !!forceRefresh;
        clearLeaderboardPageRefreshTimer();
        leaderboardPageRefreshTimer = setTimeout(() => {
            leaderboardPageRefreshTimer = null;
            const nextForceRefresh = pendingLeaderboardForceRefresh;
            pendingLeaderboardForceRefresh = false;
            renderLeaderboardPage(nextForceRefresh);
        }, Math.max(0, Number(delay) || 0));
    }
    function ensureLeaderboardPageRendered() {
        if (location.pathname !== LEADERBOARD_PATH) return;
        if (document.getElementById('leaderboard-table-container')) {
            stopLeaderboardShellObserver();
            setActiveTab();
            return;
        }
        const main = document.querySelector('main.structure-content');
        if (shouldRenderLeaderboardPageShell(main)) {
            stopLeaderboardShellObserver();
            scheduleLeaderboardPageRefresh(false, 0);
            return;
        }
        ensureLeaderboardShellObserver();
    }
    function rerenderLeaderboardPageFromSettings() {
        if (location.pathname === LEADERBOARD_PATH) {
            scheduleLeaderboardPageRefresh(true, 0);
        }
    }
    function clearLeaderboardsPageSyncTimer() {
        if (!leaderboardsPageSyncTimer) return;
        clearTimeout(leaderboardsPageSyncTimer);
        leaderboardsPageSyncTimer = null;
    }
    function scheduleLeaderboardsPageSync({ syncRoute = false, delay = 0 } = {}) {
        pendingLeaderboardsRouteSync = pendingLeaderboardsRouteSync || !!syncRoute;
        clearLeaderboardsPageSyncTimer();
        leaderboardsPageSyncTimer = setTimeout(() => {
            leaderboardsPageSyncTimer = null;
            const shouldSyncRoute = pendingLeaderboardsRouteSync;
            pendingLeaderboardsRouteSync = false;
            if (shouldSyncRoute) {
                updateRouteStatus();
                syncBackgroundWorkToRoute();
            }
            handlePage();
        }, Math.max(0, Number(delay) || 0));
    }
    function applyLeaderboardsSettingSideEffects(settingKey) {
        if (!settingKey) return;
        if (settingKey === 'HIDE_CLASS_TAB') {
            syncHideClassTabState();
            return;
        }
        if (settingKey === 'SHOW_ROUTE_TAB') {
            if (isNtcfgLeaderboardsFeatureEnabled('SHOW_ROUTE_TAB')) insertLeaderboardTab();
            else removeLeaderboardTab();
            return;
        }
        if (settingKey === 'SHOW_DROPDOWN_LINK') {
            if (isNtcfgLeaderboardsFeatureEnabled('SHOW_DROPDOWN_LINK')) insertLeaderboardDropdownLink();
            else removeLeaderboardDropdownLink();
            return;
        }
        if (settingKey === 'SHOW_MANUAL_REFRESH' || settingKey === 'HIGHLIGHT_POSITION_CHANGE') {
            rerenderLeaderboardPageFromSettings();
        }
    }
    function applyAllLeaderboardsSettingSideEffects() {
        syncHideClassTabState();
        syncLeaderboardsNavigationChrome();
        rerenderLeaderboardPageFromSettings();
    }
    function handlePage() {
        syncLeaderboardsNavigationChrome();
        if (location.pathname === LEADERBOARD_PATH) {
            ensureLeaderboardPageRendered();
        } else {
            clearLeaderboardsPageSyncTimer();
            pendingLeaderboardsRouteSync = false;
            clearLeaderboardPageRefreshTimer();
            pendingLeaderboardForceRefresh = false;
            stopLeaderboardShellObserver();
            document.querySelector('.' + TAB_CLASS)?.classList.remove('is-current');
            stopHourlyCheck();
        }
    }
    function stopLeaderboardShellObserver() {
        if (!leaderboardShellObserver) return;
        try {
            leaderboardShellObserver.disconnect();
        } catch (e) { }
        leaderboardShellObserver = null;
    }
    function ensureLeaderboardShellObserver() {
        if (leaderboardShellObserver || location.pathname !== LEADERBOARD_PATH) return;
        leaderboardShellObserver = new MutationObserver(() => {
            const main = document.querySelector('main.structure-content');
            if (shouldRenderLeaderboardPageShell(main)) {
                stopLeaderboardShellObserver();
                scheduleLeaderboardPageRefresh(false, 0);
            }
        });
        leaderboardShellObserver.observe(document.documentElement, { childList: true, subtree: true });
    }
    function fastInject() {
        syncLeaderboardsNavigationChrome();
        if (location.pathname === LEADERBOARD_PATH) {
            ensureLeaderboardShellObserver();
        }
    }
    function startLeaderboardsRouteLifecycle() {
        ntRouteHelper.subscribe(() => {
            scheduleLeaderboardsPageSync({ syncRoute: true, delay: 0 });
        }, { immediate: false });
        scheduleLeaderboardsPageSync({ syncRoute: true, delay: 0 });
    }
    function startLeaderboardsNavigationObserver() {
        if (navigationChromeObserver) return;
        navigationChromeObserver = new MutationObserver(() => {
            scheduleLeaderboardsPageSync({ syncRoute: false, delay: 0 });
        });
        navigationChromeObserver.observe(document.documentElement, { childList: true, subtree: true });
    }
    initDebugAPI();
    fastInject();
    startLeaderboardsRouteLifecycle();
    startLeaderboardsNavigationObserver();
})();