Nitro Type - Flagged Racers

Checks Nitro Type racers for flags/bans using NT StarTrack and NTL, showing color-coded icons with custom tooltips.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Nitro Type - Flagged Racers
// @namespace    https://nitrotype.info
// @version      5.1.2
// @description  Checks Nitro Type racers for flags/bans using NT StarTrack and NTL, showing color-coded icons with custom tooltips.
// @author       Captain.Loveridge & SuperJoelzy
// @match        https://www.nitrotype.com/team/*
// @match        https://www.nitrotype.com/racer/*
// @match        https://www.nitrotype.com/leagues
// @match        https://www.nitrotype.com/friends
// @match        https://www.nitrotype.com/race
// @match        https://www.nitrotype.com/race/*
// @match        https://www.nitrotype.com/racelog*
// @match        https://www.nitrotype.com/stats*
// @match        *://*.nitrotype.com/settings/mods*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        unsafeWindow
// @connect      ntleaderboards.com
// @connect      ntstartrack.org
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const pageWindow = (typeof unsafeWindow !== 'undefined' && unsafeWindow) ? unsafeWindow : window;
    const SCRIPT_SINGLETON_KEY = '__ntBotFlagSingleton';
    if (pageWindow[SCRIPT_SINGLETON_KEY]) {
        try { console.info('[BOTFLAG] Duplicate instance detected; skipping.'); } catch (e) { }
        return;
    }
    pageWindow[SCRIPT_SINGLETON_KEY] = true;

    // ===== 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;
            // Respect CROSS_TAB_SYNC setting if the bot-flag bridge is available
            if (typeof readNtcfgBotFlagValue === 'function' && readNtcfgBotFlagValue('CROSS_TAB_SYNC') === false) 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();

    function clearSharedBotStatusCache() {
        const shared = window.NTShared;
        if (!shared || typeof shared !== 'object') return;
        if (shared.isbot && typeof shared.isbot.clear === 'function') {
            shared.isbot.clear();
        }
        const cacheObj = shared.cacheObj || shared.cache;
        if (cacheObj && cacheObj.isbot && cacheObj.isbot !== shared.isbot && typeof cacheObj.isbot.clear === 'function') {
            cacheObj.isbot.clear();
        }
    }

    function initObserverManager() {
        const existing = window.NTObserverManager || {};
        if (existing.version !== '1.0.0' && existing.observer && typeof existing.observer.disconnect === 'function') {
            try { existing.observer.disconnect(); } catch (e) { }
            existing.observer = null;
        }
        if (existing.bodyWaitObserver && typeof existing.bodyWaitObserver.disconnect === 'function') {
            try { existing.bodyWaitObserver.disconnect(); } catch (e) { }
            existing.bodyWaitObserver = null;
        }
        if (existing.debounceTimer) {
            clearTimeout(existing.debounceTimer);
            existing.debounceTimer = null;
        }
        if (existing.bodyWaitTimer) {
            clearTimeout(existing.bodyWaitTimer);
            existing.bodyWaitTimer = null;
        }
        existing.callbacks = existing.callbacks || {};
        existing.version = '1.0.0';
        existing.flushCallbacks = function() {
            Object.values(this.callbacks).forEach((cb) => {
                try { cb(); } catch (e) { console.error('[Observer Error]', e); }
            });
        };
        existing.startObserver = function() {
            if (this.observer || !document.body) return !!this.observer;
            this.observer = new MutationObserver(() => {
                clearTimeout(this.debounceTimer);
                this.debounceTimer = setTimeout(() => {
                    this.flushCallbacks();
                }, 250);
            });
            this.observer.observe(document.body, {
                childList: true,
                subtree: true
            });
            if (this.bodyWaitObserver && typeof this.bodyWaitObserver.disconnect === 'function') {
                try { this.bodyWaitObserver.disconnect(); } catch (e) { }
                this.bodyWaitObserver = null;
            }
            if (this.bodyWaitTimer) {
                clearTimeout(this.bodyWaitTimer);
                this.bodyWaitTimer = null;
            }
            setTimeout(() => {
                this.flushCallbacks();
            }, 0);
            return true;
        };
        existing.ensureObserver = function() {
            if (this.startObserver()) return;
            if (this.bodyWaitObserver) return;

            const tryStart = () => {
                if (!this.startObserver()) return false;
                if (this.bodyWaitObserver && typeof this.bodyWaitObserver.disconnect === 'function') {
                    try { this.bodyWaitObserver.disconnect(); } catch (e) { }
                    this.bodyWaitObserver = null;
                }
                if (this.bodyWaitTimer) {
                    clearTimeout(this.bodyWaitTimer);
                    this.bodyWaitTimer = null;
                }
                return true;
            };

            const root = document.documentElement || document;
            if (!root) return;

            this.bodyWaitObserver = new MutationObserver(() => {
                tryStart();
            });
            this.bodyWaitObserver.observe(root, {
                childList: true,
                subtree: true
            });

            this.bodyWaitTimer = setTimeout(() => {
                tryStart();
            }, 0);
        };
        existing.register = function(scriptName, callback) {
            const observerAlreadyStarted = !!this.observer;
            this.callbacks[scriptName] = callback;
            this.ensureObserver();
            if (observerAlreadyStarted) {
                setTimeout(() => {
                    try { callback(); } catch (e) { console.error('[Observer Error]', e); }
                }, 0);
            }
        };
        window.NTObserverManager = existing;
    }

    initObserverManager();

    // ─── Mod Menu Manifest Bridge ───────────────────────────────────────────────
    const NTCFG_BOT_FLAG_MANIFEST_ID = "bot-flag";
    const NTCFG_BOT_FLAG_MANIFEST_KEY = `ntcfg:manifest:${NTCFG_BOT_FLAG_MANIFEST_ID}`;
    const NTCFG_BOT_FLAG_VALUE_PREFIX = `ntcfg:${NTCFG_BOT_FLAG_MANIFEST_ID}:`;
    const NTCFG_BOT_FLAG_BRIDGE_VERSION = "1.0.0-bridge.1";
    const BOT_FLAG_STORAGE_VERSION = 1;
    const BOT_FLAG_STORAGE_VERSION_KEY = `${NTCFG_BOT_FLAG_VALUE_PREFIX}__storage_version`;
    const BOT_FLAG_LIVE_REFRESH_KEYS = new Set([
        'SHOW_TEAM_PAGE_FLAGS',
        'SHOW_FRIENDS_PAGE_FLAGS',
        'SHOW_LEAGUE_PAGE_FLAGS',
        'SHOW_RACE_RESULT_FLAGS',
        'SHOW_NOT_FLAGGED',
        'SHOW_UNTRACKED',
        'USE_ROBOT_ICON',
        'USE_STARTRACK',
        'USE_NTL_LEGACY'
    ]);

    const BOT_FLAG_SHARED_SETTINGS = {
        SHOW_TEAM_PAGE_FLAGS: {
            type: 'boolean',
            label: 'Show Team Page Flags',
            default: true,
            group: 'Display',
            description: 'Display bot-flag icons on team member lists.'
        },
        SHOW_FRIENDS_PAGE_FLAGS: {
            type: 'boolean',
            label: 'Show Friends Page Flags',
            default: true,
            group: 'Display',
            description: 'Display bot-flag icons on the friends page.'
        },
        SHOW_LEAGUE_PAGE_FLAGS: {
            type: 'boolean',
            label: 'Show League Page Flags',
            default: true,
            group: 'Display',
            description: 'Display bot-flag icons on the leagues page.'
        },
        SHOW_RACE_RESULT_FLAGS: {
            type: 'boolean',
            label: 'Show Race Result Flags',
            default: true,
            group: 'Display',
            description: 'Display bot-flag icons on race results.'
        },
        SHOW_NOT_FLAGGED: {
            type: 'boolean',
            label: 'Show Not Flagged',
            default: true,
            group: 'Display',
            description: 'Show the green icon for racers that are not flagged. Disable to only see flagged/banned racers.'
        },
        SHOW_UNTRACKED: {
            type: 'boolean',
            label: 'Show Untracked',
            default: true,
            group: 'Display',
            description: 'Show the gold icon for racers that are not tracked by StarTrack. Disable to hide untracked indicators.'
        },
        USE_ROBOT_ICON: {
            type: 'boolean',
            label: 'Robot Icon for Flagged',
            default: false,
            group: 'Display',
            description: 'Use a robot head icon instead of a flag for StarTrack-flagged racers.'
        },
        USE_STARTRACK: {
            type: 'boolean',
            label: 'Use StarTrack',
            default: true,
            group: 'Sources',
            description: 'Query NT StarTrack for flag data.'
        },
        USE_NTL_LEGACY: {
            type: 'boolean',
            label: 'Use NTL Legacy',
            default: true,
            group: 'Sources',
            description: 'Query NTL Legacy for flag data.'
        },
        NETWORK_CONCURRENCY_LIMIT: {
            type: 'number',
            label: 'Network Concurrency Limit',
            default: 6,
            group: 'Sources',
            description: 'Maximum number of simultaneous network requests for flag lookups.',
            min: 1,
            max: 20,
            step: 1
        },
        STARTRACK_CACHE_DAYS: {
            type: 'number',
            label: 'StarTrack Cache Duration (days)',
            default: 7,
            group: 'Cache',
            description: 'Number of days to keep cached StarTrack results before re-fetching.',
            min: 1,
            max: 30,
            step: 1
        },
        CROSS_TAB_SYNC: {
            type: 'boolean',
            label: 'Cross-Tab Sync',
            default: true,
            group: 'Cache',
            description: 'Synchronize flag cache across browser tabs via localStorage.'
        },
        DEBUG_LOGGING: {
            type: 'boolean',
            label: 'Debug Logging',
            default: false,
            group: 'Advanced',
            description: 'Enable verbose console logging for troubleshooting.'
        }
    };

    const getNtcfgBotFlagStorageKey = (settingKey) => `${NTCFG_BOT_FLAG_VALUE_PREFIX}${settingKey}`;

    const coerceNtcfgBotFlagValue = (settingKey, value) => {
        const meta = BOT_FLAG_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 readNtcfgBotFlagValue = (settingKey) => {
        const meta = BOT_FLAG_SHARED_SETTINGS[settingKey];
        if (!meta) return undefined;
        try {
            const raw = localStorage.getItem(getNtcfgBotFlagStorageKey(settingKey));
            if (raw == null) return meta.default;
            const parsed = JSON.parse(raw);
            return coerceNtcfgBotFlagValue(settingKey, parsed);
        } catch {
            return meta.default;
        }
    };

    const writeNtcfgBotFlagValue = (settingKey, value) => {
        try {
            const serialized = JSON.stringify(value);
            if (localStorage.getItem(getNtcfgBotFlagStorageKey(settingKey)) !== serialized) {
                localStorage.setItem(getNtcfgBotFlagStorageKey(settingKey), serialized);
            }
        } catch {
            // ignore storage sync failures
        }
    };

    const syncNtcfgBotFlagSettingFromGM = (settingKey) => {
        const meta = BOT_FLAG_SHARED_SETTINGS[settingKey];
        if (!meta) return;

        const normalized = coerceNtcfgBotFlagValue(settingKey, GM_getValue(settingKey, meta.default));
        writeNtcfgBotFlagValue(settingKey, normalized);

        // Sync DEBUG_LOGGING bidirectionally with the existing ntBotFlagDebug localStorage key
        if (settingKey === 'DEBUG_LOGGING') {
            syncBotFlagDebugToggle(normalized);
        }
    };

    const syncAllNtcfgBotFlagSettingsFromGM = () => {
        Object.keys(BOT_FLAG_SHARED_SETTINGS).forEach(syncNtcfgBotFlagSettingFromGM);
    };

    const registerNtcfgBotFlagManifest = () => {
        try {
            const manifest = {
                id: NTCFG_BOT_FLAG_MANIFEST_ID,
                name: 'Flagged Racers',
                version: NTCFG_BOT_FLAG_BRIDGE_VERSION,
                scriptVersion: typeof GM_info !== 'undefined' ? GM_info.script.version : '',
                storageVersion: BOT_FLAG_STORAGE_VERSION,
                supportsGlobalReset: true,
                description: 'Flag and ban detection using NT StarTrack and NTL Legacy sources.',
                sections: [
                    { id: 'display', title: 'Display', subtitle: 'Visual placement and on-page behavior.', resetButton: true },
                    { id: 'sources', title: 'Sources', subtitle: 'Data source preferences and fetch policy.', resetButton: true },
                    { id: 'cache', title: 'Cache', subtitle: 'How long flag results should be kept around.', resetButton: true },
                    { id: 'advanced', title: 'Advanced', subtitle: 'Debug and diagnostic controls.', resetButton: true }
                ],
                settings: BOT_FLAG_SHARED_SETTINGS
            };
            const serialized = JSON.stringify(manifest);
            if (localStorage.getItem(NTCFG_BOT_FLAG_MANIFEST_KEY) !== serialized) {
                localStorage.setItem(NTCFG_BOT_FLAG_MANIFEST_KEY, serialized);
            }
        } catch {
            // ignore manifest registration failures
        }
    };

    const setNtcfgBotFlagValue = (settingKey, value) => {
        const meta = BOT_FLAG_SHARED_SETTINGS[settingKey];
        if (!meta) return value;

        const normalized = coerceNtcfgBotFlagValue(settingKey, value);
        GM_setValue(settingKey, normalized);
        writeNtcfgBotFlagValue(settingKey, normalized);

        // Sync DEBUG_LOGGING bidirectionally with the existing ntBotFlagDebug localStorage key
        if (settingKey === 'DEBUG_LOGGING') {
            syncBotFlagDebugToggle(normalized);
        }

        applyBotFlagSettingSideEffects(settingKey);

        return normalized;
    };

    // Direct apply: always writes through to GM (used for same-tab ntcfg:change events from mod menu)
    const applyNtcfgBotFlagValueDirect = (settingKey, value) => {
        const meta = BOT_FLAG_SHARED_SETTINGS[settingKey];
        if (!meta) return;
        const normalized = coerceNtcfgBotFlagValue(settingKey, value);
        setNtcfgBotFlagValue(settingKey, normalized);
    };

    // Deduped apply: compares against GM before writing (used for cross-tab storage events)
    const applyNtcfgBotFlagValueIfChanged = (settingKey, value) => {
        const meta = BOT_FLAG_SHARED_SETTINGS[settingKey];
        if (!meta) return;

        const normalized = coerceNtcfgBotFlagValue(settingKey, value);
        const currentValue = coerceNtcfgBotFlagValue(settingKey, GM_getValue(settingKey, meta.default));

        if (JSON.stringify(currentValue) !== JSON.stringify(normalized)) {
            setNtcfgBotFlagValue(settingKey, normalized);
        }
    };

    // Bidirectional sync for DEBUG_LOGGING <-> ntBotFlagDebug localStorage key
    const syncBotFlagDebugToggle = (enabled) => {
        try {
            localStorage.setItem('ntBotFlagDebug', enabled ? '1' : '0');
        } catch {
            // ignore storage errors
        }
    };

    // Helper to check if a specific bot-flag feature is enabled via ntcfg
    function isNtcfgBotFlagFeatureEnabled(settingKey) {
        const val = readNtcfgBotFlagValue(settingKey);
        return val !== false;
    }

    const dispatchBotFlagActionResult = (requestId, status, error = '') => {
        if (!requestId) return;
        try {
            document.dispatchEvent(new CustomEvent('ntcfg:action-result', {
                detail: {
                    requestId,
                    script: NTCFG_BOT_FLAG_MANIFEST_ID,
                    status,
                    error
                }
            }));
        } catch {
            // ignore dispatch failures
        }
    };

    const resetBotFlagSettingsToDefaults = () => {
        Object.entries(BOT_FLAG_SHARED_SETTINGS).forEach(([settingKey, meta]) => {
            if (!meta || meta.type === 'note' || meta.type === 'action') return;
            setNtcfgBotFlagValue(settingKey, meta.default);
        });
        try { GM_deleteValue('nt_token'); } catch { /* ignore */ }
        try {
            if (typeof GM_listValues === 'function') {
                GM_listValues().forEach((key) => {
                    if (key.startsWith('startrack_')
                        || key.startsWith('ntl_')
                        || key.startsWith('team_activity_v2_')
                        || key === 'team_applications_v2'
                        || key === 'leagues_user_activity_v2') {
                        GM_deleteValue(key);
                    }
                });
            }
        } catch { /* ignore */ }
        try { localStorage.removeItem('ntBotFlagDebug'); } catch { /* ignore */ }
        clearSharedBotStatusCache();
    };

    // Listen for mod menu changes (same tab)
    document.addEventListener('ntcfg:change', (event) => {
        if (event?.detail?.script !== NTCFG_BOT_FLAG_MANIFEST_ID) return;
        applyNtcfgBotFlagValueDirect(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 {
            resetBotFlagSettingsToDefaults();
            GM_setValue(BOT_FLAG_STORAGE_VERSION_KEY, BOT_FLAG_STORAGE_VERSION);
            registerNtcfgBotFlagManifest();
            syncAllNtcfgBotFlagSettingsFromGM();
            document.dispatchEvent(new CustomEvent('ntcfg:manifest-updated', {
                detail: { script: NTCFG_BOT_FLAG_MANIFEST_ID }
            }));
            dispatchBotFlagActionResult(detail.requestId, 'success');
        } catch (error) {
            dispatchBotFlagActionResult(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_BOT_FLAG_VALUE_PREFIX) || event.newValue == null) return;
        const settingKey = storageKey.slice(NTCFG_BOT_FLAG_VALUE_PREFIX.length);
        if (!BOT_FLAG_SHARED_SETTINGS[settingKey]) return;
        try {
            applyNtcfgBotFlagValueIfChanged(settingKey, JSON.parse(event.newValue));
        } catch {
            // ignore invalid synced payloads
        }
    });

    // Register manifest and sync settings
    registerNtcfgBotFlagManifest();
    syncAllNtcfgBotFlagSettingsFromGM();
    try { GM_setValue(BOT_FLAG_STORAGE_VERSION_KEY, BOT_FLAG_STORAGE_VERSION); } catch { /* ignore */ }

    const publishNtcfgBotFlagManifestHeartbeat = () => {
        try { localStorage.setItem('ntcfg:alive:' + NTCFG_BOT_FLAG_MANIFEST_ID, String(Date.now())); } catch { /* ignore */ }
        try {
            document.dispatchEvent(new CustomEvent('ntcfg:manifest-updated', {
                detail: { script: NTCFG_BOT_FLAG_MANIFEST_ID }
            }));
        } catch {
            // ignore event dispatch failures
        }
    };
    publishNtcfgBotFlagManifestHeartbeat();

    // ─── End Mod Menu Manifest Bridge ───────────────────────────────────────────

    // =============================
    // 🎨 Constants & Configuration
    // =============================
    function getStarTrackCacheDuration() {
        const days = readNtcfgBotFlagValue('STARTRACK_CACHE_DAYS');
        return (typeof days === 'number' && days > 0 ? days : 7) * 24 * 60 * 60 * 1000;
    }
    function getNetworkConcurrencyLimit() {
        const limit = readNtcfgBotFlagValue('NETWORK_CONCURRENCY_LIMIT');
        return typeof limit === 'number' && limit > 0 ? limit : 6;
    }
    const FRIENDS_PAGE_RUN_COOLDOWN_MS = 400;
    const BOT_FLAG_VIEW_REFRESH_DELAYS = [100, 320, 800];
    const DEBUG_FLAG_KEY = 'ntBotFlagDebug';
    const DEBUG_PARAM_ENABLED = /(?:\?|&)ntbotdebug=1(?:&|$)/.test(window.location.search);
    const SCRIPT_VERSION = typeof GM_info !== 'undefined' && GM_info?.script?.version
        ? String(GM_info.script.version)
        : '5.1.2';
    const networkQueue = [];
    const startrackInflight = new Map();
    const ntlInflight = new Map();
    let activeNetworkRequests = 0;
    let lastFriendsPageRunAt = 0;
    let lastBotFlagViewSignature = '';

    const colorMap = {
        green: "#4ade80",
        red: "#d62f3a",
        yellow: "#f3a81b",
        orange: "#e8796b",
        gray: "#a6aac1"
    };

    let customTooltipElement = null;

    function normalizeUsername(username) {
        return String(username || '').trim().toLowerCase();
    }

    function normalizeDisplayName(displayName) {
        return String(displayName || '').trim();
    }

    function normalizeTeamTag(teamTag) {
        return String(teamTag || '').replace(/[\[\]\s]/g, '').trim().toLowerCase();
    }

    function normalizeBotFlagViewSignatureText(value) {
        return String(value || '').replace(/\s+/g, ' ').trim().toLowerCase();
    }

    function buildBotFlagViewSignatureSample(values, limit = 8) {
        return values
            .map((value) => normalizeBotFlagViewSignatureText(value))
            .filter(Boolean)
            .slice(0, limit)
            .join('|');
    }

    function isIdentityLookup(value) {
        return !!(value
            && typeof value === 'object'
            && value.byDisplay
            && value.byDisplayAndTag
            && value.byUsername
            && value.ambiguousDisplay);
    }

    function createIdentityLookup(entries) {
        const lookup = {
            byDisplay: Object.create(null),
            byDisplayAndTag: Object.create(null),
            byUsername: Object.create(null),
            ambiguousDisplay: Object.create(null)
        };

        (Array.isArray(entries) ? entries : []).forEach((entry) => {
            const displayName = normalizeDisplayName(entry?.displayName || entry?.username || '');
            const username = normalizeUsername(entry?.username || '');
            const teamTag = normalizeTeamTag(entry?.tag || entry?.teamTag || entry?.team || '');

            if (username) {
                lookup.byUsername[username] = username;
            }

            if (displayName && username && teamTag) {
                lookup.byDisplayAndTag[`${displayName}::${teamTag}`] = username;
            }

            if (!displayName || !username) return;

            const existing = lookup.byDisplay[displayName];
            if (!existing) {
                lookup.byDisplay[displayName] = username;
                return;
            }

            if (existing !== username) {
                delete lookup.byDisplay[displayName];
                lookup.ambiguousDisplay[displayName] = true;
            }
        });

        return lookup;
    }

    function resolveUsernameFromLookup(lookup, options = {}) {
        if (!isIdentityLookup(lookup)) return '';

        const explicitUsername = normalizeUsername(options.username);
        if (explicitUsername && lookup.byUsername[explicitUsername]) {
            return lookup.byUsername[explicitUsername];
        }

        const displayName = normalizeDisplayName(options.displayName);
        const teamTag = normalizeTeamTag(options.teamTag);
        if (displayName && teamTag) {
            const combined = lookup.byDisplayAndTag[`${displayName}::${teamTag}`];
            if (combined) return combined;
        }

        if (displayName && lookup.byDisplay[displayName]) {
            return lookup.byDisplay[displayName];
        }

        return '';
    }

    function isAmbiguousDisplayMatch(lookup, displayName) {
        if (!isIdentityLookup(lookup)) return false;
        const normalizedDisplay = normalizeDisplayName(displayName);
        return !!(normalizedDisplay && lookup.ambiguousDisplay[normalizedDisplay]);
    }

    function getDisplayNameFromElement(element) {
        if (!element) return '';
        const titled = normalizeDisplayName(element.getAttribute && element.getAttribute('title'));
        if (titled) return titled;
        return normalizeDisplayName(element.textContent);
    }

    function extractTeamTagFromElement(element) {
        if (!element) return '';

        const root = element?.classList?.contains('player-name--container')
            ? element.closest('.table-row, .gridTable-row, .raceResults-body.row, .row') || element.parentElement || element
            : element;

        const tagCandidates = [
            root?.querySelector?.('.player-name--tag'),
            root?.querySelector?.('.team-tag'),
            root?.querySelector?.('.team-name--tag'),
            element?.previousElementSibling
        ].filter(Boolean);

        for (const candidate of tagCandidates) {
            if (!candidate) continue;
            const text = normalizeTeamTag(candidate.textContent || '');
            if (text) return text;
        }

        const linkCandidates = [];
        if (typeof root?.querySelectorAll === 'function') {
            root.querySelectorAll('a[href*="/team/"]').forEach((link) => linkCandidates.push(link));
        }
        if (typeof element?.closest === 'function') {
            const direct = element.closest('a[href*="/team/"]');
            if (direct) linkCandidates.push(direct);
        }

        for (const link of linkCandidates) {
            const href = link?.getAttribute?.('href') || '';
            const match = href.match(/\/team\/([^/?#]+)/i);
            const normalized = normalizeTeamTag(match ? match[1] : '');
            if (normalized) return normalized;
        }

        return '';
    }

    function extractExplicitUsernameFromElement(element) {
        if (!element) return '';

        const attrCandidates = [
            element,
            element.closest?.('[data-nt-username]'),
            element.closest?.('[data-username]'),
            element.closest?.('[data-user]')
        ].filter(Boolean);

        for (const candidate of attrCandidates) {
            const attrValue = candidate.getAttribute('data-nt-username')
                || candidate.getAttribute('data-username')
                || candidate.getAttribute('data-user');
            const normalized = normalizeUsername(attrValue);
            if (normalized) return normalized;
        }

        const linkCandidates = [];
        const directLink = element.closest?.('a[href*="/racer/"]');
        if (directLink) linkCandidates.push(directLink);

        const searchRoots = [
            element,
            element.closest?.('.player-name--container'),
            element.closest?.('.table-row'),
            element.closest?.('.gridTable-row'),
            element.closest?.('.modal--raceResults'),
            element.closest?.('.profile-title')
        ].filter(Boolean);

        searchRoots.forEach((root) => {
            if (typeof root.querySelectorAll !== 'function') return;
            root.querySelectorAll('a[href*="/racer/"]').forEach((link) => linkCandidates.push(link));
        });

        for (const link of linkCandidates) {
            const href = link?.getAttribute?.('href') || '';
            const match = href.match(/\/racer\/([^/?#]+)/i);
            const normalized = normalizeUsername(match ? match[1] : '');
            if (normalized) return normalized;
        }

        if (window.location.pathname.startsWith('/racer/')) {
            return normalizeUsername(window.location.pathname.split('/').pop());
        }

        return '';
    }

    function extractRecentTabUsernameFromElement(element) {
        const explicitUsername = extractExplicitUsernameFromElement(element);
        if (explicitUsername) return explicitUsername;

        const namedContainer = element?.closest?.('.player-name--container')
            || element?.querySelector?.('.player-name--container');
        if (namedContainer) {
            const titledUsername = normalizeUsername(namedContainer.getAttribute('title'));
            if (titledUsername) return titledUsername;
        }

        const visibleName = normalizeUsername(getDisplayNameFromElement(
            element?.classList?.contains?.('player-name--container')
                ? element
                : element?.querySelector?.('.player-name--container') || element
        ));
        return visibleName || '';
    }

    function resolveUsernameForElement(element, lookup, surfaceLabel) {
        const explicitUsername = extractExplicitUsernameFromElement(element);
        if (explicitUsername) return explicitUsername;

        const playerElement = element?.classList?.contains('player-name--container')
            ? element
            : element?.querySelector?.('.player-name--container') || element;

        const displayName = getDisplayNameFromElement(playerElement);
        if (!displayName) return '';

        const teamTag = extractTeamTagFromElement(playerElement || element);
        const mappedUsername = resolveUsernameFromLookup(lookup, {
            displayName: displayName,
            teamTag: teamTag
        });
        if (!mappedUsername && isAmbiguousDisplayMatch(lookup, displayName)) {
            debugLog('Skipped ambiguous display-name match', {
                surface: surfaceLabel,
                displayName: displayName,
                teamTag: teamTag || ''
            });
        }
        return mappedUsername;
    }

    function isDebugEnabled() {
        return DEBUG_PARAM_ENABLED || localStorage.getItem(DEBUG_FLAG_KEY) === '1' || readNtcfgBotFlagValue('DEBUG_LOGGING') === true;
    }

    function debugLog(event, payload) {
        if (!isDebugEnabled()) return;
        const ts = new Date().toISOString();
        if (payload === undefined) {
            console.log(`[BotFlag][DBG ${ts}] ${event}`);
            return;
        }
        console.log(`[BotFlag][DBG ${ts}] ${event}`, payload);
    }

    function queueNetworkRequest(requestFn, meta = {}) {
        const label = meta.label || 'request';
        const username = meta.username ? normalizeUsername(meta.username) : null;
        return new Promise((resolve, reject) => {
            networkQueue.push({ requestFn, resolve, reject, label, username });
            debugLog('Queue enqueue', {
                label: label,
                username: username,
                queueDepth: networkQueue.length,
                activeNetworkRequests: activeNetworkRequests,
                concurrencyLimit: getNetworkConcurrencyLimit()
            });
            drainNetworkQueue();
        });
    }

    function drainNetworkQueue() {
        if (activeNetworkRequests >= getNetworkConcurrencyLimit()) return;
        const next = networkQueue.shift();
        if (!next) return;

        activeNetworkRequests += 1;
        debugLog('Queue start', {
            label: next.label,
            username: next.username,
            queueDepth: networkQueue.length,
            activeNetworkRequests: activeNetworkRequests
        });
        Promise.resolve()
            .then(next.requestFn)
            .then(next.resolve, next.reject)
            .finally(() => {
                activeNetworkRequests -= 1;
                debugLog('Queue complete', {
                    label: next.label,
                    username: next.username,
                    queueDepth: networkQueue.length,
                    activeNetworkRequests: activeNetworkRequests
                });
                drainNetworkQueue();
            });
    }

    function initDebugAPI() {
        const debugApi = {
            enable: function() {
                localStorage.setItem(DEBUG_FLAG_KEY, '1');
                console.log('[BotFlag][DBG] Enabled.');
            },
            disable: function() {
                localStorage.removeItem(DEBUG_FLAG_KEY);
                console.log('[BotFlag][DBG] Disabled.');
            },
            status: function() {
                const status = {
                    enabled: isDebugEnabled(),
                    paramEnabled: DEBUG_PARAM_ENABLED,
                    scriptVersion: SCRIPT_VERSION,
                    queueDepth: networkQueue.length,
                    activeNetworkRequests: activeNetworkRequests,
                    concurrencyLimit: getNetworkConcurrencyLimit(),
                    startrackInflight: startrackInflight.size,
                    ntlInflight: ntlInflight.size
                };
                console.log('[BotFlag][DBG] Status', status);
                return status;
            },
            inspectUser: function(username) {
                const normalized = normalizeUsername(username);
                const result = {
                    username: normalized,
                    ntSharedStatus: window.NTShared && window.NTShared.getBotStatus ? window.NTShared.getBotStatus(normalized) : null,
                    startrackCache: GM_getValue(`startrack_${normalized}`),
                    ntlCache: GM_getValue(`ntl_${normalized}`),
                    startrackInflight: startrackInflight.has(normalized),
                    ntlInflight: ntlInflight.has(normalized)
                };
                console.log('[BotFlag][DBG] Inspect user', result);
                return result;
            },
            clearRowState: function() {
                const targets = document.querySelectorAll('[data-status-processing],[data-status-processing-for],[data-status-processed-for],[data-nt-username],[data-botflag-processed]');
                targets.forEach((el) => {
                    el.removeAttribute('data-status-processing');
                    el.removeAttribute('data-status-processing-for');
                    el.removeAttribute('data-status-processed-for');
                    el.removeAttribute('data-nt-username');
                    el.removeAttribute('data-botflag-processed');
                });
                console.log(`[BotFlag][DBG] Cleared row state on ${targets.length} elements.`);
                return targets.length;
            },
            clearTeamActivityCache: function(teamTag) {
                const fromPath = (window.location.pathname.split('/').filter(Boolean).pop() || '').trim();
                const raw = String(teamTag || fromPath || '').trim();
                if (!raw) {
                    console.log('[BotFlag][DBG] No team tag available to clear cache.');
                    return [];
                }

                const upper = raw.toUpperCase();
                const lower = raw.toLowerCase();
                const keys = [
                    `team_activity_${upper}`,
                    `team_activity_${lower}`,
                    `team_activity_v2_${upper}`,
                    `team_activity_v2_${lower}`
                ];
                keys.forEach((key) => GM_setValue(key, null));
                console.log('[BotFlag][DBG] Cleared team activity cache keys', keys);
                return keys;
            },
            refreshTeamActivityNow: async function(teamTag) {
                this.clearTeamActivityCache(teamTag);
                this.clearRowState();
                const data = await fetchTeamActivity();
                if (data) await updateUsersTeam(data);
                const summary = {
                    refreshed: !!data,
                    users: data ? Object.keys(data).length : 0
                };
                console.log('[BotFlag][DBG] Team activity refresh result', summary);
                return summary;
            }
        };

        window.NTBotFlagDebug = debugApi;
        try {
            if (typeof unsafeWindow !== 'undefined') {
                unsafeWindow.NTBotFlagDebug = debugApi;
            }
        } catch (e) {}
        if (isDebugEnabled()) {
            console.log('[BotFlag][DBG] Enabled. API: window.NTBotFlagDebug.status(), .inspectUser("username"), .clearRowState(), .clearTeamActivityCache("TAG"), .refreshTeamActivityNow("TAG"), .disable()');
        }
    }

    // =============================
    // 🔑 Token Management
    // =============================
    function clearLegacyStoredToken() {
        try { GM_deleteValue('nt_token'); } catch { /* ignore */ }
    }

    function getToken() {
        const token = String(localStorage.getItem("player_token") || "").trim();
        clearLegacyStoredToken();
        return token || null;
    }

    function getTokenAndRetry(callback) {
        const token = getToken();
        if (token) {
            callback();
        }
    }

    // =============================
    // 🛠️ Helper: Set Icon on Element
    // =============================
    function findPreferredRaceStatusField(element) {
        if (!element) return null;

        const directMatches = [
            element.closest('.profile-title'),
            element.closest('.tsxs.tc-fuel')
        ].filter(Boolean);
        if (directMatches.length > 0) {
            return directMatches[0];
        }

        const containers = [
            element.closest('.gridTable-cell'),
            element.closest('.gridTable-row'),
            element.closest('.raceResults-playerName'),
            element.closest('.raceResults'),
            element.closest('.race-results'),
            element.closest('.modal--raceResults')
        ].filter(Boolean);

        for (const container of containers) {
            if (!container || container === element) continue;
            const titleField = container.querySelector('.tsxs.tc-fuel');
            if (titleField) return titleField;
            const profileTitle = container.querySelector('.profile-title');
            if (profileTitle) return profileTitle;
        }

        let node = element.parentElement;
        while (node && node !== document.body) {
            if (node.classList?.contains('profile-title')) {
                return node;
            }
            if (node.classList?.contains('tc-fuel') && node.classList.contains('tsxs')) {
                return node;
            }

            const titleField = node.querySelector('.tsxs.tc-fuel');
            if (titleField) return titleField;
            const profileTitle = node.querySelector('.profile-title');
            if (profileTitle) return profileTitle;
            node = node.parentElement;
        }

        return null;
    }

    function getStatusField(element) {
        if (!element) return null;

        let statusField;
        if (element.classList.contains('table-row')) {
            statusField = element.querySelector('.tsi.tc-lemon.tsxs');
        } else if (element.classList.contains('profile-title')) {
            statusField = element;
        } else if (element.classList.contains('player-name--container')) {
            statusField = findPreferredRaceStatusField(element) || element;
        } else if (element.classList.contains('raceResults-playerName')) {
            statusField = findPreferredRaceStatusField(element) || element;
        } else if (element.classList.contains('tc-fuel')) {
            statusField = element;
        } else {
            const allRows = document.querySelectorAll('.table-row');
            for (const row of allRows) {
                const racerContainer = row.querySelector('.player-name--container');
                if (racerContainer && racerContainer.getAttribute('title') === element.getAttribute('title')) {
                    statusField = row.querySelector('.tsi.tc-lemon.tsxs');
                    break;
                }
            }
        }

        return statusField;
    }

    function clearStatusIcons(element) {
        const statusField = getStatusField(element);
        if (!statusField) return;
        statusField.querySelectorAll('.status-icon').forEach((icon) => icon.remove());
    }

    let botFlagRefreshTimer = null;
    let botFlagViewRefreshTimers = new Set();
    let botFlagDelegatedViewListenersInstalled = false;
    let racelogModalMonitorTimer = null;
    let racePagePollerTimer = null;
    let racePagePollerStopTimer = null;
    let nonRacePagePollerTimer = null;
    let nonRacePagePollerStopTimer = null;
    let lastRacelogResultsModalKey = '';

    function clearRenderedBotFlags() {
        document.querySelectorAll('.status-icon').forEach((icon) => icon.remove());
        document.querySelectorAll('[data-status-processing],[data-status-processing-for],[data-status-processed-for],[data-nt-username],[data-botflag-processed]').forEach((el) => {
            el.removeAttribute('data-status-processing');
            el.removeAttribute('data-status-processing-for');
            el.removeAttribute('data-status-processed-for');
            el.removeAttribute('data-nt-username');
            el.removeAttribute('data-botflag-processed');
        });
        if (customTooltipElement && typeof customTooltipElement.remove === 'function') {
            customTooltipElement.remove();
            customTooltipElement = null;
        }
    }

    function clearTrackedBotFlagViewRefreshTimers() {
        botFlagViewRefreshTimers.forEach((timerId) => {
            clearTimeout(timerId);
        });
        botFlagViewRefreshTimers.clear();
    }

    function rerunCurrentPageBotFlags() {
        runBotFlagHandlersForCurrentRoute({ clearExisting: true, requireDomReady: false });
    }

    function scheduleBotFlagRefresh() {
        if (botFlagRefreshTimer) {
            clearTimeout(botFlagRefreshTimer);
        }
        botFlagRefreshTimer = setTimeout(() => {
            botFlagRefreshTimer = null;
            rerunCurrentPageBotFlags();
        }, 50);
    }

    function scheduleBotFlagViewRefresh(path = window.location.pathname, delays = BOT_FLAG_VIEW_REFRESH_DELAYS) {
        clearTrackedBotFlagViewRefreshTimers();
        delays.forEach((delay) => {
            const timerId = setTimeout(() => {
                botFlagViewRefreshTimers.delete(timerId);
                if (window.location.pathname !== path) return;
                if (path === '/friends') {
                    lastFriendsPageRunAt = 0;
                }
                lastBotFlagViewSignature = '';
                runBotFlagHandlersForCurrentRoute({ clearExisting: true, requireDomReady: false });
            }, delay);
            botFlagViewRefreshTimers.add(timerId);
        });
    }

    function installDelegatedBotFlagViewListeners() {
        if (botFlagDelegatedViewListenersInstalled) return;
        botFlagDelegatedViewListenersInstalled = true;

        const handleViewEvent = (event) => {
            const target = event.target;
            if (!(target instanceof Element)) return;

            if (window.location.pathname === '/friends') {
                if (target.closest('.tab') || target.closest('input[name="onlineOnly"]')) {
                    scheduleBotFlagViewRefresh('/friends');
                }
                return;
            }

            if (window.location.pathname === '/leagues') {
                if (target.closest('input[type="radio"][name="showteam"]') || target.closest('label[for="showteam"]') || target.closest('label[for="showindividual"]')) {
                    scheduleBotFlagViewRefresh('/leagues');
                }
            }
        };

        document.addEventListener('click', handleViewEvent, true);
        document.addEventListener('change', handleViewEvent, true);
    }

    function applyBotFlagSettingSideEffects(settingKey) {
        if (!settingKey || !BOT_FLAG_LIVE_REFRESH_KEYS.has(settingKey)) return;
        scheduleBotFlagRefresh();
    }
    function isRaceRoute(path = window.location.pathname) {
        return path === '/race' || path.startsWith('/race/');
    }
    function isRacelogResultsRoute(path = window.location.pathname) {
        return path.startsWith('/racelog') || path.startsWith('/stats');
    }
    function isNonRaceBotFlagRoute(path = window.location.pathname) {
        return path.startsWith('/team') || path === '/friends' || path === '/leagues';
    }
    function getRelevantNonRaceBotFlagRows(path = window.location.pathname) {
        if (path.startsWith('/team')) {
            const teamTable = document.querySelector('.table.table--striped.table--selectable.table--team.table--teamOverview');
            if (!teamTable) return [];
            return Array.from(teamTable.querySelectorAll('.table-row')).filter((row) => !!row.querySelector('.player-name--container'));
        }

        if (path === '/friends') {
            const friendsTable = document.querySelector('.table.table--selectable.table--striped.table--friends');
            if (!friendsTable) return [];
            return Array.from(friendsTable.querySelectorAll('.table-row')).filter((row) => !!row.querySelector('.player-name--container'));
        }

        if (path === '/leagues') {
            return Array.from(document.querySelectorAll('.table-row')).filter((row) => !!row.querySelector('.player-name--container'));
        }

        return [];
    }
    function isNonRaceBotFlagDomReady(path = window.location.pathname) {
        return getRelevantNonRaceBotFlagRows(path).length > 0;
    }
    function hasPendingNonRaceBotFlagRows(path = window.location.pathname) {
        return getRelevantNonRaceBotFlagRows(path).some((row) => {
            const statusField = getStatusField(row);
            const alreadyTouched = row.hasAttribute('data-status-processing-for') || row.hasAttribute('data-status-processed-for');
            const hasIcon = !!statusField?.querySelector('.status-icon');
            return !alreadyTouched && !hasIcon;
        });
    }
    function stopRacePagePollingFallback() {
        if (racePagePollerTimer) {
            clearInterval(racePagePollerTimer);
            racePagePollerTimer = null;
        }
        if (racePagePollerStopTimer) {
            clearTimeout(racePagePollerStopTimer);
            racePagePollerStopTimer = null;
        }
    }
    function stopNonRacePagePollingFallback() {
        if (nonRacePagePollerTimer) {
            clearInterval(nonRacePagePollerTimer);
            nonRacePagePollerTimer = null;
        }
        if (nonRacePagePollerStopTimer) {
            clearTimeout(nonRacePagePollerStopTimer);
            nonRacePagePollerStopTimer = null;
        }
    }
    function stopRacelogResultsModalMonitor() {
        if (racelogModalMonitorTimer) {
            clearInterval(racelogModalMonitorTimer);
            racelogModalMonitorTimer = null;
        }
        lastRacelogResultsModalKey = '';
    }
    function syncNonRacePagePollingFallback(path = window.location.pathname) {
        const normalizedPath = path.replace(/\/+$/, '') || '/';
        const routeEnabled = (normalizedPath.startsWith('/team') && isNtcfgBotFlagFeatureEnabled('SHOW_TEAM_PAGE_FLAGS'))
            || (normalizedPath === '/friends' && isNtcfgBotFlagFeatureEnabled('SHOW_FRIENDS_PAGE_FLAGS'))
            || (normalizedPath === '/leagues' && isNtcfgBotFlagFeatureEnabled('SHOW_LEAGUE_PAGE_FLAGS'));

        if (!isNonRaceBotFlagRoute(normalizedPath) || !routeEnabled) {
            stopNonRacePagePollingFallback();
            return;
        }

        if (nonRacePagePollerTimer) return;

        nonRacePagePollerTimer = setInterval(() => {
            try {
                const currentPath = window.location.pathname.replace(/\/+$/, '') || '/';
                if (currentPath !== normalizedPath) {
                    stopNonRacePagePollingFallback();
                    return;
                }
                if (!isNonRaceBotFlagDomReady(currentPath)) return;

                runBotFlagHandlersForCurrentRoute({ requireDomReady: false });

                if (!hasPendingNonRaceBotFlagRows(currentPath)) {
                    stopNonRacePagePollingFallback();
                }
            } catch (e) { /* ignore */ }
        }, 500);

        nonRacePagePollerStopTimer = setTimeout(() => {
            stopNonRacePagePollingFallback();
        }, 20000);
    }
    function syncBotFlagRouteFallbacks() {
        const racePath = window.location.pathname.replace(/\/+$/, '') || '/';
        const isTopRaceShell = isRaceRoute(racePath)
            && window.top === window
            && !document.getElementById('raceContainer');

        if (isRaceRoute(racePath) && !isTopRaceShell) {
            if (!racePagePollerTimer) {
                racePagePollerTimer = setInterval(() => {
                    try {
                        if (!isNtcfgBotFlagFeatureEnabled('SHOW_RACE_RESULT_FLAGS')) return;
                        if (document.querySelector('.race-results')) {
                            handleRacePage();
                        }
                    } catch (e) { /* ignore */ }
                }, 500);
                racePagePollerStopTimer = setTimeout(() => {
                    stopRacePagePollingFallback();
                }, 600000);
            }
        } else {
            stopRacePagePollingFallback();
        }

        if (!isRacelogResultsRoute(window.location.pathname) || !isNtcfgBotFlagFeatureEnabled('SHOW_RACE_RESULT_FLAGS')) {
            stopRacelogResultsModalMonitor();
        }

        syncNonRacePagePollingFallback(racePath);
    }

    function buildBotFlagViewSignature(path = window.location.pathname) {
        const normalizedPath = path.replace(/\/+$/, '') || '/';

        if (normalizedPath === '/friends') {
            const activeTab = getActiveTab() || '';
            const filterInput = document.querySelector('input[name="onlineOnly"]:checked');
            const filterValue = filterInput ? `${filterInput.name}:${filterInput.value}` : '';
            const names = getRowsForTab()
                .map((row) => {
                    const nameContainer = row.querySelector('.player-name--container');
                    return normalizeBotFlagViewSignatureText(nameContainer?.textContent || '');
                });
            return ['friends', normalizeBotFlagViewSignatureText(activeTab), filterValue, buildBotFlagViewSignatureSample(names)].join('::');
        }

        if (normalizedPath === '/leagues') {
            const checkedRadio = document.querySelector('input[name="showteam"]:checked');
            const tabValue = normalizeBotFlagViewSignatureText(checkedRadio?.value || checkedRadio?.id || '');
            if (tabValue === 'team' || tabValue.includes('team')) {
                const teamTags = Array.from(document.querySelectorAll('td.table-cell.leagues--standings--team'))
                    .map((cell) => {
                        const match = String(cell.textContent || '').match(/\[([^\]]+)\]/);
                        return normalizeTeamTag(match ? match[1] : '') || normalizeBotFlagViewSignatureText(cell.textContent || '');
                    });
                return ['leagues', 'team', buildBotFlagViewSignatureSample(teamTags)].join('::');
            }
            const names = getRowsForTab()
                .map((row) => {
                    const nameContainer = row.querySelector('.player-name--container');
                    return normalizeBotFlagViewSignatureText(nameContainer?.textContent || '');
                });
            return ['leagues', 'personal', buildBotFlagViewSignatureSample(names)].join('::');
        }

        if (normalizedPath.startsWith('/team')) {
            const names = getRelevantNonRaceBotFlagRows(normalizedPath)
                .map((row) => {
                    const nameContainer = row.querySelector('.player-name--container');
                    return normalizeBotFlagViewSignatureText(nameContainer?.textContent || '');
                });
            return ['team', normalizedPath, buildBotFlagViewSignatureSample(names)].join('::');
        }

        return normalizedPath;
    }

    function runBotFlagHandlersForCurrentRoute(options = {}) {
        let {
            clearExisting = false,
            requireDomReady = true
        } = options;

        const path = window.location.pathname;
        const currentViewSignature = buildBotFlagViewSignature(path);
        if (currentViewSignature && currentViewSignature !== lastBotFlagViewSignature) {
            clearExisting = true;
            if (path === '/friends') {
                lastFriendsPageRunAt = 0;
            }
        }
        lastBotFlagViewSignature = currentViewSignature || path;

        if (clearExisting) {
            clearRenderedBotFlags();
        }

        if (path.startsWith("/team")) {
            if (!isNtcfgBotFlagFeatureEnabled('SHOW_TEAM_PAGE_FLAGS')) return;
            if (!requireDomReady || document.querySelector('.table-row')) {
                handleTeamPage();
            }
            return;
        }

        if (path.startsWith("/racer")) {
            if (!requireDomReady || document.querySelector('.profile-title')) {
                handleRacerPage();
            }
            return;
        }

        if (path === "/leagues") {
            if (!isNtcfgBotFlagFeatureEnabled('SHOW_LEAGUE_PAGE_FLAGS')) return;
            if (!requireDomReady || document.querySelector('.table-row')) {
                handleLeaguesPage();
            }
            return;
        }

        if (path === "/friends") {
            if (!isNtcfgBotFlagFeatureEnabled('SHOW_FRIENDS_PAGE_FLAGS')) return;
            if (!requireDomReady || document.querySelector('.tab')) {
                handleFriendsPage();
            }
            return;
        }

        if (isRaceRoute(path)) {
            if (!isNtcfgBotFlagFeatureEnabled('SHOW_RACE_RESULT_FLAGS')) return;
            if (!requireDomReady || document.querySelector('.race-results')) {
                handleRacePage();
            }
            return;
        }

        if (isRacelogResultsRoute(path)) {
            if (!isNtcfgBotFlagFeatureEnabled('SHOW_RACE_RESULT_FLAGS')) return;
            lastRacelogResultsModalKey = '';
            if (!requireDomReady || document.querySelector('.modal--raceResults.is-active')) {
                void handleRacelogResultsModalPage();
            }
            ensureRacelogResultsModalMonitor();
        }
    }

    function shouldProcessStatusFor(element, username) {
        if (!element) return false;
        const normalized = normalizeUsername(username);
        if (!normalized) return false;

        const processingFor = normalizeUsername(element.getAttribute('data-status-processing-for'));
        if (processingFor === normalized) return false;

        const processedFor = normalizeUsername(element.getAttribute('data-status-processed-for'));
        if (processedFor !== normalized) return true;

        const statusField = getStatusField(element);
        if (!statusField) return true;
        return !statusField.querySelector('.status-icon-source-st');
    }

    function markStatusProcessing(element, username) {
        const normalized = normalizeUsername(username);
        const processedFor = normalizeUsername(element.getAttribute('data-status-processed-for'));
        if (processedFor && processedFor !== normalized) {
            clearStatusIcons(element);
        }

        element.setAttribute('data-status-processing', 'true');
        element.setAttribute('data-status-processing-for', normalized);
        element.setAttribute('data-nt-username', normalized);
    }

    function markStatusProcessed(element, username) {
        const normalized = normalizeUsername(username);
        const processingFor = normalizeUsername(element.getAttribute('data-status-processing-for'));
        if (processingFor && processingFor !== normalized) return;

        element.removeAttribute('data-status-processing');
        element.removeAttribute('data-status-processing-for');
        element.setAttribute('data-status-processed-for', normalized);
    }

    // SVG icon builders for each status type
    function getFlagSvg(fillColor) {
        return `<svg viewBox="0 0 24 24" width="14" height="14" fill="${fillColor}" stroke="none" style="vertical-align:-2px;"><path d="M4 2v20h2v-8h4l1 2h8V4h-8l-1 2H6V2z"/></svg>`;
    }

    function getRobotSvg(fillColor) {
        return `<svg viewBox="0 0 24 24" width="1em" height="1em" fill="${fillColor}" stroke="none" style="vertical-align:-0.15em;"><path d="M12 2a1 1 0 0 1 1 1v2h3a3 3 0 0 1 3 3v2h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-1v2a3 3 0 0 1-3 3H8a3 3 0 0 1-3-3v-2H4a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h1V8a3 3 0 0 1 3-3h3V3a1 1 0 0 1 1-1zM9.5 10a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm5 0a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zM9 15a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2H9z"/></svg>`;
    }

    function getStatusSvg(color, source) {
        const fillColor = colorMap[color] || '#a6aac1';
        // Use robot icon for red StarTrack flags when the setting is enabled
        if (color === 'red' && source === 'ST' && readNtcfgBotFlagValue('USE_ROBOT_ICON') === true) {
            return getRobotSvg(fillColor);
        }
        return getFlagSvg(fillColor);
    }

    function setIcon(element, color, tooltip, source) {
        // Respect visibility toggles
        if (color === 'green' && readNtcfgBotFlagValue('SHOW_NOT_FLAGGED') === false) return;
        if (color === 'yellow' && readNtcfgBotFlagValue('SHOW_UNTRACKED') === false) return;

        const statusField = getStatusField(element);
        if (!statusField) return;

        const sourceClass = source === "NTL" ? 'status-icon-source-ntl' : 'status-icon-source-st';
        const existingIcons = statusField.querySelectorAll(`.${sourceClass}`);
        existingIcons.forEach(icon => icon.remove());

        const iconSpan = document.createElement('span');
        iconSpan.classList.add('status-icon', `status-icon-${color}`, sourceClass);
        iconSpan.style.marginLeft = "5px";
        iconSpan.style.display = "inline-flex";
        iconSpan.style.alignItems = "center";
        iconSpan.style.gap = "3px";
        iconSpan.style.fontSize = "12px";
        iconSpan.style.fontWeight = "600";
        iconSpan.style.whiteSpace = "nowrap";
        iconSpan.style.cursor = "help";

        const label = source === "NTL" ? "NTL" : "ST";
        const labelColor = colorMap[color] || color;
        const useRobot = color === 'red' && source === 'ST' && readNtcfgBotFlagValue('USE_ROBOT_ICON') === true;
        // Robot icon: render inline like text, inherit surrounding font size
        if (useRobot) {
            iconSpan.style.display = "inline";
            iconSpan.style.fontSize = "inherit";
            iconSpan.innerHTML = getRobotSvg(labelColor);
        } else {
            iconSpan.innerHTML = `<span style="color: ${labelColor};">${label}</span>`;
        }

        iconSpan.addEventListener('mouseenter', function(event) {
            customTooltipElement = document.createElement('div');
            customTooltipElement.textContent = tooltip;
            customTooltipElement.style.position = 'absolute';
            customTooltipElement.style.backgroundColor = '#333';
            customTooltipElement.style.color = 'white';
            customTooltipElement.style.padding = '4px 8px';
            customTooltipElement.style.borderRadius = '4px';
            customTooltipElement.style.zIndex = '10001';
            customTooltipElement.style.fontSize = '12px';
            customTooltipElement.style.fontFamily = 'Arial, sans-serif';
            customTooltipElement.style.pointerEvents = 'none';
            customTooltipElement.style.left = (event.pageX + 10) + 'px';
            customTooltipElement.style.top = (event.pageY + 10) + 'px';
            document.body.appendChild(customTooltipElement);
        });

        iconSpan.addEventListener('mousemove', function(event) {
            if (customTooltipElement) {
                customTooltipElement.style.left = (event.pageX + 10) + 'px';
                customTooltipElement.style.top = (event.pageY + 10) + 'px';
            }
        });

        iconSpan.addEventListener('mouseleave', function() {
            if (customTooltipElement) {
                customTooltipElement.remove();
                customTooltipElement = null;
            }
        });

        // Use icon queue for coordinated loading if available
        if (window.NTIconQueue && window.NTIconQueue.add) {
            window.NTIconQueue.add(() => statusField.appendChild(iconSpan));
        } else {
            statusField.appendChild(iconSpan);
        }
    }

    // =============================
    // 🟢 Fetch NT StarTrack Status
    // =============================
    function getStarTrackStatusFromResponse(response) {
        try {
            const data = JSON.parse(response.responseText || '{}');
            if (data.is_bot === true) return { color: "red", tooltip: "StarTrack: Flagged" };
            if (data.is_bot === false) return { color: "green", tooltip: "StarTrack: Not Flagged" };
            if (data.error || response.status === 404) return { color: "yellow", tooltip: "StarTrack: Untracked" };
            return { color: "gray", tooltip: "StarTrack: Unexpected Response" };
        } catch (err) {
            if (response.status === 404) return { color: "yellow", tooltip: "StarTrack: Untracked" };
            return { color: "gray", tooltip: "StarTrack: Error" };
        }
    }

    function fetchStarTrackStatusData(username) {
        const normalized = normalizeUsername(username);
        if (!normalized) {
            debugLog('StarTrack skipped (invalid username)', { username: username });
            return Promise.resolve(null);
        }

        const sharedStatus = window.NTShared.getBotStatus(normalized);
        if (sharedStatus) {
            debugLog('StarTrack cache hit (NTShared)', { username: normalized, status: sharedStatus });
            return Promise.resolve(sharedStatus);
        }

        const cacheKey = `startrack_${normalized}`;
        const cached = GM_getValue(cacheKey);
        if (cached && (Date.now() - cached.timestamp) < getStarTrackCacheDuration()) {
            debugLog('StarTrack cache hit (GM)', { username: normalized });
            return Promise.resolve({ color: cached.color, tooltip: cached.tooltip });
        }

        if (startrackInflight.has(normalized)) {
            debugLog('StarTrack dedupe hit (inflight)', { username: normalized });
            return startrackInflight.get(normalized);
        }

        const requestPromise = queueNetworkRequest(() => new Promise((resolve) => {
            const url = `http://ntstartrack.org:5001/api/isbot/${encodeURIComponent(normalized)}`;
            debugLog('StarTrack network start', { username: normalized, url: url });
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function(response) {
                    const status = getStarTrackStatusFromResponse(response);
                    debugLog('StarTrack network success', {
                        username: normalized,
                        httpStatus: response.status,
                        status: status
                    });
                    if (status && status.color !== "gray") {
                        GM_setValue(cacheKey, { color: status.color, tooltip: status.tooltip, timestamp: Date.now() });
                        window.NTShared.setBotStatus(normalized, status);
                    }
                    resolve(status);
                },
                onerror: function() {
                    debugLog('StarTrack network error', { username: normalized, url: url });
                    resolve({ color: "gray", tooltip: "StarTrack: Network Error" });
                }
            });
        }), { label: 'startrack', username: normalized }).finally(() => {
            startrackInflight.delete(normalized);
            debugLog('StarTrack inflight cleared', { username: normalized, remainingInflight: startrackInflight.size });
        });

        startrackInflight.set(normalized, requestPromise);
        return requestPromise;
    }

    function fetchStarTrackStatus(username, element) {
        return fetchStarTrackStatusData(username).then((status) => {
            if (status && status.color && status.tooltip) {
                setIcon(element, status.color, status.tooltip, "ST");
            }
        });
    }

    // =============================
    // 🟠 Fetch NTL Legacy Status
    // =============================
    function fetchNTLStatusData(username) {
        const normalized = normalizeUsername(username);
        if (!normalized) {
            debugLog('NTL skipped (invalid username)', { username: username });
            return Promise.resolve(null);
        }

        const cacheKey = `ntl_${normalized}`;
        const cached = GM_getValue(cacheKey);
        if (cached && cached.color && cached.tooltip) {
            debugLog('NTL cache hit (GM)', { username: normalized });
            return Promise.resolve({ color: cached.color, tooltip: cached.tooltip });
        }

        if (ntlInflight.has(normalized)) {
            debugLog('NTL dedupe hit (inflight)', { username: normalized });
            return ntlInflight.get(normalized);
        }

        const requestPromise = queueNetworkRequest(() => new Promise((resolve) => {
            const url = `https://ntleaderboards.com/is_user_banned/${encodeURIComponent(normalized)}`;
            debugLog('NTL network start', { username: normalized, url: url });
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function(response) {
                    if (response.status !== 200) {
                        debugLog('NTL network non-200', { username: normalized, httpStatus: response.status });
                        resolve(null);
                        return;
                    }

                    const data = response.responseText.trim();
                    let status = null;
                    if (data === "Y (ban)") {
                        status = { color: "orange", tooltip: "Legacy NTL (Banned)" };
                    } else if (data === "Y (ban+flag)") {
                        status = { color: "orange", tooltip: "Legacy NTL (Flagged)" };
                    }

                    if (status) {
                        GM_setValue(cacheKey, status); // Intentionally indefinite cache
                    }
                    debugLog('NTL network success', {
                        username: normalized,
                        httpStatus: response.status,
                        status: status
                    });
                    resolve(status);
                },
                onerror: function() {
                    debugLog('NTL network error', { username: normalized, url: url });
                    resolve(null);
                }
            });
        }), { label: 'ntl', username: normalized }).finally(() => {
            ntlInflight.delete(normalized);
            debugLog('NTL inflight cleared', { username: normalized, remainingInflight: ntlInflight.size });
        });

        ntlInflight.set(normalized, requestPromise);
        return requestPromise;
    }

    function fetchNTLStatus(username, element) {
        return fetchNTLStatusData(username).then((status) => {
            if (status && status.color && status.tooltip) {
                setIcon(element, status.color, status.tooltip, "NTL");
            }
        });
    }

    // =============================
    // 🎯 Update Status (Dual Check)
    // =============================
    async function updateStatus(element, username) {
        const normalized = normalizeUsername(username);
        if (!normalized || !element) {
            debugLog('Update skipped (missing element/username)', { username: username });
            return;
        }
        if (!shouldProcessStatusFor(element, normalized)) {
            debugLog('Update skipped (already processed)', { username: normalized });
            return;
        }

        markStatusProcessing(element, normalized);
        debugLog('Update start', { username: normalized });
        try {
            const checks = [];
            if (isNtcfgBotFlagFeatureEnabled('USE_STARTRACK')) {
                checks.push(fetchStarTrackStatus(normalized, element));
            }
            if (isNtcfgBotFlagFeatureEnabled('USE_NTL_LEGACY')) {
                checks.push(fetchNTLStatus(normalized, element));
            }
            await Promise.all(checks);
        } finally {
            markStatusProcessed(element, normalized);
            debugLog('Update complete', { username: normalized });
        }
    }

    function registerSharedBotStatusListener() {
        window.addEventListener('nt-cache-updated', (event) => {
            const detail = event && event.detail ? event.detail : null;
            if (!detail || detail.type !== 'isbot' || !detail.username || !detail.data) return;

            const status = detail.data;
            if (!status.color || !status.tooltip) return;

            const username = String(detail.username).toLowerCase();
            const taggedElements = document.querySelectorAll('[data-nt-username]');
            let updatedCount = 0;
            taggedElements.forEach((element) => {
                const taggedUsername = (element.getAttribute('data-nt-username') || '').toLowerCase();
                if (taggedUsername === username) {
                    setIcon(element, status.color, status.tooltip, "ST");
                    updatedCount += 1;
                }
            });
            if (updatedCount > 0) {
                debugLog('Shared ST update applied', { username: username, updatedElements: updatedCount });
            }
        });
    }

    // =============================
    // 🔍 Observe Main Content
    // =============================
    function observeMainContent() {
        window.NTObserverManager.register('botflag', () => {
            syncBotFlagRouteFallbacks();
            runBotFlagHandlersForCurrentRoute({ requireDomReady: true });
        });
        syncBotFlagRouteFallbacks();
    }

    function extractTeamTagFromNameContainer(nameContainer) {
        if (!nameContainer) return '';
        const teamLink = nameContainer.querySelector('a[href*="/team/"]');
        const href = teamLink?.getAttribute('href') || '';
        const match = href.match(/\/team\/([^\/?#]+)/i);
        return match ? String(match[1]).trim().toUpperCase() : '';
    }

    function isNativeBotRaceResultRow(nameContainer) {
        const row = nameContainer?.closest?.('.gridTable-row, .raceResults-body.row, .race-results .row');
        if (!row) return false;

        const statusField = row.querySelector('.tsxs.tc-fuel');
        const statusText = normalizeDisplayName(statusField?.textContent || '');
        if (/nitro type bot/i.test(statusText)) {
            return true;
        }

        const taggedCandidates = [nameContainer, statusField].filter(Boolean);
        for (let i = 0; i < taggedCandidates.length; i++) {
            const candidate = taggedCandidates[i];
            const username = normalizeUsername(
                candidate.getAttribute?.('data-nt-username')
                || candidate.getAttribute?.('data-username')
                || candidate.getAttribute?.('data-status-processed-for')
                || candidate.dataset?.ntUsername
                || candidate.dataset?.username
                || candidate.dataset?.statusProcessedFor
            );
            if (username === 'bot' || /^robot\d/i.test(username)) {
                return true;
            }
        }

        return false;
    }

    function clearRaceResultStatusState(element) {
        if (!element) return;
        clearStatusIcons(element);
        element.removeAttribute('data-status-processing');
        element.removeAttribute('data-status-processing-for');
        element.removeAttribute('data-status-processed-for');
        element.removeAttribute('data-nt-username');
        element.removeAttribute('data-botflag-processed');
    }

    function fetchTeamMembersByTag(teamTagRaw) {
        const normalizedTag = String(teamTagRaw || '').trim().toUpperCase();
        if (!normalizedTag) return Promise.resolve(null);

        const cacheKey = `team_activity_v2_${normalizedTag}`;
        const cached = GM_getValue(cacheKey);
        const TEAM_CACHE_DURATION = 300000;

        if (cached && isIdentityLookup(cached.data) && (Date.now() - cached.timestamp) < TEAM_CACHE_DURATION) {
            return Promise.resolve(cached.data);
        }

        const token = getToken();
        if (!token) return Promise.resolve(null);

        return new Promise((resolve) => {
            const teamUrl = `https://www.nitrotype.com/api/v2/teams/${encodeURIComponent(normalizedTag)}`;
            debugLog('Team activity request start', {
                teamApi: normalizedTag,
                url: teamUrl,
                cacheKey: cacheKey
            });
            GM_xmlhttpRequest({
                method: "GET",
                url: teamUrl,
                headers: {
                    "Authorization": `Bearer ${token}`,
                    "accept": "application/json, text/plain, */*"
                },
                onload: function(response) {
                    debugLog('Team activity request complete', {
                        teamApi: normalizedTag,
                        httpStatus: response.status
                    });
                    if (response.status === 200) {
                        let data;
                        try {
                            data = JSON.parse(response.responseText);
                        } catch (err) {
                            debugLog('Team activity parse error', { teamApi: normalizedTag });
                            resolve(null);
                            return;
                        }
                        if (data.status === "OK") {
                            const members = Array.isArray(data?.results?.members) ? data.results.members : [];
                            const memberLookup = createIdentityLookup(
                                members.map((member) => ({
                                    displayName: member.displayName || member.username,
                                    username: member.username,
                                    tag: normalizedTag
                                }))
                            );

                            GM_setValue(cacheKey, {
                                data: memberLookup,
                                timestamp: Date.now()
                            });

                            resolve(memberLookup);
                            return;
                        }
                    } else if (response.status === 401) {
                        getTokenAndRetry(() => fetchTeamMembersByTag(normalizedTag).then(resolve));
                        return;
                    }

                    resolve(null);
                },
                onerror: () => resolve(null)
            });
        });
    }

    // =============================
    // 👥 /team Page Handling
    // =============================
    function handleTeamPage() {
        checkUserBansTeam();
    }

    async function checkUserBansTeam() {
        const applicationsMap = await fetchTeamApplications();
        const userMap = await fetchTeamActivity();

        if (applicationsMap) {
            updateUsersTeamApplications(applicationsMap);
        }
        if (userMap) {
            updateUsersTeam(userMap);
        }
    }

    async function fetchTeamActivity() {
        try {
            const pathParts = window.location.pathname.split('/').filter(Boolean);
            const teamTagFromUrl = pathParts.length >= 2 ? pathParts[pathParts.length - 1] : '';
            const teamTag = String(teamTagFromUrl || '').trim();
            if (!teamTag) {
                debugLog('Team activity skipped (missing team tag in URL)', { pathname: window.location.pathname });
                return null;
            }
            return fetchTeamMembersByTag(teamTag);
        } catch (error) {
            return null;
        }
    }

    async function fetchTeamApplications() {
        try {
            // Check cache first (5 minute cache)
            const cacheKey = 'team_applications_v2';
            const cached = GM_getValue(cacheKey);
            const APPLICATIONS_CACHE_DURATION = 300000; // 5 minutes

            if (cached && isIdentityLookup(cached.data) && (Date.now() - cached.timestamp) < APPLICATIONS_CACHE_DURATION) {
                return cached.data;
            }

            const token = getToken();

            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: "GET",
                    url: "https://www.nitrotype.com/api/v2/teams/applications",
                    headers: {
                        "Authorization": `Bearer ${token}`,
                        "accept": "application/json, text/plain, */*"
                    },
                    onload: function(response) {
                        if (response.status === 200) {
                            let data;
                            try {
                                data = JSON.parse(response.responseText);
                            } catch (err) {
                                resolve(null);
                                return;
                            }
                            const results = Array.isArray(data?.results) ? data.results : [];
                            if (results.length > 0) {
                                const memberLookup = createIdentityLookup(
                                    results.map((member) => ({
                                        displayName: member.displayName || member.username,
                                        username: member.username,
                                        tag: member.tag || member.teamTag || member.team
                                    }))
                                );

                                // Cache the result
                                GM_setValue(cacheKey, {
                                    data: memberLookup,
                                    timestamp: Date.now()
                                });

                                resolve(memberLookup);
                            } else {
                                resolve(null);
                            }
                        } else if (response.status === 401) {
                            getTokenAndRetry(() => fetchTeamApplications().then(resolve));
                        } else {
                            resolve(null);
                        }
                    },
                    onerror: () => resolve(null)
                });
            });
        } catch (error) {
            return null;
        }
    }

    async function updateUsersTeam(userLookup) {
        // Collect all promises to wait for batch completion
        const promises = [];
        const teamTable = document.querySelector('.table.table--striped.table--selectable.table--team.table--teamOverview');
        if (!teamTable) return;

        teamTable.querySelectorAll('.table-row').forEach((row) => {
            const username = resolveUsernameForElement(row, userLookup, 'team');
            if (username) {
                const promise = updateStatus(row, username);
                if (promise) promises.push(promise);
            }
        });

        // Wait for all icon additions to complete before flushing queue
        await Promise.all(promises);
    }

    async function updateUsersTeamApplications(userLookup) {
        const promises = [];
        const allRows = document.querySelectorAll('.row.row--o.well.well--b.well--l');
        let appSection = null;

        for (const row of allRows) {
            const h3 = row.querySelector('h3.mbf');
            if (h3 && h3.textContent.includes('Pending Applications')) {
                appSection = row;
                break;
            }
        }

        if (!appSection) return;

        appSection.querySelectorAll('.table-row').forEach((row) => {
            const username = resolveUsernameForElement(row, userLookup, 'team-applications');
            if (username) {
                const promise = updateStatus(row, username);
                if (promise) promises.push(promise);
            }
        });

        await Promise.all(promises);
    }

    // =============================
    // 🏁 /racer Page Handling
    // =============================
    function handleRacerPage() {
        const username = window.location.pathname.split('/').pop();
        const el = document.querySelector('.profile-title');
        if (username && el) updateStatus(el, username);
    }

    // =============================
    // 🏎️ /race Results Handling
    // =============================
    const findReact = (dom) => {
        if (!dom) return null;
        const key = Object.keys(dom).find((k) => k.startsWith('__reactFiber$'));
        const domFiber = dom[key];
        if (!domFiber) return null;
        let parentFiber = domFiber?.return;
        while (typeof parentFiber?.type === 'string') {
            parentFiber = parentFiber?.return;
        }
        return parentFiber?.stateNode;
    };

    function handleRacePage() {
        const raceContainer = document.getElementById('raceContainer');
        if (!raceContainer) return;

        const results = raceContainer.querySelector('.race-results');
        if (!results) return;

        // Get racers from React state for username lookup
        const raceObj = findReact(raceContainer);
        const racers = raceObj?.state?.racers;
        if (!racers) return;

        const racerLookup = createIdentityLookup(
            racers.map((r) => ({
                displayName: r.profile?.displayName || r.profile?.username,
                username: r.profile?.username,
                tag: r.profile?.tag || r.profile?.team || r.profile?.teamTag || r.tag || r.team
            }))
        );
        const botUsernames = Object.create(null);
        racers.forEach(r => {
            const username = normalizeUsername(r.profile?.username || '');
            // Skip NT native bots (username "bot", starts with "robot", or title is "Nitro Type Bot")
            const isBot = username === 'bot' || /^robot\d/i.test(username) || r.profile?.title === 'Nitro Type Bot';
            if (username && isBot) botUsernames[username] = true;
        });

        const resolveRaceTarget = (nameContainer) => {
            const preferred = findPreferredRaceStatusField(nameContainer);
            if (preferred) {
                return preferred;
            }

            const resultCell = nameContainer.closest('.gridTable-cell');
            if (resultCell && results.contains(resultCell)) {
                return resultCell.querySelector('.tsxs.tc-fuel')
                    || resultCell.querySelector('.raceResults-playerName')
                    || nameContainer;
            }

            let node = nameContainer;
            while (node && node !== document.body) {
                if (node.classList?.contains('profile-title')) {
                    return node;
                }
                if (node !== nameContainer) {
                    const profileTitle = node.querySelector('.profile-title');
                    if (profileTitle) return profileTitle;

                    const titleField = node.querySelector('.tsxs.tc-fuel');
                    if (titleField) return titleField;
                }
                node = node.parentElement;
            }

            return nameContainer;
        };

        const playerNameContainers = document.querySelectorAll('.player-name--container[title]');
        playerNameContainers.forEach(nameContainer => {
            const targetEl = resolveRaceTarget(nameContainer) || nameContainer;
            if (isNativeBotRaceResultRow(nameContainer)) {
                clearRaceResultStatusState(targetEl);
                return;
            }

            const username = resolveUsernameForElement(nameContainer, racerLookup, 'race-results');
            if (!username || botUsernames[username]) return;

            if (!targetEl) return;
            if (targetEl.hasAttribute('data-botflag-processed')) return;
            targetEl.setAttribute('data-botflag-processed', '');
            targetEl.setAttribute('data-nt-username', username);

            updateStatus(targetEl, username);
        });
    }

    async function handleRacelogResultsModalPage() {
        const modal = document.querySelector('.modal--raceResults.is-active');
        if (!modal) return;

        const playerNameContainers = Array.from(modal.querySelectorAll('.player-name--container[title]'));
        if (playerNameContainers.length === 0) return;

        const pendingNameContainers = playerNameContainers.filter((nameContainer) => {
            const targetEl = findPreferredRaceStatusField(nameContainer) || nameContainer;
            return !targetEl
                || !targetEl.hasAttribute('data-botflag-processed')
                || !normalizeUsername(targetEl.getAttribute('data-nt-username') || '');
        });
        if (pendingNameContainers.length === 0) return;

        const teamTags = Array.from(new Set(
            pendingNameContainers
                .map(extractTeamTagFromNameContainer)
                .filter(Boolean)
        ));

        const teamMemberMaps = Object.create(null);
        await Promise.all(teamTags.map(async (teamTag) => {
            teamMemberMaps[teamTag] = await fetchTeamMembersByTag(teamTag);
        }));

        pendingNameContainers.forEach((nameContainer) => {
            const teamTag = extractTeamTagFromNameContainer(nameContainer);
            const resolvedUsername = extractExplicitUsernameFromElement(nameContainer)
                || ((teamTag && teamMemberMaps[teamTag])
                    ? resolveUsernameFromLookup(teamMemberMaps[teamTag], {
                        displayName: getDisplayNameFromElement(nameContainer)
                    })
                    : '');
            if (!resolvedUsername) return;

            const targetEl = findPreferredRaceStatusField(nameContainer) || nameContainer;
            if (!targetEl) return;

            const normalizedResolved = normalizeUsername(resolvedUsername);
            if (targetEl.hasAttribute('data-botflag-processed') && normalizeUsername(targetEl.getAttribute('data-nt-username')) === normalizedResolved) {
                return;
            }

            targetEl.setAttribute('data-botflag-processed', '');
            targetEl.setAttribute('data-nt-username', normalizedResolved);
            updateStatus(targetEl, resolvedUsername);
        });
    }

    function ensureRacelogResultsModalMonitor() {
        if (racelogModalMonitorTimer) return;

        const tick = () => {
            const path = window.location.pathname;
            if (!(path.startsWith('/racelog') || path.startsWith('/stats'))) {
                stopRacelogResultsModalMonitor();
                return;
            }
            if (!isNtcfgBotFlagFeatureEnabled('SHOW_RACE_RESULT_FLAGS')) {
                stopRacelogResultsModalMonitor();
                return;
            }

            const modal = document.querySelector('.modal--raceResults.is-active');
            if (!modal) {
                lastRacelogResultsModalKey = '';
                return;
            }

            const modalKey = modal.querySelector('.raceResults')?.getAttribute('data-estats-racelog-modal') || 'active';
            if (modalKey === lastRacelogResultsModalKey) {
                return;
            }

            lastRacelogResultsModalKey = modalKey;
            void handleRacelogResultsModalPage();
        };

        tick();
        racelogModalMonitorTimer = setInterval(tick, 300);
    }

    // =============================
    // 🏆 /leagues Page Handling
    // =============================
    async function handleLeaguesPage() {
        // Check cache first (5 minute cache)
        const cacheKey = 'leagues_user_activity_v2';
        const cached = GM_getValue(cacheKey);
        const LEAGUES_CACHE_DURATION = 300000; // 5 minutes

        let userMap;

        if (cached && isIdentityLookup(cached.data) && (Date.now() - cached.timestamp) < LEAGUES_CACHE_DURATION) {
            userMap = cached.data;
        } else {
            const token = getToken();
            if (!token) return;

            userMap = await new Promise((resolve) => {
                GM_xmlhttpRequest({
                    method: "GET",
                    url: "https://www.nitrotype.com/api/v2/leagues/user/activity",
                    headers: {
                        "Authorization": `Bearer ${token}`,
                        "Content-Type": "application/json"
                    },
                    onload: function(response) {
                        if (response.status === 200) {
                            let data;
                            try {
                                data = JSON.parse(response.responseText);
                            } catch (err) {
                                resolve(null);
                                return;
                            }
                            if (data.status === "OK") {
                                const standings = Array.isArray(data?.results?.standings) ? data.results.standings : [];
                                const lookup = createIdentityLookup(
                                    standings.map((u) => ({
                                        displayName: u.displayName || u.username,
                                        username: u.username,
                                        tag: u.tag || u.teamTag || u.team
                                    }))
                                );

                                // Cache the result
                                GM_setValue(cacheKey, {
                                    data: lookup,
                                    timestamp: Date.now()
                                });

                                resolve(lookup);
                            } else {
                                resolve(null);
                            }
                        } else if (response.status === 401) {
                            getTokenAndRetry(() => handleLeaguesPage());
                            resolve(null);
                        } else {
                            resolve(null);
                        }
                    },
                    onerror: () => resolve(null)
                });
            });
        }

        if (userMap) {
            await processLeagues(userMap);
        }
    }

    async function processLeagues(userMap) {
        const leaderboardRows = document.querySelectorAll('.table-row');
        const promises = [];

        leaderboardRows.forEach(row => {
            const username = resolveUsernameForElement(row, userMap, 'leagues');

            if (username) {
                promises.push(updateStatus(row, username));
            }
        });

        // Wait for all icon additions to complete
        await Promise.all(promises);
    }

    // =============================
    // 👫 /friends Page Handling (v3.1 LOGIC)
    // =============================
    function handleFriendsPage() {
        const now = Date.now();
        if (now - lastFriendsPageRunAt < FRIENDS_PAGE_RUN_COOLDOWN_MS) {
            debugLog('Friends handler skipped (cooldown)', {
                cooldownMs: FRIENDS_PAGE_RUN_COOLDOWN_MS,
                elapsedMs: now - lastFriendsPageRunAt
            });
            return;
        }
        lastFriendsPageRunAt = now;

        const activeTab = getActiveTab();
        if (!activeTab) return;
        debugLog('Friends handler run', { activeTab: activeTab });

        if (activeTab === "Friends") {
            handleFriendsTab();
        } else if (activeTab === "Requests") {
            handleRequestsTab();
        } else if (activeTab === "Search") {
            handleSearchTab();
        } else if (activeTab === "Recent") {
            handleRecentTab();
        }
    }

    function getActiveTab() {
        const activeTabElement = document.querySelector('.tab.is-active');
        if (!activeTabElement) return null;
        const bucketContent = activeTabElement.querySelector('.bucket-content');
        return bucketContent ? bucketContent.textContent.trim() : null;
    }

    function handleFriendsTab() {
        const rows = getRowsForTab();
        processFriends(rows, "Friends");
    }

    function handleRequestsTab() {
        const token = getToken();
        if (!token) return;

        GM_xmlhttpRequest({
            method: "GET",
            url: "https://www.nitrotype.com/api/v2/friend-requests",
            headers: {
                "Authorization": `Bearer ${token}`,
                "Content-Type": "application/json"
            },
            onload: function(response) {
                if (response.status === 200) {
                    const data = JSON.parse(response.responseText);
                    const requests = data.results.requests || [];
                    const requestLookup = createIdentityLookup(
                        requests.map((req) => ({
                            displayName: req.displayName || req.username,
                            username: req.username,
                            tag: req.tag || req.teamTag || req.team
                        }))
                    );

                    const rows = getRowsForTab();
                    rows.forEach(row => {
                        const username = resolveUsernameForElement(row, requestLookup, 'friend-requests');
                        if (username) {
                            updateStatus(row, username);
                        }
                    });
                } else if (response.status === 401) {
                    getTokenAndRetry(handleRequestsTab);
                }
            }
        });
    }

    function handleSearchTab() {
        const searchInput = document.querySelector('#friendsearch');
        if (!searchInput || !searchInput.value) return;

        const searchTerm = searchInput.value.trim();
        if (!searchTerm) return;

        const token = getToken();
        if (!token) return;

        GM_xmlhttpRequest({
            method: "POST",
            url: "https://www.nitrotype.com/api/v2/players/search",
            headers: {
                "Authorization": `Bearer ${token}`,
                "Content-Type": "application/x-www-form-urlencoded"
            },
            data: `term=${encodeURIComponent(searchTerm)}`,
            onload: function(response) {
                if (response.status === 200) {
                    const data = JSON.parse(response.responseText);
                    const results = data.results || [];
                    const resultLookup = createIdentityLookup(
                        results.map((r) => ({
                            displayName: r.displayName || r.username,
                            username: r.username,
                            tag: r.tag || r.teamTag || r.team
                        }))
                    );

                    const rows = getRowsForTab();
                    rows.forEach(row => {
                        const username = resolveUsernameForElement(row, resultLookup, 'friend-search');
                        if (username) {
                            updateStatus(row, username);
                        }
                    });
                } else if (response.status === 401) {
                    getTokenAndRetry(handleSearchTab);
                }
            }
        });
    }

    function handleRecentTab() {
        const rows = getRowsForTab();
        rows.forEach(row => {
            const username = extractRecentTabUsernameFromElement(row);
            if (username) {
                updateStatus(row, username);
            }
        });
    }

    // Cache for friends list - fetch once, use for all lookups
    let friendsListCache = null;
    let friendsListTimestamp = 0;
    const FRIENDS_CACHE_DURATION = 300000; // 5 minutes

    async function getFriendsList() {
        // Check cache first
        if (friendsListCache && (Date.now() - friendsListTimestamp) < FRIENDS_CACHE_DURATION) {
            return friendsListCache;
        }

        const token = getToken();
        if (!token) return null;

        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://www.nitrotype.com/api/v2/friends",
                headers: {
                    "Authorization": `Bearer ${token}`,
                    "Content-Type": "application/json"
                },
                onload: function(response) {
                    if (response.status === 200) {
                        let data;
                        try {
                            data = JSON.parse(response.responseText);
                        } catch (err) {
                            resolve(null);
                            return;
                        }
                        const fields = Array.isArray(data?.results?.fields) ? data.results.fields : [];
                        const values = Array.isArray(data?.results?.values) ? data.results.values : [];

                        const displayNameIndex = fields.indexOf("displayName");
                        const usernameIndex = fields.indexOf("username");
                        if (displayNameIndex < 0 || usernameIndex < 0) {
                            resolve(null);
                            return;
                        }

                        const friendsMap = createIdentityLookup(
                            values.map((friendData) => ({
                                displayName: friendData[displayNameIndex],
                                username: friendData[usernameIndex],
                                tag: friendData[fields.indexOf("tag")]
                            }))
                        );

                        friendsListCache = friendsMap;
                        friendsListTimestamp = Date.now();
                        resolve(friendsMap);
                    } else if (response.status === 401) {
                        getTokenAndRetry(() => getFriendsList().then(resolve));
                    } else {
                        resolve(null);
                    }
                },
                onerror: function() {
                    resolve(null);
                }
            });
        });
    }

    async function processFriends(rows, context) {
        // Fetch friends list once for all friends
        const friendsMap = await getFriendsList();
        if (!friendsMap) return;

        // Collect all promises to wait for batch completion
        const promises = [];

        // Process all friends using the cached list
        rows.forEach(row => {
            const playerElement = row.querySelector('.player-name--container');
            if (playerElement) {
                const username = resolveUsernameForElement(row, friendsMap, 'friends');
                if (username) {
                    promises.push(updateStatus(row, username));
                }
            }
        });

        // Wait for all icon additions to complete
        await Promise.all(promises);
    }

    function getRowsForTab() {
        const rows = document.querySelectorAll('.table-row');
        return Array.from(rows).filter(row => !row.querySelector('th'));
    }

    // =============================
    // 🚀 Initialize Script
    // =============================
    function startBotFlagLiveRuntime() {
        installDelegatedBotFlagViewListeners();
        registerSharedBotStatusListener();
        observeMainContent();
    }

    initDebugAPI();
    debugLog('Script loaded', { version: SCRIPT_VERSION });
    startBotFlagLiveRuntime();

})();