Nitro Type - Mod Menu

Unified Nitro Type mod menu shell at /settings/mods with route takeover, mod tabs, and race-options-inspired settings layout.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Nitro Type - Mod Menu
// @namespace    https://nitrotype.info
// @version      4.0.2
// @description  Unified Nitro Type mod menu shell at /settings/mods with route takeover, mod tabs, and race-options-inspired settings layout.
// @author       Captain.Loveridge
// @icon         https://nitrotype.info/assets/icons/nitro-type-mod-suite.png
// @match        https://www.nitrotype.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_xmlhttpRequest
// @grant        GM_info
// @connect      greasyfork.org
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    (function markNitroTypeModSuiteStandaloneRuntime() {
        try {
            const payload = {
                id: 'mod-menu',
                name: 'Mod Menu',
                source: 'userscript',
                version: typeof GM_info !== 'undefined' && GM_info?.script?.version ? String(GM_info.script.version) : '',
                manager: typeof GM_info !== 'undefined' && GM_info?.scriptHandler ? String(GM_info.scriptHandler) : '',
                seenAt: Date.now()
            };
            localStorage.setItem(`ntms:runtime:userscript:${payload.id}`, JSON.stringify(payload));
            window.dispatchEvent(new CustomEvent('ntms:runtime-marker-updated', { detail: payload }));
            document.dispatchEvent(new CustomEvent('ntms:runtime-marker-updated', { detail: payload }));
        } catch {
            // Runtime conflict detection is best-effort.
        }
    })();

    const SCRIPT_SINGLETON_KEY = '__ntModMenuSingleton';
    if (window[SCRIPT_SINGLETON_KEY]) {
        return;
    }
    window[SCRIPT_SINGLETON_KEY] = true;

    const MOD_MENU_PATH = '/settings/mods';
    const MOD_SUITE_EXTENSION_URL = 'https://chromewebstore.google.com/detail/nitro-type-mod-suite/dffhnncbpjpagpoljfianioidpajldni';
    const MOD_SUITE_LOGO_URL = 'https://nitrotype.info/assets/icons/nitro-type-mod-suite.png';
    const SCRIPT_EOL_NOTICE_KEY = 'ntmods:eol-extension-notice:2026-07';
    const SCRIPT_EOL_NOTICE_DISMISSED_KEY = `${SCRIPT_EOL_NOTICE_KEY}:dismissed`;
    const SCRIPT_EOL_NOTICE_SNOOZE_KEY = `${SCRIPT_EOL_NOTICE_KEY}:snoozeUntil`;
    const SCRIPT_EOL_NOTICE_SNOOZE_MS = 7 * 24 * 60 * 60 * 1000;
    const DROPDOWN_ITEM_CLASS = 'ntmods-dropdown-item';
    const MANIFEST_PREFIX = 'ntcfg:manifest:';
    const ALIVE_PREFIX = 'ntcfg:alive:';
    const RUNTIME_MARKER_PREFIX = 'ntms:runtime:';
    const STANDALONE_RUNTIME_PREFIX = `${RUNTIME_MARKER_PREFIX}userscript:`;
    const EXTENSION_RUNTIME_MARKER_KEY = `${RUNTIME_MARKER_PREFIX}extension:mod-suite`;
    const RUNTIME_MARKER_EVENT = 'ntms:runtime-marker-updated';
    const RUNTIME_MARKER_FRESH_TOLERANCE_MS = 5000;
    const RUNTIME_WARNING_EXCLUDED_IDS = new Set(['bot-hunter']);
    const PAGE_SESSION_START = Date.now();
    const VERSION_CACHE_KEY = 'ntmods:version-cache';
    const VERSION_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
    const BOT_HUNTER_MODULE_ID = 'bot-hunter';
    const BOT_HUNTER_INSTALL_URL = 'https://greasyfork.org/en/scripts/586784-startrack-bot-hunter';
    const BOT_HUNTER_VERSION_API_URL = 'https://greasyfork.org/en/scripts/586784.json';
    const BOT_HUNTER_VERSION_CHECK_TIMESTAMP_KEY = 'ntmods:bot-hunter-version-check-ts';
    const UI_STATE_KEY = 'ntmods:ui-state';
    const TAB_ORDER_KEY = 'ntmods:tab-order';
    const PREVIEW_VALUE_PREFIX = 'ntmods:preview:';
    const PREVIEW_FALLBACK_ENABLED = true;
    let scriptEolNoticeClosedThisVisit = false;
    let renderInProgress = false;
    let modMenuPageSyncTimer = null;
    let pendingModMenuRouteSync = false;
    let _lastAliveSnapshot = '';
    let globalResetState = null;
    let modMenuRouteGuardObserver = null;
    let modMenuRouteGuardTimer = null;

    // ── Anti-flicker curtain (injected unconditionally, gated by class) ──
    // Matches the leaderboard script pattern: CSS is always present on every page,
    // but only activates when html.is-mods-route is set. This ensures the curtain
    // is already loaded for SPA navigations, not just fresh page loads.
    const curtainEl = document.createElement('style');
    curtainEl.id = 'ntmods-curtain';
    curtainEl.textContent = `
        html.is-mods-route main.structure-content {
            opacity: 0 !important;
            visibility: hidden !important;
        }
        html.is-mods-route main.structure-content.custom-loaded {
            opacity: 1 !important;
            visibility: visible !important;
            transition: opacity 0.15s ease-in;
        }
    `;
    (document.head || document.documentElement).appendChild(curtainEl);

    // If already on the mods route at document-start, activate the curtain immediately
    if (location.pathname.replace(/\/+$/, '') === MOD_MENU_PATH) {
        document.documentElement.classList.add('is-mods-route');
    }

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

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

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

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

            wrapHistoryMethod('pushState');
            wrapHistoryMethod('replaceState');
            hostWindow.addEventListener('popstate', () => queueMicrotask(() => notify('popstate')));
            helper.__ntRouteHelperWrapped = true;
        }

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

        hostWindow.NTRouteHelper = helper;
        return helper;
    }

    const toggleField = (key, label, defaultValue, help) => ({
        type: 'toggle',
        key,
        label,
        default: defaultValue,
        help
    });

    const numberField = (key, label, defaultValue, help, min = null, max = null, step = '1') => ({
        type: 'number',
        key,
        label,
        default: defaultValue,
        help,
        min,
        max,
        step
    });

    const selectField = (key, label, defaultValue, options, help) => ({
        type: 'select',
        key,
        label,
        default: defaultValue,
        options,
        help
    });

    const textField = (key, label, defaultValue, help, placeholder = '') => ({
        type: 'text',
        key,
        label,
        default: defaultValue,
        help,
        placeholder
    });

    const colorField = (key, label, defaultValue, help) => ({
        type: 'color',
        key,
        label,
        default: defaultValue,
        help
    });

    const noteField = (message, tone = 'info') => ({
        type: 'note',
        message,
        tone
    });

    const actionField = (key, label, style = 'primary', help = '') => ({
        type: 'action',
        key,
        label,
        style,
        help
    });

    const MODULE_LIBRARY = [
        {
            id: 'race-options',
            label: 'Race Options',
            shortLabel: 'RO',
            accent: '#1c99f4',
            description: 'Race overlays, pacing tools, and theme controls for the race screen.',
            installUrl: 'https://greasyfork.org/en/scripts/567849-nitro-type-race-options',
            previewSections: [
                {
                    id: 'general',
                    title: 'General',
                    subtitle: 'Visual cleanup, overlays, and race helpers.',
                    items: [
                        toggleField('hide_track', 'Hide Track', false, 'Removes the large race track art to keep the page cleaner.'),
                        toggleField('hide_notifications', 'Hide Notifications During Race', true, 'Suppresses popups while a race is active.'),
                        toggleField('enable_mini_map', 'Enable Mini Map', false, 'Shows a compact lane map near the race text.'),
                        selectField('mini_map_position', 'Mini Map Position', 'bottom', [
                            { value: 'bottom', label: 'Bottom' },
                            { value: 'top', label: 'Top' }
                        ], 'Choose where the mini map sits relative to the race UI.'),
                        { ...toggleField('enable_racer_badges_in_race', 'Enable Racer Badges (In-Race)', true, 'Lets racer badges inject race nameplate icons.'), storageKey: 'ntcfg:racer-badges:ENABLE_RACE_BADGES', emitModule: 'racer-badges', emitKey: 'ENABLE_RACE_BADGES' },
                        noteField('This section is a visual shell right now. The live bridge will replace these preview keys with shared mod settings.', 'info')
                    ]
                },
                {
                    id: 'pacing',
                    title: 'Pacing',
                    subtitle: 'Alt WPM and reload flow.',
                    items: [
                        toggleField('enable_alt_wpm_counter', 'Enable Alt. WPM / Countdown', true, 'Displays the draggable pace helper during the race.'),
                        numberField('target_wpm', 'Target WPM', 79.5, 'Target pace used by the helper.', 1, 300, '0.1'),
                        numberField('indicate_wpm_within', 'Yellow Threshold', 2, 'Highlight the helper when pace is within this range.', 0, 30, '0.1'),
                        toggleField('reload_on_stats', 'Enable Auto Reload', true, 'Refresh after the results screen updates.'),
                        numberField('greedy_stats_reload_int', 'Fast Reload Interval (ms)', 50, 'Low values reload sooner but can be heavier.', 25, 500, '5'),
                        actionField('preview_reload_action', 'Preview Save and Reload', 'primary', 'A placeholder action so the footer/button styling can be reviewed.')
                    ]
                },
                {
                    id: 'theme',
                    title: 'Theme',
                    subtitle: 'Text colors, backgrounds, and typing feel.',
                    items: [
                        toggleField('theme_enable_dark_mode', 'Enable Dark Mode', false, 'Uses the darker theme palette on the race page.'),
                        colorField('theme_color_background', 'Background Color', '#0A121E', 'Main background color used for text theming.'),
                        colorField('theme_color_background_active', 'Active Word Background', '#1C99F4', 'Background color for the active word.'),
                        colorField('theme_color_foreground', 'Foreground Color', '#E7EEF8', 'Main text color for upcoming characters.'),
                        selectField('theme_font_family_preset', 'Font Family', 'roboto_mono', [
                            { value: 'roboto_mono', label: 'Roboto Mono' },
                            { value: 'montserrat', label: 'Montserrat' },
                            { value: 'space_mono', label: 'Space Mono' }
                        ], 'Preview typography for the race text.'),
                        toggleField('theme_enable_rainbow_typed_text', 'Rainbow Typed Text', false, 'Applies animated color cycling to typed text.')
                    ]
                }
            ]
        },
        {
            id: 'racer-badges',
            label: 'Racer Badges',
            shortLabel: 'RB',
            accent: '#ffd166',
            description: 'Top racer and top team badge placement, in-race badge rules, and badge presentation.',
            installUrl: 'https://greasyfork.org/en/scripts/555617-nitro-type-top-racer-team-badges',
            previewSections: [
                {
                    id: 'general',
                    title: 'General',
                    subtitle: 'Global badge and banner visibility controls.',
                    items: [
                        toggleField('SHOW_RACER_BADGES', 'Show Racer Badges', true, 'Show small inline top-racer ranking icons next to names in lists.'),
                        toggleField('SHOW_RACER_BANNERS', 'Show Racer Banners', true, 'Show large top-racer ranking images on profile and garage pages.'),
                        toggleField('SHOW_TEAM_BADGES', 'Show Team Badges', true, 'Show small inline top-team ranking icons next to names in lists.'),
                        toggleField('SHOW_TEAM_BANNERS', 'Show Team Banners', true, 'Show large top-team ranking images on team, profile, and garage pages.'),
                        noteField('Install the Racer Badges script for these settings to take effect.', 'info')
                    ]
                },
                {
                    id: 'pages',
                    title: 'Pages',
                    subtitle: 'Per-page badge visibility.',
                    items: [
                        toggleField('ENABLE_RACE_BADGES', 'Enable Race Page', true, 'Show badge overlays inside the race view on racer nameplates.'),
                        toggleField('ENABLE_PROFILE_BADGES', 'Enable Profile Pages', true, 'Show badges and banners on racer profile pages.'),
                        toggleField('ENABLE_TEAM_PAGE_BADGES', 'Enable Team Pages', true, 'Show badges and banners on team pages.'),
                        toggleField('ENABLE_GARAGE_BADGES', 'Enable Garage Pages', true, 'Show badges and banners on garage pages.'),
                        toggleField('ENABLE_FRIENDS_BADGES', 'Enable Friends Page', true, 'Show badges on the friends page.'),
                        toggleField('ENABLE_LEAGUES_BADGES', 'Enable Leagues Page', true, 'Show badges inside league tables.'),
                        toggleField('ENABLE_LEAGUES_TEAM_BADGES', 'Enable Team Tab League Badges', true, 'Show top-team badges on the Team tab of the leagues page, even if global team badges are disabled.'),
                        toggleField('ENABLE_HEADER_BADGE', 'Enable Header Badge', true, 'Display your own badge in the site header.'),
                        noteField('Install the Racer Badges script for these settings to take effect.', 'info')
                    ]
                },
                {
                    id: 'advanced',
                    title: 'Advanced',
                    subtitle: 'Debug and diagnostic controls.',
                    items: [
                        toggleField('DEBUG_LOGGING', 'Debug Logging', false, 'Enable verbose console logging for troubleshooting.'),
                        noteField('Install the Racer Badges script for these settings to take effect.', 'info')
                    ]
                }
            ]
        },
        {
            id: 'leaderboards',
            label: 'Leaderboards',
            shortLabel: 'LB',
            accent: '#6c8cff',
            description: 'StarTrack leaderboard routing, cached timeframes, and background refresh behavior.',
            installUrl: 'https://greasyfork.org/en/scripts/555066-nitro-type-startrack-leaderboard-integration',
            previewSections: [
                {
                    id: 'views',
                    title: 'Views',
                    subtitle: 'Top-level presentation and default landing behavior.',
                    items: [
                        selectField('default_view', 'Default View', 'individual', [
                            { value: 'individual', label: 'Top Racers' },
                            { value: 'team', label: 'Top Teams' }
                        ], 'Controls which leaderboard view opens first.'),
                        selectField('default_timeframe', 'Default Timeframe', 'season', [
                            { value: 'season', label: 'Season' },
                            { value: '24hr', label: 'Last 24 Hours' },
                            { value: '7day', label: 'Last 7 Days' }
                        ], 'Preview the default timeframe selection.'),
                        toggleField('highlight_position_change', 'Highlight Position Change', true, 'Show movement arrows beside leaderboard entries.'),
                        toggleField('manual_refresh_button', 'Show Manual Refresh Button', true, 'Keep the refresh control visible in the page header.')
                    ]
                },
                {
                    id: 'sync',
                    title: 'Sync',
                    subtitle: 'Refresh cadence and cache freshness.',
                    items: [
                        toggleField('daily_background_sync', 'Daily Background Sync', true, 'Warm cache data for daily-style ranges.'),
                        toggleField('hourly_background_sync', 'Hourly Background Sync', true, 'Refresh fast-moving windows in the background.'),
                        numberField('cache_duration_minutes', 'Cache Duration (minutes)', 15, 'General cache freshness for rolling windows.', 1, 120, '1'),
                        actionField('preview_resync', 'Preview Sync All Tabs', 'primary', 'Placeholder for a future cross-tab refresh action.')
                    ]
                },
                {
                    id: 'presentation',
                    title: 'Presentation',
                    subtitle: 'Spacing, motion, and accent treatment.',
                    items: [
                        colorField('accent_color', 'Accent Color', '#1C99F4', 'Used by route highlights and selected pills.'),
                        toggleField('anti_flicker_mode', 'Anti-Flicker Route Mode', true, 'Hide the default page shell until the custom page is ready.'),
                        toggleField('show_route_tab', 'Show Leaderboards Nav Tab', true, 'Controls the custom nav insertion.'),
                        noteField('The mods page is borrowing this script\'s route takeover pattern for its own shell.', 'info')
                    ]
                }
            ]
        },
        {
            id: 'bot-flag',
            label: 'Bot Flag',
            shortLabel: 'BF',
            accent: '#ff9f1c',
            description: 'StarTrack + NTL flag display controls, cache behavior, and status indicators.',
            installUrl: 'https://greasyfork.org/en/scripts/529360-nitro-type-flag-check-startrack-ntl-legacy',
            previewSections: [
                {
                    id: 'display',
                    title: 'Display',
                    subtitle: 'Visual placement and on-page behavior.',
                    items: [
                        toggleField('show_team_page_flags', 'Show Team Page Flags', true, 'Render badges on team rosters and member lists.'),
                        toggleField('show_friends_page_flags', 'Show Friends Page Flags', true, 'Keep friend page annotations enabled.'),
                        toggleField('show_league_page_flags', 'Show League Page Flags', true, 'Display status icons inside league tables.'),
                        selectField('tooltip_style', 'Tooltip Style', 'rich', [
                            { value: 'rich', label: 'Rich Tooltip' },
                            { value: 'compact', label: 'Compact Tooltip' }
                        ], 'Preview how detailed each hover card should be.')
                    ]
                },
                {
                    id: 'sources',
                    title: 'Sources',
                    subtitle: 'Data source preferences and fetch policy.',
                    items: [
                        toggleField('use_startrack', 'Use StarTrack Data', true, 'Primary flag source for status checks.'),
                        toggleField('use_ntl_legacy', 'Use NTL Legacy Data', true, 'Fallback source for additional history.'),
                        numberField('network_concurrency_limit', 'Network Concurrency Limit', 6, 'Soft cap for simultaneous user lookups.', 1, 20, '1'),
                        toggleField('debug_logging', 'Debug Logging', false, 'Enable verbose logging for troubleshooting.')
                    ]
                },
                {
                    id: 'cache',
                    title: 'Cache',
                    subtitle: 'How long flag results should be kept around.',
                    items: [
                        numberField('startrack_cache_days', 'StarTrack Cache (days)', 7, 'How long StarTrack lookups should be considered fresh.', 1, 30, '1'),
                        toggleField('cross_tab_sync', 'Cross-Tab Sync', true, 'Broadcast fresh data to other open Nitro Type tabs.'),
                        actionField('clear_flag_cache', 'Preview Clear Cache', 'danger', 'Placeholder action button for the maintenance area.'),
                        noteField('Cache maintenance actions are visual-only in this shell pass.', 'warning')
                    ]
                }
            ]
        },
        {
            id: 'music-player',
            label: 'Music Player',
            shortLabel: 'MP',
            accent: '#1db954',
            description: 'Universal music playback shell for Spotify, YouTube, and Apple Music sources.',
            installUrl: 'https://greasyfork.org/en/scripts/567896-nitro-type-universal-music-player',
            previewSections: [
                {
                    id: 'queue',
                    title: 'Queue',
                    subtitle: 'Mix and match saved sources into one queue.',
                    items: [
                        textField('source_url', 'Add Source URL', '', 'Paste a Spotify, YouTube, or Apple Music URL to add it to the saved sources.', 'Paste playlist, album, or track URL'),
                        selectField('queue_mode', 'Queue Mode', 'shuffle', [
                            { value: 'shuffle', label: 'Shuffle' },
                            { value: 'sequential', label: 'Sequential' }
                        ], 'Controls how tracks are ordered during playback.')
                    ]
                },
                {
                    id: 'playback',
                    title: 'Playback',
                    subtitle: 'Platform, session, and in-race control feel.',
                    items: [
                        selectField('default_platform', 'Default Platform', 'spotify', [
                            { value: 'spotify', label: 'Spotify' },
                            { value: 'youtube', label: 'YouTube' },
                            { value: 'apple-music', label: 'Apple Music' }
                        ], 'Controls the label styling and initial queue hint when no source is configured.'),
                        toggleField('session_resume', 'Resume Last Session', true, 'Restores queue position and shuffle mode after reload.'),
                        toggleField('auto_activate', 'Auto Activate on Race Load', true, 'Immediately bring the player shell online on race pages.'),
                        toggleField('show_album_art', 'Show Album Art', true, 'Display album cover artwork in the inline now-playing card.'),
                        toggleField('mute_native_station', 'Mute Native Nitro Type Station', true, 'Silence the built-in race station when this player is active.'),
                        numberField('progress_tick_ms', 'Progress Tick (ms)', 1000, 'How often the inline player UI refreshes progress text.', 250, 5000, '250')
                    ]
                },
                {
                    id: 'advanced',
                    title: 'Advanced',
                    subtitle: 'Debug and diagnostic controls.',
                    items: [
                        toggleField('debug_logging', 'Debug Logging', false, 'Enable verbose console logging for troubleshooting.')
                    ]
                }
            ]
        },
        {
            id: 'bot-hunter',
            label: 'Bot Hunter',
            shortLabel: 'BH',
            accent: '#d62f3a',
            description: 'Bot Hunter observation controls, observer identity, and pattern-analysis presentation.',
            hiddenUnlessInstalled: true,
            installUrl: BOT_HUNTER_INSTALL_URL,
            externalCompanion: true,
            previewSections: [
                {
                    id: 'general',
                    title: 'General',
                    subtitle: 'Main enablement and capture behavior.',
                    items: [
                        toggleField('enabled', 'Enable Bot Hunter', true, 'Temporarily toggles Bot Hunter capture on and off.'),
                        toggleField('self_observe_mode', 'Self Observe Mode', true, 'Includes your own races in the payload for testing.'),
                        toggleField('show_floating_toggle', 'Show Floating Toggle', true, 'Keeps the in-page Bot Hunter button visible.'),
                        toggleField('enable_logging', 'Enable Logging', true, 'Writes Bot Hunter diagnostics to the console.')
                    ]
                },
                {
                    id: 'observer',
                    title: 'Observer',
                    subtitle: 'Observer identity and payload metadata.',
                    items: [
                        textField('observer_id', 'Observer ID', 'generated-on-demand', 'Visual placeholder for the observer identity field.', 'Observer UUID'),
                        textField('api_endpoint', 'API Endpoint', 'https://ntstartrack.org/api/bot-observations', 'Current destination for submitted observations.'),
                        toggleField('use_keepalive', 'Use Keepalive on Exit', true, 'Send pending data during page unload or route exit.'),
                        noteField('This tab is ideal for a later "copy observer ID" button once behavior wiring starts.', 'info')
                    ]
                },
                {
                    id: 'analysis',
                    title: 'Analysis',
                    subtitle: 'Pattern heuristics and suspicious-race review.',
                    items: [
                        toggleField('detect_1000ms_pattern', 'Detect 1000ms Pattern', true, 'Flag repeated speed changes near one-second boundaries.'),
                        toggleField('detect_excessive_smoothing', 'Detect Excessive Smoothing', true, 'Flag unnaturally smooth speed transitions.'),
                        numberField('boundary_tolerance_ms', 'Boundary Tolerance (ms)', 100, 'Tolerance used when checking one-second boundaries.', 25, 250, '5'),
                        actionField('preview_recent_observations', 'Preview Recent Observations', 'secondary', 'Layout-only action card for a future review drawer.')
                    ]
                }
            ]
        },
        {
            id: 'toolkit',
            label: 'Toolkit',
            shortLabel: 'TK',
            accent: '#36d399',
            description: 'Quality-of-life utilities: garage organizer, shop leaks, cash hiding, keyboard shortcuts & more.',
            installUrl: 'https://greasyfork.org/en/scripts/570637-nitro-type-toolkit',
            previewSections: [
                {
                    id: 'garage',
                    title: 'Garage',
                    subtitle: 'Garage section management and display options.',
                    items: [
                        toggleField('ENABLE_GARAGE_ORGANIZER', 'Garage Section Organizer', true, 'Add a button on the garage page to set the number of visible garage sections (pages of 30 cars).'),
                        actionField('GARAGE_ORGANIZER_ACTION', 'Open Garage Organizer', 'primary', 'Set the number of garage sections directly from the mod menu.'),
                        toggleField('HIDE_BUY_CASH', 'Hide Buy Cash Button', false, 'Hide the buy cash button in the Garage.')
                    ]
                },
                {
                    id: 'profile',
                    title: 'Profile',
                    subtitle: 'Profile icon, cash formatting, and donation links.',
                    items: [
                        toggleField('ENABLE_CAR_ICON', 'Car Profile Icon', false, 'Replace the green profile icon in the top-right with your current car.'),
                        toggleField('ENABLE_SEND_CASH_FORMAT', 'Readable Cash Amounts', true, 'Format large numbers with commas in the Send Cash modal.'),
                        selectField('DONATION_LINK_STYLE', 'Donation Profile Link', 'username', [
                            { value: 'off', label: 'Off' },
                            { value: 'username', label: 'Username Only' },
                            { value: 'full_url', label: 'Full URL (nitrotype.com/racer/username)' }
                        ], 'Show a clickable link to the donor\'s profile in cash gift modals.'),
                        toggleField('DONATION_LINK_NEW_TAB', 'Open Donation Link in New Tab', false, 'Open the donor profile link in a new browser tab instead of redirecting.')
                    ]
                },
                {
                    id: 'team',
                    title: 'Team',
                    subtitle: 'Team member labels and MOTD tools.',
                    items: [
                        toggleField('ENABLE_BANNED_LABELS', 'Banned/Warned Labels', true, 'Show ban/warn status badges on team member lists and the team header.'),
                        toggleField('ENABLE_MOTD_FILTER', 'MOTD Offensive Filter', true, 'Pre-check MOTD text for possibly offensive words before saving.'),
                        textField('CUSTOM_MOTD_WORDS', 'Custom Filter Words', '', 'Additional words to flag, separated by commas.', 'word1, word2, word3')
                    ]
                },
                {
                    id: 'shop',
                    title: 'Shop',
                    subtitle: 'Shop page previews and features.',
                    items: [
                        toggleField('ENABLE_SHOP_LEAKS', 'Shop Leaks', true, 'Show a preview of tomorrow\'s featured and daily shop items on the shop page.'),
                        noteField('Shop notification options (Hide Shop Notifications, Smart Shop Notifications) are available in the Notifications tab.', 'info')
                    ]
                },
                {
                    id: 'appearance',
                    title: 'Appearance',
                    subtitle: 'Hide or show page elements.',
                    items: [
                        toggleField('HIDE_SEASON_BANNER', 'Hide Season Banner', false, 'Hide the seasonal event banner at the top of the page.'),
                        toggleField('HIDE_ADS', 'Hide Ads', false, 'Hide ad containers on the page.'),
                        toggleField('HIDE_FOOTER', 'Hide Footer', false, 'Hide the page footer.'),
                        toggleField('HIDE_ALT_LOGINS', 'Hide Alternative Login Options', false, 'Hide third-party login options on the login and race pages.'),
                        toggleField('HIDE_CASH_DISPLAY', 'Hide Cash Display', false, 'Hide your Nitro Cash balance across all pages.'),
                        selectField('HIDE_CASH_MODE', 'Cash Display Mode', 'hidden', [
                            { value: 'hidden', label: 'Hidden (blank space)' },
                            { value: 'redacted', label: 'REDACTED' },
                            { value: 'stars', label: '*******' },
                            { value: 'custom', label: 'Custom Message' }
                        ], 'How to hide the cash amount.'),
                        toggleField('HIDE_CASH_TOTAL_SPENT', 'Hide Total Spent', false, 'Also hide the Total Spent value on the stats page.')
                    ]
                },
                {
                    id: 'notifications',
                    title: 'Notifications',
                    subtitle: 'Control which navigation bar notification badges are visible.',
                    items: [
                        toggleField('HIDE_NOTIFY_ALL', 'Disable All Notifications', false, 'Hide all notification badges on the navigation bar.'),
                        toggleField('HIDE_NOTIFY_SHOP', 'Hide Shop Notifications', false, 'Hide the notification badge on the Shop nav item.'),
                        toggleField('SMART_SHOP_NOTIFY', 'Smart Shop Notifications', false, 'Only show the Shop notification when there are unowned items in the current daily rotation.'),
                        toggleField('HIDE_NOTIFY_FRIENDS', 'Hide Friends Notifications', false, 'Hide the notification badge on the Friends nav item.'),
                        toggleField('HIDE_FRIEND_ONLINE_POPUPS', 'Hide Friend Online Popups', false, 'Hide the bottom-right "Your friend is online!" popup notification.'),
                        toggleField('HIDE_NOTIFY_TEAM', 'Hide Team Notifications', false, 'Hide the notification badge on the Team nav item.'),
                        toggleField('HIDE_NOTIFY_NEWS', 'Hide News Notifications', false, 'Hide the notification badge on the News nav item.'),
                        toggleField('HIDE_NOTIFY_ACHIEVEMENTS', 'Hide Achievements Notifications', false, 'Hide the notification badge on the Achievements nav item.')
                    ]
                },
                {
                    id: 'shortcuts',
                    title: 'Keyboard Shortcuts',
                    subtitle: 'Quick navigation with customizable shortcuts.',
                    items: [
                        toggleField('ENABLE_KEYBOARD_SHORTCUTS', 'Keyboard Shortcuts', true, 'Quick page navigation with modifier+key shortcuts.'),
                        noteField('Install the Toolkit script to customize shortcut key mappings.', 'info')
                    ]
                },
                {
                    id: 'advanced',
                    title: 'Advanced',
                    subtitle: 'Debug and diagnostic controls.',
                    items: [
                        toggleField('DEBUG_LOGGING', 'Debug Logging', false, 'Enable verbose Toolkit console logging for troubleshooting.')
                    ]
                }
            ]
        },
        {
            id: 'enhanced-stats',
            label: 'Stats',
            shortLabel: 'ES',
            accent: '#f3a81b',
            description: 'Race and racer data analytics: hidden stats, WPM curves, enhanced racelog, league calculators, and more.',
            installUrl: 'https://greasyfork.org/en/scripts/570658-nitro-type-enhanced-stats',
            previewSections: [
                {
                    id: 'racer',
                    title: 'Racer Profile',
                    subtitle: 'Session tracking, hidden stats, and garage info.',
                    items: [
                        toggleField('ENABLE_SESSION_COUNTER', 'Session Race Counter', false, 'Show a persistent race counter next to your profile dropdown.'),
                        numberField('SESSION_INACTIVITY_MINUTES', 'Inactivity Reset (minutes)', 30, 'Auto-reset session counter after this many minutes of inactivity.', 5, 120, '5'),
                        toggleField('ENABLE_HIDDEN_STATS', 'Hidden Racer Stats', true, 'Show hidden stats (Nitros Used, Cars Owned, Longest Session) on racer profiles.'),
                        toggleField('ENABLE_GARAGE_CAR_COUNT', 'Garage Car Count', true, 'Show total car count next to the "Cars" heading on the garage page.')
                    ]
                },
                {
                    id: 'race',
                    title: 'Race',
                    subtitle: 'In-race analytics and post-race breakdowns.',
                    items: [
                        toggleField('ENABLE_RACE_ENHANCEMENTS', 'Race Result Enhancements', true, 'Show season points and skipped characters on the post-race scoreboard.'),
                        toggleField('ENABLE_WPM_CURVE', 'Post-Race Graph', true, 'Show a Graph button on race results to view WPM, accuracy, and nitro usage over time.')
                    ]
                },
                {
                    id: 'stats',
                    title: 'Stats Page',
                    subtitle: 'Enhanced overview, racelog, and hidden account data.',
                    items: [
                        toggleField('ENABLE_ENHANCED_STATS_PAGE', 'Enhanced Stats Overview', true, 'Inject additional statistics from your account data into the stats page overview boxes and summary table.'),
                        toggleField('ENABLE_ENHANCED_RACELOG', 'Enhanced Racelog', true, 'Add extra columns, corrected stats, and session time estimates to the racelog.')
                    ]
                },
                {
                    id: 'leagues',
                    title: 'Leagues',
                    subtitle: 'League calculators and tournament stats.',
                    items: [
                        toggleField('ENABLE_LEAGUE_CALCULATOR', 'XP-Races Calculator', true, 'Show how many races needed to beat each league player.'),
                        toggleField('ENABLE_TOURNAMENT_WINS', 'Tournament Wins', true, 'Show personal and team tournament win counts on the leagues page.')
                    ]
                }
            ]
        }
    ];

    const MODULE_META = Object.create(null);
    MODULE_LIBRARY.forEach((module) => {
        MODULE_META[module.id] = module;
    });

    const MODULE_SECTION_OVERRIDES = {
        'race-options': {
            'perfect-nitros': {
                preview: { type: 'perfect-nitro' }
            },
            'theme': {
                layout: 'compact',
                preview: { type: 'theme' }
            }
        }
    };

    function applyModuleSectionOverrides(moduleId, sections) {
        const sectionOverrides = MODULE_SECTION_OVERRIDES[moduleId];
        if (!sectionOverrides || !Array.isArray(sections) || sections.length === 0) return sections;
        return sections.map((section) => {
            const override = sectionOverrides[section.id];
            if (!override) return section;
            return Object.assign({}, section, override);
        });
    }

    const RACE_OPTIONS_THEME_FONT_FAMILY_DEFAULT_CSS = '"Roboto Mono", "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace';
    const RACE_OPTIONS_THEME_FONT_WEIGHT_DEFAULT = 400;
    const RACE_OPTIONS_THEME_DARK_MODE_FOREGROUND = '#E7EEF8';
    const RACE_OPTIONS_THEME_DARK_MODE_FOREGROUND_ACTIVE = '#101623';
    const RACE_OPTIONS_THEME_DARK_MODE_FOREGROUND_TYPED = '#8FA3BA';
    const RACE_OPTIONS_THEME_DARK_MODE_BACKGROUND = '#0A121E';
    const RACE_OPTIONS_THEME_DARK_MODE_BACKGROUND_ACTIVE = '#1C99F4';
    const RACE_OPTIONS_THEME_DARK_MODE_BACKGROUND_INCORRECT = '#D62F3A';
    const RACE_OPTIONS_THEME_FONT_FAMILY_PRESETS = [
        { value: '__default__', label: 'Default', css: RACE_OPTIONS_THEME_FONT_FAMILY_DEFAULT_CSS },
        { value: 'roboto_mono', label: 'Roboto Mono', css: '"Roboto Mono", "Courier New", Courier, monospace' },
        { value: 'montserrat', label: 'Montserrat', css: '"Montserrat", "Helvetica Neue", Helvetica, Arial, sans-serif' },
        { value: 'poppins', label: 'Poppins', css: '"Poppins", "Segoe UI", Tahoma, sans-serif' },
        { value: 'nunito', label: 'Nunito', css: '"Nunito", "Trebuchet MS", sans-serif' },
        { value: 'oswald', label: 'Oswald', css: '"Oswald", "Arial Narrow", sans-serif' },
        { value: 'space_mono', label: 'Space Mono', css: '"Space Mono", "Courier New", monospace' }
    ];
    const RACE_OPTIONS_THEME_FONT_SIZE_PRESETS = [
        { value: '__default__', label: 'Default', px: 18 },
        { value: '8', label: '8px', px: 8 },
        { value: '10', label: '10px', px: 10 },
        { value: '12', label: '12px', px: 12 },
        { value: '14', label: '14px', px: 14 },
        { value: '16', label: '16px', px: 16 },
        { value: '18', label: '18px', px: 18 },
        { value: '20', label: '20px', px: 20 },
        { value: '24', label: '24px', px: 24 },
        { value: '28', label: '28px', px: 28 },
        { value: '32', label: '32px', px: 32 },
        { value: '36', label: '36px', px: 36 }
    ];
    // Section defaults are now computed from item metadata by mountGenericSection.

    function normalizePath(pathname) {
        if (!pathname || pathname === '/') return '/';
        return pathname.replace(/\/+$/, '') || '/';
    }

    function isModMenuRoute(pathname = window.location.pathname) {
        return normalizePath(pathname) === MOD_MENU_PATH;
    }

    function escapeHtml(value) {
        return String(value ?? '')
            .replace(/&/g, '&')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;');
    }

    function titleCaseKey(value) {
        return String(value || '')
            .replace(/[_-]+/g, ' ')
            .replace(/\s+/g, ' ')
            .trim()
            .replace(/\b\w/g, (m) => m.toUpperCase());
    }

    function slugify(value) {
        return String(value || '')
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, '-')
            .replace(/^-+|-+$/g, '') || 'section';
    }

    function readJson(key, fallback = null) {
        try {
            const raw = localStorage.getItem(key);
            if (raw == null) return fallback;
            return JSON.parse(raw);
        } catch {
            return fallback;
        }
    }

    function isFreshRuntimeMarker(marker) {
        const seenAt = Number(marker?.seenAt || 0);
        return Number.isFinite(seenAt) && seenAt >= (PAGE_SESSION_START - RUNTIME_MARKER_FRESH_TOLERANCE_MS);
    }

    function isExtensionRuntimeActive() {
        const marker = readJson(EXTENSION_RUNTIME_MARKER_KEY, null);
        return marker?.source === 'extension' && isFreshRuntimeMarker(marker);
    }

    function getStandaloneRuntimeDetections() {
        if (!isExtensionRuntimeActive()) return [];
        const detections = [];
        const seen = new Set();
        try {
            Object.keys(localStorage).forEach((key) => {
                if (!key.startsWith(STANDALONE_RUNTIME_PREFIX)) return;
                const marker = readJson(key, null);
                const id = String(marker?.id || key.slice(STANDALONE_RUNTIME_PREFIX.length)).trim();
                if (!id || RUNTIME_WARNING_EXCLUDED_IDS.has(id) || seen.has(id)) return;
                if (marker?.source !== 'userscript' || !isFreshRuntimeMarker(marker)) return;
                seen.add(id);
                detections.push({
                    id,
                    name: marker.name || MODULE_META[id]?.label || titleCaseKey(id),
                    manager: marker.manager || '',
                    version: marker.version || ''
                });
            });
        } catch {
            return [];
        }
        const orderIndex = new Map(MODULE_LIBRARY.map((module, index) => [module.id, index]));
        detections.sort((a, b) => {
            const left = orderIndex.has(a.id) ? orderIndex.get(a.id) : Number.MAX_SAFE_INTEGER;
            const right = orderIndex.has(b.id) ? orderIndex.get(b.id) : Number.MAX_SAFE_INTEGER;
            if (left !== right) return left - right;
            return String(a.name).localeCompare(String(b.name));
        });
        return detections;
    }

    function readCanonicalValue(key) {
        if (typeof GM_getValue !== 'function') return undefined;
        try {
            return GM_getValue(key);
        } catch {
            return undefined;
        }
    }

    function writeCanonicalValue(key, value) {
        if (typeof GM_setValue !== 'function') return;
        try {
            GM_setValue(key, value);
        } catch {
            // ignore storage errors
        }
    }

    function deleteCanonicalValue(key) {
        if (typeof GM_deleteValue !== 'function') return;
        try {
            GM_deleteValue(key);
        } catch {
            // ignore storage errors
        }
    }

    function readPersistentJson(key, fallback = null) {
        const canonical = readCanonicalValue(key);
        if (canonical !== undefined) return canonical;
        const legacy = readJson(key, fallback);
        if (legacy !== null && legacy !== undefined) {
            writeCanonicalValue(key, legacy);
        }
        return legacy;
    }

    function writePersistentJson(key, value) {
        writeCanonicalValue(key, value);
        writeJson(key, value);
    }

    function removePersistentJson(key) {
        deleteCanonicalValue(key);
        try {
            localStorage.removeItem(key);
        } catch {
            // ignore storage errors
        }
    }

    function writeJson(key, value) {
        try {
            localStorage.setItem(key, JSON.stringify(value));
        } catch {
            // ignore storage errors
        }
    }

    // Returns: 'alive' | 'outdated' | 'stale'
    // Simple 2-outcome status: 'alive' or 'outdated'.
    // Alive = alive key was written during THIS page session (timestamp >= PAGE_SESSION_START).
    // Outdated = no key, old key from previous session, or unparseable.
    // This avoids race conditions — no need to clear keys on load.
    function getScriptStatus(moduleId) {
        try {
            const raw = localStorage.getItem(ALIVE_PREFIX + moduleId);
            if (raw == null) return 'outdated';
            const ts = parseInt(raw, 10);
            if (isNaN(ts)) return 'outdated';
            // Key must be from this page session (with 5s tolerance for scripts
            // that may have initialized slightly before the mod menu)
            return ts >= (PAGE_SESSION_START - 5000) ? 'alive' : 'outdated';
        } catch {
            return 'outdated';
        }
    }

    function readVersionCache() {
        return readPersistentJson(VERSION_CACHE_KEY, {});
    }

    function writeVersionCache(cache) {
        writePersistentJson(VERSION_CACHE_KEY, cache);
    }

    function getCachedLatestVersion(moduleId) {
        const entry = readVersionCache()[moduleId];
        if (!entry) return null;
        if (Date.now() - Number(entry.ts || 0) > VERSION_CACHE_TTL_MS) return null;
        return entry.version ? String(entry.version) : null;
    }

    function setCachedLatestVersion(moduleId, version) {
        const cache = readVersionCache();
        cache[moduleId] = { version: String(version), ts: Date.now() };
        writeVersionCache(cache);
    }

    function getBotHunterManifest() {
        const manifest = readJson(`${MANIFEST_PREFIX}${BOT_HUNTER_MODULE_ID}`, null);
        return manifest?.id === BOT_HUNTER_MODULE_ID ? manifest : null;
    }

    function getBotHunterInstalledVersion(module = null) {
        const manifest = getBotHunterManifest();
        if (!manifest) return '';
        const manifestVersion = module?.manifestVersion || manifest.scriptVersion || '';
        if (manifestVersion) return String(manifestVersion);
        const marker = readJson(`${STANDALONE_RUNTIME_PREFIX}${BOT_HUNTER_MODULE_ID}`, null);
        return marker?.version ? String(marker.version) : '';
    }

    function isVersionOutdated(installed, latest) {
        if (!installed || !latest) return false;
        const currentParts = String(installed).split('.').map(Number);
        const latestParts = String(latest).split('.').map(Number);
        const length = Math.max(currentParts.length, latestParts.length);
        for (let index = 0; index < length; index += 1) {
            const currentPart = Number.isFinite(currentParts[index]) ? currentParts[index] : 0;
            const latestPart = Number.isFinite(latestParts[index]) ? latestParts[index] : 0;
            if (currentPart < latestPart) return true;
            if (currentPart > latestPart) return false;
        }
        return false;
    }

    function getBotHunterUpdateState(module = null) {
        const installedVersion = getBotHunterInstalledVersion(module);
        const latestVersion = getCachedLatestVersion(BOT_HUNTER_MODULE_ID) || '';
        return {
            installed: !!getBotHunterManifest(),
            installedVersion,
            latestVersion,
            hasUpdate: isVersionOutdated(installedVersion, latestVersion)
        };
    }

    function fetchLatestBotHunterVersion(forceRefresh = false) {
        if (!forceRefresh) {
            const cached = getCachedLatestVersion(BOT_HUNTER_MODULE_ID);
            if (cached) return Promise.resolve(cached);
        }
        if (typeof GM_xmlhttpRequest !== 'function') return Promise.resolve(null);
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: BOT_HUNTER_VERSION_API_URL,
                responseType: 'json',
                timeout: 8000,
                anonymous: true,
                onload(response) {
                    try {
                        if (Number(response?.status || 0) < 200 || Number(response?.status || 0) >= 300) {
                            resolve(null);
                            return;
                        }
                        const data = typeof response.response === 'string'
                            ? JSON.parse(response.response)
                            : response.response;
                        const version = String(data?.version || '').trim();
                        if (!version) {
                            resolve(null);
                            return;
                        }
                        setCachedLatestVersion(BOT_HUNTER_MODULE_ID, version);
                        resolve(version);
                    } catch {
                        resolve(null);
                    }
                },
                onerror() { resolve(null); },
                ontimeout() { resolve(null); }
            });
        });
    }

    async function refreshBotHunterVersion({ forceRefresh = false, stampGlobalCheck = false } = {}) {
        const before = getBotHunterUpdateState();
        if (!before.installed || !before.installedVersion) {
            updateDropdownDot();
            return { ...before, checked: false, error: 'not-installed' };
        }
        if (stampGlobalCheck) {
            writePersistentJson(BOT_HUNTER_VERSION_CHECK_TIMESTAMP_KEY, Date.now());
        }
        const latestVersion = await fetchLatestBotHunterVersion(forceRefresh);
        updateDropdownDot();
        if (!latestVersion) {
            return { ...before, checked: true, error: 'request-failed' };
        }
        const after = getBotHunterUpdateState();
        return { ...after, checked: true, error: '' };
    }

    async function refreshInstalledModuleVersions(options = {}) {
        const result = await refreshBotHunterVersion(options);
        return result.installed ? 1 : 0;
    }

    function getPreviewStorageKey(moduleId, fieldKey) {
        return `${PREVIEW_VALUE_PREFIX}${moduleId}:${fieldKey}`;
    }

    function getManifestStorageKey(moduleId, fieldKey) {
        return `ntcfg:${moduleId}:${fieldKey}`;
    }

    // ── Field value cache ──
    // Batch-reads all field values for a module in one pass over localStorage keys,
    // so individual getFieldValue() calls are served from memory instead of N storage reads.
    let _fieldCacheModuleId = '';
    const _fieldValueCache = new Map();

    function preloadFieldValues(moduleId, source) {
        if (_fieldCacheModuleId === moduleId && _fieldValueCache.size > 0) return;
        _fieldValueCache.clear();
        _fieldCacheModuleId = moduleId;
        const manifestPrefix = `ntcfg:${moduleId}:`;
        const previewPrefix = `${PREVIEW_VALUE_PREFIX}${moduleId}:`;
        try {
            const keys = Object.keys(localStorage);
            for (let i = 0; i < keys.length; i++) {
                const key = keys[i];
                if (key.startsWith(manifestPrefix) || key.startsWith(previewPrefix)) {
                    try {
                        _fieldValueCache.set(key, JSON.parse(localStorage.getItem(key)));
                    } catch { /* skip malformed */ }
                }
            }
        } catch { /* ignore */ }
    }

    function invalidateFieldCache() {
        _fieldCacheModuleId = '';
        _fieldValueCache.clear();
    }

    function emitManifestChange(moduleId, fieldKey, value) {
        try {
            document.dispatchEvent(new CustomEvent('ntcfg:change', {
                detail: {
                    script: moduleId,
                    key: fieldKey,
                    value
                }
            }));
        } catch {
            // ignore custom event failures
        }
    }

    function convertManifestSetting(fieldKey, meta) {
        const inputType = String(meta?.type || 'text').toLowerCase();
        const common = {
            key: fieldKey,
            label: meta?.label || titleCaseKey(fieldKey),
            default: meta?.default,
            help: meta?.description || meta?.help || ''
        };

        // Pass through extended metadata when present
        if (meta?.visibleWhen) common.visibleWhen = meta.visibleWhen;
        if (meta?.disabledWhen) common.disabledWhen = meta.disabledWhen;
        if (meta?.compound) common.compound = meta.compound;
        if (meta?.warn) common.warn = meta.warn;
        if (meta?.presets) common.presets = meta.presets;
        if (meta?.normalize) common.normalize = meta.normalize;
        if (meta?.layout) common.layout = meta.layout;
        if (meta?.section) common.section = meta.section;
        if (meta?.confirmOnEnable) common.confirmOnEnable = meta.confirmOnEnable;
        if (meta?.exclusiveWith) common.exclusiveWith = meta.exclusiveWith;
        if (meta?.displayScale != null) common.displayScale = meta.displayScale;
        if (meta?.displayUnit) common.displayUnit = meta.displayUnit;
        if (meta?.displayPrecision != null) common.displayPrecision = meta.displayPrecision;

        if (inputType === 'note') {
            return { type: 'note', message: meta?.message || meta?.description || '', tone: meta?.tone || 'info' };
        }
        if (inputType === 'boolean') {
            return Object.assign({}, common, { type: 'toggle', default: Boolean(meta?.default) });
        }
        if (inputType === 'color') {
            return Object.assign({}, common, { type: 'color', default: meta?.default || '#1C99F4' });
        }
        if (inputType === 'range') {
            return Object.assign({}, common, {
                type: 'range',
                min: meta?.min ?? null,
                max: meta?.max ?? null,
                step: meta?.step ?? '1'
            });
        }
        if (inputType === 'number') {
            return Object.assign({}, common, {
                type: 'number',
                min: meta?.min ?? null,
                max: meta?.max ?? null,
                step: meta?.step ?? '1'
            });
        }
        if (inputType === 'select') {
            return Object.assign({}, common, {
                type: 'select',
                options: Array.isArray(meta?.options) ? meta.options.map((option) => ({
                    value: option?.value,
                    label: option?.label ?? String(option?.value ?? '')
                })) : []
            });
        }
        if (inputType === 'multi-select' || inputType === 'multiselect') {
            return Object.assign({}, common, {
                type: 'multi-select',
                default: Array.isArray(meta?.default) ? meta.default : [],
                options: Array.isArray(meta?.options) ? meta.options.map((option) => ({
                    value: option?.value,
                    label: option?.label ?? String(option?.value ?? '')
                })) : []
            });
        }
        if (inputType === 'keymap') {
            return Object.assign({}, common, {
                type: 'keymap',
                default: Array.isArray(meta?.default) ? meta.default : []
            });
        }
        if (inputType === 'action') {
            return Object.assign({}, common, {
                type: 'action',
                style: meta?.style || 'primary'
            });
        }
        if (inputType === 'mute-list') {
            return Object.assign({}, common, {
                type: 'mute-list'
            });
        }
        return Object.assign({}, common, {
            type: 'text',
            placeholder: meta?.placeholder || ''
        });
    }

    function moduleFromManifest(manifest) {
        const base = MODULE_META[manifest.id] || {
            id: manifest.id,
            label: manifest.name || titleCaseKey(manifest.id),
            shortLabel: titleCaseKey(manifest.id).slice(0, 2).toUpperCase(),
            accent: '#1c99f4',
            description: manifest.description || `${manifest.name || titleCaseKey(manifest.id)} settings.`
        };

        const grouped = new Map();
        const settings = manifest?.settings && typeof manifest.settings === 'object' ? manifest.settings : {};

        // Collect compound child keys so they are not rendered as standalone items
        const compoundChildKeys = new Set();
        Object.entries(settings).forEach(([, meta]) => {
            if (Array.isArray(meta?.compound)) {
                meta.compound.forEach((child) => {
                    if (child?.key) compoundChildKeys.add(child.key);
                });
            }
        });

        Object.entries(settings).forEach(([fieldKey, meta]) => {
            if (meta?.hidden || String(meta?.type || '').toLowerCase() === 'legacy') return;
            if (compoundChildKeys.has(fieldKey)) return; // skip: rendered inline by its parent
            const groupName = meta?.group || 'General';
            if (!grouped.has(groupName)) grouped.set(groupName, []);
            grouped.get(groupName).push(convertManifestSetting(fieldKey, meta));
        });

        // Build a lookup from manifest-provided section metadata (subtitle, ordering)
        const sectionMeta = new Map();
        if (Array.isArray(manifest.sections)) {
            manifest.sections.forEach((sec, i) => sectionMeta.set(sec.id || slugify(sec.title || ''), { index: i, ...sec }));
        }

        const sections = Array.from(grouped.entries()).map(([groupName, items]) => {
            const sectionId = slugify(groupName);
            const meta = sectionMeta.get(sectionId);
            const sec = {
                id: sectionId,
                title: meta?.title || groupName,
                subtitle: meta?.subtitle || `${items.length} synced ${items.length === 1 ? 'setting' : 'settings'} from ${manifest.name || base.label}.`,
                items
            };
            // Pass through section-level metadata
            if (meta?.layout) sec.layout = meta.layout;
            if (meta?.preview) sec.preview = meta.preview;
            if (meta?.resetButton != null) sec.resetButton = meta.resetButton;
            return sec;
        });

        // Sort sections by manifest-defined order if available
        if (sectionMeta.size > 0) {
            sections.sort((a, b) => {
                const ai = sectionMeta.has(a.id) ? sectionMeta.get(a.id).index : Number.MAX_SAFE_INTEGER;
                const bi = sectionMeta.has(b.id) ? sectionMeta.get(b.id).index : Number.MAX_SAFE_INTEGER;
                return ai - bi;
            });
        }

        if (!sections.length) {
            sections.push({
                id: 'general',
                title: 'General',
                subtitle: `${manifest.name || base.label} is registered, but no grouped settings were provided yet.`,
                items: [
                    noteField('This manifest was detected, but it did not expose any structured settings yet.', 'warning')
                ]
            });
        }

        const normalizedSections = applyModuleSectionOverrides(manifest.id, sections);

        const result = Object.assign({}, base, {
            source: 'manifest',
            manifestVersion: manifest.scriptVersion || '',
            sections: normalizedSections
        });

        // Allow script authors to set a custom tab icon via manifest.icon (SVG inner content)
        if (manifest.icon) result.icon = manifest.icon;

        return result;
    }

    function readManifestModules() {
        const modules = [];
        try {
            Object.keys(localStorage).forEach((key) => {
                if (!key.startsWith(MANIFEST_PREFIX)) return;
                const manifest = readJson(key, null);
                if (!manifest || !manifest.id) return;
                const mod = moduleFromManifest(manifest);
                // Always check real heartbeat status — tabs start dimmed and light up as heartbeats arrive
                mod.status = getScriptStatus(manifest.id);
                modules.push(mod);
            });
        } catch {
            return [];
        }

        const orderIndex = new Map(MODULE_LIBRARY.map((module, index) => [module.id, index]));
        modules.sort((a, b) => {
            const left = orderIndex.has(a.id) ? orderIndex.get(a.id) : Number.MAX_SAFE_INTEGER;
            const right = orderIndex.has(b.id) ? orderIndex.get(b.id) : Number.MAX_SAFE_INTEGER;
            if (left !== right) return left - right;
            return a.label.localeCompare(b.label);
        });

        return modules;
    }

    function buildPreviewModule(module) {
        return {
            id: module.id,
            label: module.label,
            shortLabel: module.shortLabel,
            accent: module.accent,
            description: module.description,
            source: 'preview',
            sections: module.previewSections
        };
    }

    function buildVisibleModules() {
        const manifestModules = readManifestModules();
        if (!PREVIEW_FALLBACK_ENABLED) return applyCustomTabOrder(manifestModules);

        const manifestById = new Map(manifestModules.map((module) => [module.id, module]));
        const modules = [];

        MODULE_LIBRARY.forEach((module) => {
            const manifest = manifestById.get(module.id);
            if (manifest && (manifest.status === 'alive' || module.hiddenUnlessInstalled)) {
                // Script is alive, or exempt from heartbeat (e.g. manually distributed scripts)
                modules.push(manifest);
            } else if (manifest) {
                // Manifest exists but no fresh heartbeat — needs update or is disabled
                const outdated = buildPreviewModule(module);
                outdated.source = 'outdated';
                outdated.manifestVersion = manifest.manifestVersion || '';
                modules.push(outdated);
            } else if (!module.hiddenUnlessInstalled) {
                modules.push(buildPreviewModule(module));
            }
        });

        manifestModules.forEach((module) => {
            if (!MODULE_META[module.id]) {
                if (module.status === 'alive') {
                    modules.push(module);
                }
                // Unknown stale/outdated scripts are silently dropped
            }
        });

        return applyCustomTabOrder(modules);
    }

    function applyCustomTabOrder(modules) {
        try {
            const savedOrder = readPersistentJson(TAB_ORDER_KEY, null);
            if (Array.isArray(savedOrder) && savedOrder.length) {
                const byId = new Map(modules.map((m) => [m.id, m]));
                const ordered = [];
                savedOrder.forEach((id) => {
                    const m = byId.get(id);
                    if (m) {
                        ordered.push(m);
                        byId.delete(id);
                    }
                });
                byId.forEach((m) => ordered.push(m));
                return ordered;
            }
        } catch { /* ignore */ }
        return modules;
    }

    function getRenderableSections(module) {
        return Array.isArray(module?.sections) ? module.sections : [];
    }

    function readUiState() {
        return readPersistentJson(UI_STATE_KEY, {}) || {};
    }

    function writeUiState(nextState) {
        writePersistentJson(UI_STATE_KEY, nextState);
    }

    function parseHashSelection() {
        const hash = String(window.location.hash || '').replace(/^#/, '').trim();
        if (!hash) return null;
        const [moduleId, sectionId] = hash.split('/');
        if (!moduleId) return null;
        return { moduleId, sectionId: sectionId || '' };
    }

    function getInitialSelection(modules) {
        const stored = readUiState();
        const fromHash = parseHashSelection();
        const candidates = [fromHash, stored].filter(Boolean);

        for (const candidate of candidates) {
            const module = modules.find((entry) => entry.id === candidate.moduleId);
            if (!module) continue;
            const moduleSections = getRenderableSections(module);
            const section = moduleSections.find((entry) => entry.id === candidate.sectionId) || moduleSections[0];
            return {
                activeModuleId: module.id,
                activeSectionId: section ? section.id : ''
            };
        }

        const firstModule = modules[0] || null;
        const firstSections = getRenderableSections(firstModule);
        return {
            activeModuleId: firstModule ? firstModule.id : '',
            activeSectionId: firstSections[0] ? firstSections[0].id : ''
        };
    }

    function getFieldValue(module, item) {
        if (!item || !item.key) return item?.default ?? '';
        const storageKey = item.storageKey
            || (module.source === 'manifest'
                ? getManifestStorageKey(module.id, item.key)
                : getPreviewStorageKey(module.id, item.key));

        // If the batch cache is loaded for this module, trust it completely.
        // Keys not in the cache don't exist in localStorage — return the default
        // without hitting storage. This avoids N individual reads for uncustomized fields.
        if (_fieldCacheModuleId === module.id) {
            return _fieldValueCache.has(storageKey) ? _fieldValueCache.get(storageKey) : item.default;
        }

        try {
            const raw = localStorage.getItem(storageKey);
            if (raw == null) return item.default;
            return JSON.parse(raw);
        } catch {
            return item.default;
        }
    }

    function setFieldValue(module, item, value) {
        if (!item || !item.key) return;
        const storageKey = item.storageKey
            || (module.source === 'manifest'
                ? getManifestStorageKey(module.id, item.key)
                : getPreviewStorageKey(module.id, item.key));

        try {
            localStorage.setItem(storageKey, JSON.stringify(value));
            // Keep field cache in sync
            if (_fieldCacheModuleId === module.id) _fieldValueCache.set(storageKey, value);
        } catch {
            // ignore storage errors
        }

        if (module.source === 'manifest') {
            emitManifestChange(module.id, item.key, value);
        }
        // Linked fields: emit change event for the target module so the badge script picks it up.
        if (item.emitModule && item.emitKey) {
            emitManifestChange(item.emitModule, item.emitKey, value);
        }
    }

    // Brand icons matching the music player's createPlatformIcon set, as
    // innerHTML strings (the factories live in the music script).
    function getMusicPlatformIconSvg(platform, size) {
        const open = '<svg viewBox="0 0 24 24" width="' + size + '" height="' + size + '" aria-hidden="true" style="display:block;">';
        if (platform === 'youtube') {
            return open + '<path fill="#FF0000" d="M23.5 6.2a3 3 0 0 0-2.1-2.1C19.5 3.6 12 3.6 12 3.6s-7.5 0-9.4.5A3 3 0 0 0 .5 6.2C0 8.1 0 12 0 12s0 3.9.5 5.8a3 3 0 0 0 2.1 2.1c1.9.5 9.4.5 9.4.5s7.5 0 9.4-.5a3 3 0 0 0 2.1-2.1c.5-1.9.5-5.8.5-5.8s0-3.9-.5-5.8zM9.6 15.6V8.4l6.3 3.6-6.3 3.6z"/></svg>';
        }
        if (platform === 'apple-music') {
            return open + '<path fill="#FA2D48" d="M23.99 6.07a6.16 6.16 0 0 0-.14-1.43 3.77 3.77 0 0 0-.6-1.22 3.65 3.65 0 0 0-1.22-.91 3.7 3.7 0 0 0-1.27-.42A13.3 13.3 0 0 0 18.7 2H5.3a12.6 12.6 0 0 0-2.06.1 3.7 3.7 0 0 0-1.27.41 3.52 3.52 0 0 0-1.22.92 3.72 3.72 0 0 0-.6 1.22A6 6 0 0 0 .01 6.07C0 6.68 0 7.29 0 7.91v8.18c0 .62 0 1.23.01 1.84a6.16 6.16 0 0 0 .14 1.43 3.77 3.77 0 0 0 .6 1.22 3.52 3.52 0 0 0 1.22.92 3.7 3.7 0 0 0 1.27.41c.68.08 1.37.1 2.06.1H18.7c.69 0 1.38-.02 2.06-.1a3.7 3.7 0 0 0 1.27-.42 3.65 3.65 0 0 0 1.22-.91 3.72 3.72 0 0 0 .6-1.22 6 6 0 0 0 .14-1.44c.01-.6.01-1.21.01-1.83V7.9c0-.61 0-1.22-.01-1.83zM16.94 13.7v2.23a2 2 0 0 1-.04.4 1.39 1.39 0 0 1-.55.89 1.81 1.81 0 0 1-.86.38 2.29 2.29 0 0 1-.92-.02 1.43 1.43 0 0 1-.74-.44 1.18 1.18 0 0 1-.3-.73 1.26 1.26 0 0 1 .2-.82 1.58 1.58 0 0 1 .73-.55c.37-.14.75-.23 1.13-.32.35-.08.52-.25.55-.61V10.7l-.01-.19a.63.63 0 0 0-.3-.46.8.8 0 0 0-.44-.13l-.13.02-4.23.97a.72.72 0 0 0-.56.57l-.01.14v5.63a2.06 2.06 0 0 1-.04.42 1.4 1.4 0 0 1-.55.87 1.79 1.79 0 0 1-.86.39 2.32 2.32 0 0 1-.92-.02 1.41 1.41 0 0 1-.74-.44 1.2 1.2 0 0 1-.3-.73 1.28 1.28 0 0 1 .2-.83 1.59 1.59 0 0 1 .73-.54c.37-.14.76-.23 1.13-.33.34-.08.51-.25.54-.6V8.94a1.6 1.6 0 0 1 .1-.56 1.1 1.1 0 0 1 .7-.7l4.84-1.27a2.1 2.1 0 0 1 .43-.08.68.68 0 0 1 .59.26.83.83 0 0 1 .15.4c.01.08.02.16.02.24v6.48z"/></svg>';
        }
        return open + '<path fill="#1DB954" d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.52 17.34a.72.72 0 0 1-1.02.24c-2.82-1.74-6.36-2.1-10.56-1.14a.72.72 0 0 1-.36-1.4c4.56-1.02 8.52-.6 11.64 1.32.36.24.48.72.3 1.02zm1.44-3.3a.9.9 0 0 1-1.26.3c-3.24-1.98-8.16-2.58-11.94-1.38a.9.9 0 1 1-.54-1.72c4.32-1.32 9.72-.66 13.44 1.62.42.24.6.78.3 1.2zm.12-3.36c-3.9-2.34-10.32-2.52-13.98-1.38a1.08 1.08 0 1 1-.66-2.04c4.26-1.26 11.28-1.02 15.72 1.62.48.3.66.96.42 1.44-.3.42-.96.6-1.5.36z"/></svg>';
    }

    // Custom Queue-section field: renders the music player's saved sources
    // (JSON list value) as manageable rows — enable checkbox, name, platform
    // tag, and a trash button. Adding happens via the "Add Source URL" field.
    function renderMusicSavedSourcesField(targetRoot, module, item) {
        const wrap = createElement('div', 'ntcfg-sources-list');
        wrap.style.cssText = 'display:flex;flex-direction:column;gap:6px;margin:6px 0 10px;';

        const readList = () => {
            try {
                const parsed = JSON.parse(String(getFieldValue(module, item) || '[]'));
                return Array.isArray(parsed) ? parsed.filter((e) => e && e.uri) : [];
            } catch { return []; }
        };
        const writeList = (list) => setFieldValue(module, item, JSON.stringify(list));

        const render = () => {
            wrap.textContent = '';
            const list = readList();
            if (!list.length) {
                const empty = createElement('div', '', 'No saved sources yet — paste a URL above and save it.');
                empty.style.cssText = 'font-size:13px;color:#8e9aaf;';
                wrap.appendChild(empty);
                return;
            }
            list.forEach((entry) => {
                const isEnabled = entry.enabled !== false;
                const row = createElement('div', 'ntcfg-source-row');
                row.style.cssText = 'display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);';

                const checkbox = createElement('input');
                checkbox.type = 'checkbox';
                checkbox.checked = isEnabled;
                checkbox.title = isEnabled ? 'Disable this source' : 'Enable this source';
                checkbox.style.cssText = 'margin:0;cursor:pointer;';
                checkbox.addEventListener('change', () => {
                    const next = readList();
                    const target = next.find((e) => e.uri === entry.uri);
                    if (!target) return;
                    target.enabled = checkbox.checked;
                    writeList(next);
                    render();
                });

                const name = createElement('div', '', entry.label || entry.url || entry.uri);
                name.title = entry.url || entry.uri;
                name.style.cssText = 'flex:1 1 auto;font-size:14px;color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;opacity:' + (isEnabled ? '1' : '0.5') + ';';

                // Icon + platform name — the settings page has room for both
                // (the in-race dropdown stays icon-only).
                const platformName = entry.platform === 'youtube' ? 'YouTube' : entry.platform === 'apple-music' ? 'Apple Music' : 'Spotify';
                const tag = createElement('span');
                tag.style.cssText = 'flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;font-size:12px;color:#8e9aaf;';
                tag.innerHTML = getMusicPlatformIconSvg(entry.platform, 14);
                tag.appendChild(document.createTextNode(platformName));

                const trash = createElement('button');
                trash.type = 'button';
                trash.title = 'Remove source';
                trash.innerHTML = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1L5 6"/><path d="M10 11v6M14 11v6"/></svg>';
                trash.style.cssText = 'flex:0 0 auto;display:inline-flex;align-items:center;background:none;border:none;color:#c66;cursor:pointer;padding:2px;';
                trash.addEventListener('click', () => {
                    writeList(readList().filter((e) => e.uri !== entry.uri));
                    render();
                });

                row.appendChild(checkbox);
                if (entry.art) {
                    const art = createElement('img');
                    art.src = entry.art;
                    art.alt = '';
                    art.style.cssText = 'flex:0 0 auto;width:22px;height:22px;border-radius:4px;object-fit:cover;opacity:' + (isEnabled ? '1' : '0.5') + ';';
                    art.addEventListener('error', () => art.remove());
                    row.appendChild(art);
                }
                row.appendChild(name);
                row.appendChild(tag);
                row.appendChild(trash);
                wrap.appendChild(row);
            });
        };

        render();
        targetRoot.appendChild(wrap);
        return { row: wrap };
    }

    function renderSafeSpaceMutedUsersField(root, item) {
        const field = createElement('div', 'ntcfg-field');
        field.dataset.fieldKey = item.key || 'LOW_ENERGY_MUTED_USERS';
        field.appendChild(createElement('div', 'ntcfg-field-title', item.label || 'Muted Racers'));
        if (item.help) field.appendChild(createElement('div', 'ntcfg-field-help', item.help));

        const list = createElement('div', 'ntcfg-muted-users-list');
        list.style.cssText = 'display:flex;flex-direction:column;gap:8px;margin-top:8px;';
        field.appendChild(list);

        const renderEmpty = (message) => {
            list.replaceChildren();
            list.appendChild(createElement('div', 'ntmods-note ntmods-note--info', message));
        };

        const render = async () => {
            renderEmpty('Loading muted racers...');
            const users = await readSafeSpaceMutedUsers();
            if (!users.length) {
                renderEmpty('No muted racers yet.');
                return;
            }

            list.replaceChildren();
            users.forEach((user) => {
                const displayName = user.displayName || user.username || 'Unknown racer';
                const row = createElement('div', 'ntcfg-muted-user-row');
                row.style.cssText = 'display:grid;grid-template-columns:78px minmax(0,1fr) auto;gap:12px;align-items:center;padding:10px;border:1px solid rgba(255,255,255,0.1);border-radius:8px;background:rgba(10,12,20,0.36);';

                const car = createElement('img', 'ntcfg-muted-user-car');
                car.alt = `${displayName}'s car`;
                car.style.cssText = 'display:block;width:78px;max-height:42px;object-fit:contain;';
                if (user.carImgSrc) car.src = user.carImgSrc;

                const copy = createElement('div', 'ntcfg-muted-user-copy');
                copy.style.cssText = 'min-width:0;';
                const name = createElement('div', 'ntcfg-muted-user-name');
                name.style.cssText = 'font-size:14px;font-weight:700;color:#eef3ff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
                name.textContent = `${user.team ? `[${user.team}] ` : ''}${displayName}`;
                if (user.teamColor) name.style.color = `#${String(user.teamColor).replace(/^#/, '')}`;

                const title = createElement('div', 'ntcfg-muted-user-title');
                title.style.cssText = 'margin-top:2px;font-size:12px;color:#99a4c5;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
                title.textContent = user.title ? `"${user.title}"` : 'Muted in Low Energy Mode';
                copy.append(name, title);

                const remove = createElement('button', 'ntcfg-action ntcfg-action--danger', 'Remove');
                remove.type = 'button';
                remove.style.cssText = 'padding:8px 12px;';
                remove.addEventListener('click', async () => {
                    remove.disabled = true;
                    const removed = await deleteSafeSpaceMutedUser(user.id);
                    if (removed) {
                        showToast('Muted racer removed.');
                        render();
                    } else {
                        remove.disabled = false;
                        showToast('Could not remove muted racer.');
                    }
                });

                row.append(car, copy, remove);
                list.appendChild(row);
            });
        };

        render();
        root.appendChild(field);
        return { field };
    }

    function coerceInputValue(element, item) {
        if (!element || !item) return null;
        if (item.type === 'toggle') return !!element.checked;
        if (item.type === 'number' || item.type === 'range') {
            const parsed = Number(element.value);
            return Number.isFinite(parsed) ? parsed : (item.default ?? 0);
        }
        if (item.type === 'multi-select') {
            const fieldRoot = element.closest('.ntmods-field-card, .ntcfg-field') || document;
            const checkboxes = Array.from(fieldRoot.querySelectorAll(`[data-field-key="${CSS.escape(item.key)}"][data-field-type="multi-select"]`));
            const selectedValues = (Array.isArray(item.options) ? item.options : [])
                .map((option) => String(option.value))
                .filter((optionValue) => {
                    const checkbox = checkboxes.find((candidate) => candidate.value === optionValue);
                    return checkbox?.checked;
                });
            return selectedValues.length > 0 ? selectedValues : (Array.isArray(item.default) ? item.default : []);
        }
        return String(element.value ?? '');
    }

    function createElement(tagName, className = '', textContent = null) {
        const element = document.createElement(tagName);
        if (className) element.className = className;
        if (textContent != null) element.textContent = textContent;
        return element;
    }

    const SAFE_SPACE_DB_NAME = 'NTSafeSpace';
    const SAFE_SPACE_USERS_STORE = 'users';

    async function hasSafeSpaceDatabase() {
        if (!window.indexedDB) return false;
        if (typeof indexedDB.databases !== 'function') return true;
        try {
            const databases = await indexedDB.databases();
            return databases.some((database) => database?.name === SAFE_SPACE_DB_NAME);
        } catch {
            return true;
        }
    }

    async function openSafeSpaceUsersStore(mode = 'readonly') {
        if (!await hasSafeSpaceDatabase()) return null;
        return new Promise((resolve) => {
            const request = indexedDB.open(SAFE_SPACE_DB_NAME);
            request.onerror = () => resolve(null);
            request.onsuccess = () => {
                const db = request.result;
                if (!db.objectStoreNames.contains(SAFE_SPACE_USERS_STORE)) {
                    db.close();
                    resolve(null);
                    return;
                }
                try {
                    const tx = db.transaction(SAFE_SPACE_USERS_STORE, mode);
                    tx.oncomplete = () => db.close();
                    tx.onerror = () => db.close();
                    tx.onabort = () => db.close();
                    resolve({ db, tx, store: tx.objectStore(SAFE_SPACE_USERS_STORE) });
                } catch {
                    db.close();
                    resolve(null);
                }
            };
        });
    }

    async function readSafeSpaceMutedUsers() {
        const connection = await openSafeSpaceUsersStore('readonly');
        if (!connection) return [];
        return new Promise((resolve) => {
            const request = connection.store.getAll();
            request.onerror = () => resolve([]);
            request.onsuccess = () => {
                const users = Array.isArray(request.result) ? request.result : [];
                resolve(users
                    .filter((user) => String(user?.status || '').toUpperCase() === 'MUTE')
                    .sort((a, b) => String(a.displayName || a.username || '').localeCompare(String(b.displayName || b.username || ''))));
            };
        });
    }

    async function deleteSafeSpaceMutedUser(userId) {
        const numericId = Number(userId);
        if (!Number.isFinite(numericId)) return false;
        const connection = await openSafeSpaceUsersStore('readwrite');
        if (!connection) return false;
        return new Promise((resolve) => {
            const request = connection.store.delete(numericId);
            request.onerror = () => resolve(false);
            request.onsuccess = () => resolve(true);
        });
    }

    function getModuleFieldMap(module) {
        const fieldMap = new Map();
        (module?.sections || []).forEach((section) => {
            (section?.items || []).forEach((item) => {
                if (item?.key) fieldMap.set(item.key, item);
            });
        });
        return fieldMap;
    }

    function resolveModuleFieldItem(fieldMap, key, fallback = {}) {
        return fieldMap.get(key) || Object.assign({
            key,
            label: titleCaseKey(key),
            default: '',
            type: 'text',
            help: ''
        }, fallback);
    }

    function readModuleFieldValue(module, fieldMap, key, fallback = {}) {
        return getFieldValue(module, resolveModuleFieldItem(fieldMap, key, fallback));
    }

    function writeModuleFieldValue(module, fieldMap, key, value, fallback = {}) {
        setFieldValue(module, resolveModuleFieldItem(fieldMap, key, fallback), value);
    }

    function normalizeRaceOptionsHexColorValue(value, fallback = '#FFFFFF') {
        const normalizedFallback = /^#[0-9A-Fa-f]{6}$/.test(String(fallback || '').trim())
            ? String(fallback).toUpperCase()
            : '#FFFFFF';
        const raw = String(value || '').trim();
        if (/^#[0-9A-Fa-f]{6}$/.test(raw)) return raw.toUpperCase();
        if (/^#[0-9A-Fa-f]{3}$/.test(raw)) {
            return `#${raw[1]}${raw[1]}${raw[2]}${raw[2]}${raw[3]}${raw[3]}`.toUpperCase();
        }
        return normalizedFallback;
    }

    function hexToRgba(hex, alpha = 1) {
        const h = normalizeRaceOptionsHexColorValue(hex, '#FFFFFF');
        const r = parseInt(h.slice(1, 3), 16);
        const g = parseInt(h.slice(3, 5), 16);
        const b = parseInt(h.slice(5, 7), 16);
        return `rgba(${r}, ${g}, ${b}, ${Math.min(1, Math.max(0, alpha))})`;
    }

    function hexToRgbChannels(hex) {
        const h = normalizeRaceOptionsHexColorValue(hex, '#FFFFFF');
        return [parseInt(h.slice(1, 3), 16), parseInt(h.slice(3, 5), 16), parseInt(h.slice(5, 7), 16)];
    }

    function blendedLuminance(fgHex, bgHex, alpha) {
        const [fr, fg, fb] = hexToRgbChannels(fgHex);
        const [br, bg, bb] = hexToRgbChannels(bgHex);
        const a = Math.min(1, Math.max(0, alpha));
        const r = (fr * a + br * (1 - a)) / 255;
        const g = (fg * a + bg * (1 - a)) / 255;
        const b = (fb * a + bb * (1 - a)) / 255;
        const linear = (ch) => (ch <= 0.03928 ? ch / 12.92 : Math.pow((ch + 0.055) / 1.055, 2.4));
        return (0.2126 * linear(r)) + (0.7152 * linear(g)) + (0.0722 * linear(b));
    }

    function normalizeRaceOptionsHighlightOpacity(value, fallback = 0.5) {
        const normalizedFallback = Number.isFinite(Number(fallback)) ? Number(fallback) : 0.5;
        const parsed = Number(value);
        if (!Number.isFinite(parsed)) return normalizedFallback;
        return Math.min(1, Math.max(0.05, Math.round(parsed * 100) / 100));
    }

    function normalizeRaceOptionsRainbowSpeedSeconds(value, fallback = 10) {
        const normalizedFallback = Number.isFinite(Number(fallback)) ? Number(fallback) : 10;
        const parsed = Number(value);
        if (!Number.isFinite(parsed)) return normalizedFallback;
        return Math.min(60, Math.max(1, parsed));
    }

    function normalizeRaceOptionsThemeFontSizePx(value, fallback = 18) {
        const normalizedFallback = Number.isFinite(Number(fallback)) ? Number(fallback) : 18;
        const parsed = Number(value);
        if (!Number.isFinite(parsed)) return normalizedFallback;
        return Math.min(72, Math.max(8, Math.round(parsed)));
    }

    function normalizeRaceOptionsThemeFontFamilyValue(value, fallback = RACE_OPTIONS_THEME_FONT_FAMILY_DEFAULT_CSS) {
        const raw = String(value ?? '').trim();
        if (!raw) return String(fallback);
        const cleaned = raw.replace(/[{}<>;\r\n]/g, '').trim().slice(0, 180);
        return cleaned || String(fallback);
    }

    function normalizeRaceOptionsThemePresetValue(value, presets, fallback = '__default__') {
        const raw = String(value ?? '').trim();
        if (Array.isArray(presets) && presets.some((preset) => preset?.value === raw)) {
            return raw;
        }
        return fallback;
    }

    function normalizeRaceOptionsDarkModeSyncSelectionValue(value, fallback = 'always') {
        if (value === true) return 'system';
        if (value === false) return 'always';
        const raw = String(value ?? '').trim().toLowerCase();
        if (raw === 'system') return 'system';
        if (raw === 'always') return 'always';
        return fallback;
    }

    function normalizeRaceOptionsDarkModeSyncStoredValue(value) {
        return normalizeRaceOptionsDarkModeSyncSelectionValue(value) === 'system';
    }

    function getRaceOptionsThemeOptions(module, fieldMap) {
        const fontFamilyPreset = normalizeRaceOptionsThemePresetValue(
            readModuleFieldValue(module, fieldMap, 'THEME_FONT_FAMILY_PRESET', { default: '__default__' }),
            RACE_OPTIONS_THEME_FONT_FAMILY_PRESETS
        );
        const fontSizePreset = normalizeRaceOptionsThemePresetValue(
            readModuleFieldValue(module, fieldMap, 'THEME_FONT_SIZE_PRESET', { default: '__default__' }),
            RACE_OPTIONS_THEME_FONT_SIZE_PRESETS
        );
        const singleLineFontSizePreset = normalizeRaceOptionsThemePresetValue(
            readModuleFieldValue(module, fieldMap, 'THEME_SINGLE_LINE_FONT_SIZE_PRESET', { default: '__default__' }),
            RACE_OPTIONS_THEME_FONT_SIZE_PRESETS
        );
        const fontFamilyPresetOption = RACE_OPTIONS_THEME_FONT_FAMILY_PRESETS.find((preset) => preset.value === fontFamilyPreset) || RACE_OPTIONS_THEME_FONT_FAMILY_PRESETS[0];
        const fontSizePresetOption = RACE_OPTIONS_THEME_FONT_SIZE_PRESETS.find((preset) => preset.value === fontSizePreset) || RACE_OPTIONS_THEME_FONT_SIZE_PRESETS[0];
        const singleLineFontSizePresetOption = RACE_OPTIONS_THEME_FONT_SIZE_PRESETS.find((preset) => preset.value === singleLineFontSizePreset) || RACE_OPTIONS_THEME_FONT_SIZE_PRESETS[0];
        const darkModeEnabled = !!readModuleFieldValue(module, fieldMap, 'THEME_ENABLE_DARK_MODE', { default: false, type: 'toggle' });
        const darkModeSyncSystem = normalizeRaceOptionsDarkModeSyncStoredValue(
            readModuleFieldValue(module, fieldMap, 'THEME_DARK_MODE_SYNC_SYSTEM', { default: false, type: 'toggle' })
        );
        const darkModeEffective = darkModeSyncSystem
            ? !!(typeof window.matchMedia === 'function' && window.matchMedia('(prefers-color-scheme: dark)').matches)
            : darkModeEnabled;

        return {
            darkModeEnabled,
            darkModeSyncSystem,
            darkModeEffective,
            foreground: normalizeRaceOptionsHexColorValue(readModuleFieldValue(module, fieldMap, 'THEME_COLOR_FOREGROUND', { default: '#FFFFFF' }), '#FFFFFF'),
            foregroundActive: normalizeRaceOptionsHexColorValue(readModuleFieldValue(module, fieldMap, 'THEME_COLOR_FOREGROUND_ACTIVE', { default: '#000000' }), '#000000'),
            foregroundTyped: normalizeRaceOptionsHexColorValue(readModuleFieldValue(module, fieldMap, 'THEME_COLOR_FOREGROUND_TYPED', { default: '#5B5B5B' }), '#5B5B5B'),
            background: normalizeRaceOptionsHexColorValue(readModuleFieldValue(module, fieldMap, 'THEME_COLOR_BACKGROUND', { default: '#000000' }), '#000000'),
            backgroundActive: normalizeRaceOptionsHexColorValue(readModuleFieldValue(module, fieldMap, 'THEME_COLOR_BACKGROUND_ACTIVE', { default: '#FFFFFF' }), '#FFFFFF'),
            backgroundIncorrect: normalizeRaceOptionsHexColorValue(readModuleFieldValue(module, fieldMap, 'THEME_COLOR_BACKGROUND_INCORRECT', { default: '#FF0000' }), '#FF0000'),
            overrideForeground: !!readModuleFieldValue(module, fieldMap, 'THEME_OVERRIDE_FOREGROUND', { default: false, type: 'toggle' }),
            overrideForegroundActive: !!readModuleFieldValue(module, fieldMap, 'THEME_OVERRIDE_FOREGROUND_ACTIVE', { default: false, type: 'toggle' }),
            overrideForegroundTyped: !!readModuleFieldValue(module, fieldMap, 'THEME_OVERRIDE_FOREGROUND_TYPED', { default: false, type: 'toggle' }),
            hideTypedText: !!readModuleFieldValue(module, fieldMap, 'THEME_HIDE_TYPED_TEXT', { default: false, type: 'toggle' }),
            overrideBackground: !!readModuleFieldValue(module, fieldMap, 'THEME_OVERRIDE_BACKGROUND', { default: false, type: 'toggle' }),
            overrideBackgroundActive: !!readModuleFieldValue(module, fieldMap, 'THEME_OVERRIDE_BACKGROUND_ACTIVE', { default: false, type: 'toggle' }),
            overrideBackgroundIncorrect: !!readModuleFieldValue(module, fieldMap, 'THEME_OVERRIDE_BACKGROUND_INCORRECT', { default: false, type: 'toggle' }),
            fontFamilyPreset,
            fontFamilyCss: fontFamilyPreset !== '__default__' && typeof fontFamilyPresetOption?.css === 'string'
                ? normalizeRaceOptionsThemeFontFamilyValue(fontFamilyPresetOption.css, RACE_OPTIONS_THEME_FONT_FAMILY_DEFAULT_CSS)
                : null,
            fontSizePreset,
            fontSizePx: fontSizePreset !== '__default__' && Number.isFinite(Number(fontSizePresetOption?.px))
                ? normalizeRaceOptionsThemeFontSizePx(fontSizePresetOption.px, 18)
                : null,
            singleLineFontSizePreset,
            singleLineFontSizePx: singleLineFontSizePreset !== '__default__' && Number.isFinite(Number(singleLineFontSizePresetOption?.px))
                ? normalizeRaceOptionsThemeFontSizePx(singleLineFontSizePresetOption.px, 18)
                : null,
            fontBold: !!readModuleFieldValue(module, fieldMap, 'THEME_FONT_BOLD', { default: false, type: 'toggle' }),
            fontItalic: !!readModuleFieldValue(module, fieldMap, 'THEME_FONT_ITALIC', { default: false, type: 'toggle' }),
            fontWeight: !!readModuleFieldValue(module, fieldMap, 'THEME_FONT_BOLD', { default: false, type: 'toggle' })
                ? Math.min(900, Math.max(100, Math.round(Number(readModuleFieldValue(module, fieldMap, 'THEME_FONT_WEIGHT', { default: 700, type: 'select' })) || 700)))
                : RACE_OPTIONS_THEME_FONT_WEIGHT_DEFAULT,
            enableRainbowTypedText: !!readModuleFieldValue(module, fieldMap, 'THEME_ENABLE_RAINBOW_TYPED_TEXT', { default: false, type: 'toggle' }),
            rainbowTypedTextSpeedSeconds: normalizeRaceOptionsRainbowSpeedSeconds(
                readModuleFieldValue(module, fieldMap, 'THEME_RAINBOW_TYPED_TEXT_SPEED_SECONDS', { default: 10, type: 'number' }),
                10
            )
        };
    }

    function getEffectiveRaceOptionsThemeBackground(module, fieldMap) {
        const themeOptions = getRaceOptionsThemeOptions(module, fieldMap);
        if (themeOptions.overrideBackground) return normalizeRaceOptionsHexColorValue(themeOptions.background, '#FFFFFF');
        if (themeOptions.darkModeEffective) return RACE_OPTIONS_THEME_DARK_MODE_BACKGROUND;
        return '#E9EAEB';
    }

    function getPerfectNitroTextStyle(module, fieldMap, highlightColor, opacity = 0.5) {
        const bgHex = getEffectiveRaceOptionsThemeBackground(module, fieldMap);
        const fgHex = normalizeRaceOptionsHexColorValue(highlightColor, '#FFFFFF');
        const luminance = blendedLuminance(fgHex, bgHex, opacity);
        const color = luminance > 0.5 ? '#101623' : '#E7EEF8';
        return {
            color,
            shadow: color === '#101623' ? '0 1px 1px rgba(255, 255, 255, 0.25)' : '0 1px 1px rgba(0, 0, 0, 0.35)'
        };
    }

    function getRaceOptionsPerfectNitroOptions(module, fieldMap) {
        return {
            enabled: !!readModuleFieldValue(module, fieldMap, 'ENABLE_PERFECT_NITROS', { default: true, type: 'toggle' }),
            highlightColor: normalizeRaceOptionsHexColorValue(readModuleFieldValue(module, fieldMap, 'PERFECT_NITRO_HIGHLIGHT_COLOR', { default: '#FFFFFF' }), '#FFFFFF'),
            enableHighlight: !!readModuleFieldValue(module, fieldMap, 'PERFECT_NITRO_ENABLE_HIGHLIGHT', { default: true, type: 'toggle' }),
            italic: !!readModuleFieldValue(module, fieldMap, 'PERFECT_NITRO_ITALIC', { default: false, type: 'toggle' }),
            rainbow: !!readModuleFieldValue(module, fieldMap, 'PERFECT_NITRO_RAINBOW', { default: false, type: 'toggle' }),
            highlightOpacity: normalizeRaceOptionsHighlightOpacity(readModuleFieldValue(module, fieldMap, 'PERFECT_NITRO_HIGHLIGHT_OPACITY', { default: 0.5, type: 'select' }), 0.5),
            overrideTextColor: !!readModuleFieldValue(module, fieldMap, 'PERFECT_NITRO_OVERRIDE_TEXT_COLOR', { default: false, type: 'toggle' }),
            textColor: normalizeRaceOptionsHexColorValue(readModuleFieldValue(module, fieldMap, 'PERFECT_NITRO_TEXT_COLOR', { default: '#FFFFFF' }), '#FFFFFF')
        };
    }

    function renderPerfectNitroPreview(preview, module, fieldMap) {
        const previewText = 'the quick brown fox jumps';
        const highlightWord = 'jumps';
        const highlightStart = previewText.indexOf(highlightWord);
        const highlightEnd = highlightStart + highlightWord.length;
        const options = getRaceOptionsPerfectNitroOptions(module, fieldMap);
        const themeOptions = getRaceOptionsThemeOptions(module, fieldMap);
        preview.innerHTML = '';

        if (themeOptions.darkModeEffective) {
            preview.style.background = RACE_OPTIONS_THEME_DARK_MODE_BACKGROUND;
            preview.style.color = RACE_OPTIONS_THEME_DARK_MODE_FOREGROUND;
        } else if (themeOptions.overrideBackground) {
            preview.style.background = themeOptions.background;
            preview.style.color = '';
        } else {
            preview.style.background = '';
            preview.style.color = '';
        }

        preview.style.fontFamily = themeOptions.fontFamilyCss || '';

        const bgRgba = options.enableHighlight ? hexToRgba(options.highlightColor, options.highlightOpacity) : null;
        const adaptiveTextStyle = options.enableHighlight
            ? getPerfectNitroTextStyle(module, fieldMap, options.highlightColor, options.highlightOpacity)
            : { color: null, shadow: null };
        const textStyle = options.overrideTextColor
            ? { color: options.textColor, shadow: null }
            : options.rainbow
                ? { color: null, shadow: adaptiveTextStyle.shadow }
                : adaptiveTextStyle;

        Array.from(previewText).forEach((char, index) => {
            const span = createElement('span');
            span.textContent = char;

            const isHighlighted = index >= highlightStart && index < highlightEnd;
            if (isHighlighted) {
                if (bgRgba) span.style.backgroundColor = bgRgba;
                if (textStyle.color) span.style.color = textStyle.color;
                if (textStyle.shadow) span.style.textShadow = textStyle.shadow;
                if (options.italic) span.style.fontStyle = 'italic';
                if (options.rainbow) span.classList.add('ntcfg-pn-preview-rainbow');
            } else if (themeOptions.overrideForeground) {
                span.style.color = themeOptions.foreground;
            } else if (themeOptions.darkModeEffective) {
                span.style.color = RACE_OPTIONS_THEME_DARK_MODE_FOREGROUND;
            }

            preview.appendChild(span);
        });
    }

    function renderThemePreview(preview, cursorIndex, incorrect, module, fieldMap) {
        const options = getRaceOptionsThemeOptions(module, fieldMap);
        const previewText = 'The Quick Brown Fox is afraid of The Big Black';
        preview.innerHTML = '';

        if (options.darkModeEffective) {
            preview.style.background = RACE_OPTIONS_THEME_DARK_MODE_BACKGROUND;
            preview.style.color = RACE_OPTIONS_THEME_DARK_MODE_FOREGROUND;
        } else if (options.overrideBackground) {
            preview.style.background = options.background;
            preview.style.color = '';
        } else {
            preview.style.background = '';
            preview.style.color = '';
        }

        preview.style.fontFamily = options.fontFamilyCss || '';
        preview.style.fontWeight = String(options.fontWeight || RACE_OPTIONS_THEME_FONT_WEIGHT_DEFAULT);
        preview.style.fontStyle = options.fontItalic ? 'italic' : '';

        Array.from(previewText).forEach((char, index) => {
            const span = createElement('span');
            if (index === cursorIndex) {
                span.classList.add(incorrect ? 'ntcfg-theme-char-incorrect' : 'ntcfg-theme-char-active');
            } else if (index < cursorIndex) {
                span.classList.add('ntcfg-theme-char-typed');
                if (options.enableRainbowTypedText && !options.hideTypedText) {
                    span.classList.add('ntcfg-theme-char-rainbow');
                    span.style.animationDuration = `${options.rainbowTypedTextSpeedSeconds}s`;
                    span.style.webkitAnimationDuration = `${options.rainbowTypedTextSpeedSeconds}s`;
                }
            }

            span.textContent = char;

            if (index > cursorIndex && options.overrideForeground) {
                span.style.color = options.foreground;
            } else if (index > cursorIndex && options.darkModeEffective) {
                span.style.color = RACE_OPTIONS_THEME_DARK_MODE_FOREGROUND;
            }
            if (index === cursorIndex && options.overrideForegroundActive) {
                span.style.color = options.foregroundActive;
            } else if (index === cursorIndex && options.darkModeEffective) {
                span.style.color = RACE_OPTIONS_THEME_DARK_MODE_FOREGROUND_ACTIVE;
            }
            if (index < cursorIndex && options.overrideForegroundTyped && !options.enableRainbowTypedText) {
                span.style.color = options.foregroundTyped;
                span.style.opacity = '1';
            } else if (index < cursorIndex && options.darkModeEffective && !options.enableRainbowTypedText) {
                span.style.color = RACE_OPTIONS_THEME_DARK_MODE_FOREGROUND_TYPED;
                span.style.opacity = '1';
            }
            if (index < cursorIndex && options.hideTypedText) {
                span.style.color = 'transparent';
                span.style.webkitTextFillColor = 'transparent';
                span.style.textShadow = 'none';
                span.style.opacity = '0';
            }
            if (index === cursorIndex && !incorrect && options.overrideBackgroundActive) {
                span.style.background = options.backgroundActive;
            } else if (index === cursorIndex && !incorrect && options.darkModeEffective) {
                span.style.background = RACE_OPTIONS_THEME_DARK_MODE_BACKGROUND_ACTIVE;
            }
            if (index === cursorIndex && incorrect && options.overrideBackgroundIncorrect) {
                span.style.background = options.backgroundIncorrect;
            } else if (index === cursorIndex && incorrect && options.darkModeEffective) {
                span.style.background = RACE_OPTIONS_THEME_DARK_MODE_BACKGROUND_INCORRECT;
            }
            if (Number.isFinite(options.fontSizePx)) {
                span.style.fontSize = `${options.fontSizePx}px`;
            }
            span.style.fontWeight = String(options.fontWeight || RACE_OPTIONS_THEME_FONT_WEIGHT_DEFAULT);
            if (options.fontItalic) span.style.fontStyle = 'italic';
            preview.appendChild(span);
        });
    }

    // ── Extracted helpers (previously closures inside mountRaceOptionsSection) ──

    function createSwitch(checked, onChange) {
        const switchRoot = createElement('span', 'ntcfg-switch');
        const input = document.createElement('input');
        input.type = 'checkbox';
        input.checked = !!checked;
        input.setAttribute('role', 'switch');
        const syncSwitchState = () => {
            input.setAttribute('aria-checked', input.checked ? 'true' : 'false');
        };
        const commitSwitchChange = () => {
            syncSwitchState();
            onChange(!!input.checked);
        };
        input.addEventListener('change', commitSwitchChange);
        input.addEventListener('keydown', (event) => {
            if (event.key !== 'Enter') return;
            event.preventDefault();
            if (input.disabled) return;
            input.checked = !input.checked;
            input.dispatchEvent(new Event('change', { bubbles: true }));
        });
        switchRoot.addEventListener('click', (event) => {
            if (event.target === input) {
                event.stopPropagation();
                return;
            }
            event.preventDefault();
            event.stopPropagation();
            if (input.disabled) return;
            input.checked = !input.checked;
            input.dispatchEvent(new Event('change', { bubbles: true }));
        });
        const track = createElement('span', 'ntcfg-switch-track');
        switchRoot.append(input, track);
        syncSwitchState();
        return { root: switchRoot, input, track };
    }

    function createCompactColorControl(initialValue, onChange, wrapperClass = 'ntcfg-theme-color-compact') {
        const colorWrap = createElement('span', wrapperClass);
        const colorInput = document.createElement('input');
        colorInput.type = 'color';
        colorInput.className = 'ntcfg-color-picker';
        const hexInput = document.createElement('input', 'ntcfg-input');
        hexInput.type = 'text';
        hexInput.maxLength = 7;
        hexInput.placeholder = '#FFFFFF';
        const setValue = (value) => {
            const normalized = normalizeRaceOptionsHexColorValue(value, '#FFFFFF');
            colorInput.value = normalized;
            hexInput.value = normalized;
        };
        setValue(initialValue);
        colorInput.addEventListener('input', () => {
            const normalized = normalizeRaceOptionsHexColorValue(colorInput.value, colorInput.value);
            setValue(normalized);
            onChange(normalized);
        });
        const commitHex = () => {
            const normalized = normalizeRaceOptionsHexColorValue(hexInput.value, colorInput.value || '#FFFFFF');
            setValue(normalized);
            onChange(normalized);
        };
        hexInput.addEventListener('change', commitHex);
        hexInput.addEventListener('blur', commitHex);
        colorWrap.append(colorInput, hexInput);
        return { root: colorWrap, colorInput, hexInput, setValue };
    }

    let activeNtcfgConfirmationDialog = null;

    function normalizeNtcfgConfirmationOptions(options) {
        const source = typeof options === 'string' ? { message: options } : (options || {});
        return {
            title: String(source.title || 'Confirm change'),
            message: String(source.message || ''),
            note: String(source.note || source.detail || ''),
            confirmLabel: String(source.confirmLabel || 'Continue'),
            cancelLabel: String(source.cancelLabel || 'Cancel'),
            tone: ['info', 'warning', 'danger'].includes(source.tone) ? source.tone : 'info'
        };
    }

    /** Show a reusable, Mod Suite-styled confirmation dialog. Resolves true only after explicit confirmation. */
    function showNtcfgConfirmationDialog(options) {
        const config = normalizeNtcfgConfirmationOptions(options);
        if (!config.message && !config.note) return Promise.resolve(true);
        if (!document.body) return Promise.resolve(false);

        if (activeNtcfgConfirmationDialog?.close) {
            activeNtcfgConfirmationDialog.close(false, true);
        }

        return new Promise((resolve) => {
            const previouslyFocused = document.activeElement;
            const previousBodyOverflow = document.body.style.overflow;
            const overlay = createElement('div', `ntcfg-confirm-overlay ntcfg-confirm-overlay--${config.tone}`);
            const backdrop = createElement('div', 'ntcfg-confirm-backdrop');
            const dialog = createElement('section', 'ntcfg-confirm-dialog');
            const header = createElement('header', 'ntcfg-confirm-header');
            const headingGroup = createElement('div', 'ntcfg-confirm-heading-group');
            const logo = document.createElement('img');
            const title = createElement('h2', 'ntcfg-confirm-title', config.title);
            const closeButton = createElement('button', 'ntcfg-confirm-close', '×');
            const body = createElement('div', 'ntcfg-confirm-body');
            const message = createElement('p', 'ntcfg-confirm-message', config.message);
            const footer = createElement('footer', 'ntcfg-confirm-footer');
            const cancelButton = createElement('button', 'ntcfg-confirm-button ntcfg-confirm-button--secondary', config.cancelLabel);
            const confirmButton = createElement('button', 'ntcfg-confirm-button ntcfg-confirm-button--primary', config.confirmLabel);
            const titleId = `ntcfg-confirm-title-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
            const descriptionId = `${titleId}-description`;
            let settled = false;

            logo.className = 'ntcfg-confirm-logo';
            logo.src = SUPPORT_MOD_SUITE_ICON_URL;
            logo.alt = '';
            title.id = titleId;
            message.id = descriptionId;
            dialog.setAttribute('role', 'dialog');
            dialog.setAttribute('aria-modal', 'true');
            dialog.setAttribute('aria-labelledby', titleId);
            dialog.setAttribute('aria-describedby', descriptionId);
            dialog.tabIndex = -1;
            closeButton.type = 'button';
            closeButton.setAttribute('aria-label', 'Close confirmation dialog');
            cancelButton.type = 'button';
            confirmButton.type = 'button';

            headingGroup.append(logo, title);
            header.append(headingGroup, closeButton);
            body.appendChild(message);
            if (config.note) {
                const note = createElement('div', 'ntcfg-confirm-note');
                const noteIcon = createElement('span', 'ntcfg-confirm-note-icon', config.tone === 'warning' ? '!' : 'i');
                const noteCopy = createElement('span', 'ntcfg-confirm-note-copy', config.note);
                noteIcon.setAttribute('aria-hidden', 'true');
                note.append(noteIcon, noteCopy);
                body.appendChild(note);
            }
            footer.append(cancelButton, confirmButton);
            dialog.append(header, body, footer);
            overlay.append(backdrop, dialog);

            const getFocusableElements = () => Array.from(dialog.querySelectorAll(
                'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
            )).filter((element) => !element.hidden && element.getAttribute('aria-hidden') !== 'true');

            const restoreFocus = () => {
                if (previouslyFocused?.isConnected && typeof previouslyFocused.focus === 'function') {
                    previouslyFocused.focus({ preventScroll: true });
                }
            };

            const close = (confirmed, immediate = false) => {
                if (settled) return;
                settled = true;
                if (activeNtcfgConfirmationDialog?.overlay === overlay) activeNtcfgConfirmationDialog = null;
                document.removeEventListener('keydown', handleKeydown, true);
                window.removeEventListener('pagehide', handlePageHide);
                document.body.style.overflow = previousBodyOverflow;
                overlay.classList.remove('is-visible');
                if (immediate) overlay.remove();
                else window.setTimeout(() => overlay.remove(), 180);
                restoreFocus();
                resolve(!!confirmed);
            };

            const handleKeydown = (event) => {
                if (event.key === 'Escape') {
                    event.preventDefault();
                    event.stopPropagation();
                    close(false);
                    return;
                }
                if (event.key !== 'Tab') return;
                const focusable = getFocusableElements();
                if (!focusable.length) {
                    event.preventDefault();
                    dialog.focus();
                    return;
                }
                const first = focusable[0];
                const last = focusable[focusable.length - 1];
                if (event.shiftKey && document.activeElement === first) {
                    event.preventDefault();
                    last.focus();
                } else if (!event.shiftKey && document.activeElement === last) {
                    event.preventDefault();
                    first.focus();
                }
            };

            const handlePageHide = () => close(false, true);
            closeButton.addEventListener('click', () => close(false));
            cancelButton.addEventListener('click', () => close(false));
            confirmButton.addEventListener('click', () => close(true));
            backdrop.addEventListener('click', () => close(false));
            document.addEventListener('keydown', handleKeydown, true);
            window.addEventListener('pagehide', handlePageHide, { once: true });
            document.body.style.overflow = 'hidden';
            document.body.appendChild(overlay);
            activeNtcfgConfirmationDialog = { overlay, close };
            window.requestAnimationFrame(() => {
                overlay.classList.add('is-visible');
                cancelButton.focus({ preventScroll: true });
            });
        });
    }

    function genericAppendCheckbox(root, opts, ctx) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const { labelText, key, defaultValue, helpText = '', confirmOnEnable = null, exclusiveWith = null, onChange = null } = opts;
        const row = createElement('label', 'ntcfg-checkbox');
        const copy = createElement('span', 'ntcfg-checkbox-copy');
        copy.appendChild(createElement('span', 'ntcfg-checkbox-label', labelText));
        if (helpText) {
            copy.appendChild(createElement('span', 'ntcfg-checkbox-help', helpText));
        }
        const switchControl = createSwitch(
            readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'toggle' }),
            (checked) => {
                const commitValue = (nextChecked) => {
                    writeModuleFieldValue(module, fieldMap, key, nextChecked, { default: defaultValue, type: 'toggle' });
                    if (typeof onChange === 'function') onChange(nextChecked, row);
                    refreshPreviews();
                };
                const requestEnableConfirmation = (confirmationOptions, onConfirmed) => {
                    switchControl.input.checked = false;
                    switchControl.input.setAttribute('aria-checked', 'false');
                    switchControl.input.disabled = true;
                    switchControl.root.classList.add('is-pending');
                    void showNtcfgConfirmationDialog(confirmationOptions).then((confirmed) => {
                        switchControl.input.disabled = false;
                        switchControl.root.classList.remove('is-pending');
                        if (!confirmed) return;
                        switchControl.input.checked = true;
                        switchControl.input.setAttribute('aria-checked', 'true');
                        onConfirmed();
                    });
                };
                if (checked && exclusiveWith?.key) {
                    const conflictKey = String(exclusiveWith.key);
                    const conflictItem = resolveModuleFieldItem(fieldMap, conflictKey, { default: false, type: 'toggle' });
                    const conflictEnabled = readModuleFieldValue(module, fieldMap, conflictKey, {
                        default: conflictItem.default ?? false,
                        type: 'toggle'
                    }) === true;
                    if (conflictEnabled) {
                        const confirmationOptions = {
                            title: `Switch to ${labelText}?`,
                            message: `${conflictItem.label} is already active. These two modes can't be used at the same time.`,
                            note: `Enabling ${labelText} will turn off ${conflictItem.label}.`,
                            confirmLabel: `Enable ${labelText}`,
                            cancelLabel: 'Cancel',
                            tone: 'warning',
                            ...exclusiveWith
                        };
                        requestEnableConfirmation(confirmationOptions, () => {
                            writeModuleFieldValue(module, fieldMap, conflictKey, false, {
                                default: conflictItem.default ?? false,
                                type: 'toggle'
                            });
                            syncRenderedToggleFieldValue(conflictKey, false);
                            commitValue(true);
                        });
                        return;
                    }
                }
                if (checked && confirmOnEnable) {
                    const confirmationOptions = typeof confirmOnEnable === 'string'
                        ? { title: `Enable ${labelText}?`, message: confirmOnEnable, confirmLabel: 'Enable' }
                        : { title: `Enable ${labelText}?`, confirmLabel: 'Enable', ...confirmOnEnable };
                    requestEnableConfirmation(confirmationOptions, () => commitValue(true));
                    return;
                }
                commitValue(checked);
            }
        );
        switchControl.input.setAttribute('data-field-key', key);
        switchControl.input.setAttribute('data-field-type', 'toggle');
        row.append(copy, switchControl.root);
        root.appendChild(row);
        return { row, input: switchControl.input };
    }

    function genericAppendNumberField(root, opts, ctx) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const { labelText, key, defaultValue, helpText = '', minRecommended = null, warningText = '', min = null, max = null, step = '1', onChange = null } = opts;
        const field = createElement('label', 'ntcfg-field');
        field.appendChild(createElement('div', 'ntcfg-field-title', labelText));

        const input = document.createElement('input');
        input.className = 'ntcfg-input';
        input.type = 'number';
        input.setAttribute('data-field-key', key);
        input.setAttribute('data-field-type', 'number');
        if (min != null) input.min = String(min);
        if (max != null) input.max = String(max);
        if (step != null) input.step = String(step);
        input.value = String(readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'number' }));

        const warning = createElement('div', 'ntcfg-field-help ntcfg-field-warning');
        warning.hidden = true;
        const updateWarningState = (value) => {
            const shouldWarn = Number.isFinite(minRecommended) && Number.isFinite(value) && value < minRecommended;
            warning.hidden = !shouldWarn;
            warning.textContent = shouldWarn ? (warningText || `Warning: values below ${minRecommended} are not recommended.`) : '';
            input.classList.toggle('ntcfg-input-warning', shouldWarn);
        };

        input.addEventListener('input', () => updateWarningState(parseFloat(input.value)));
        input.addEventListener('change', () => {
            const parsed = parseFloat(input.value);
            if (!Number.isFinite(parsed)) return;
            writeModuleFieldValue(module, fieldMap, key, parsed, { default: defaultValue, type: 'number' });
            updateWarningState(parsed);
            if (typeof onChange === 'function') onChange(parsed, field);
            refreshPreviews();
        });

        field.appendChild(input);
        if (helpText) field.appendChild(createElement('div', 'ntcfg-field-help', helpText));
        if (Number.isFinite(minRecommended)) {
            field.appendChild(warning);
            updateWarningState(parseFloat(input.value));
        }
        root.appendChild(field);
        return { field, input, warning };
    }

    function formatRangeDisplayValue(value, item) {
        const scale = Number(item?.displayScale);
        const scaled = Number.isFinite(scale) && scale !== 0 ? Number(value) / scale : Number(value);
        const precision = Number(item?.displayPrecision);
        const decimals = Number.isInteger(precision) ? Math.max(0, Math.min(4, precision)) : 0;
        const safeValue = Number.isFinite(scaled) ? scaled : 0;
        const formatted = safeValue.toFixed(decimals).replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1');
        const unit = String(item?.displayUnit || '').trim();
        return unit ? `${formatted} ${unit}` : formatted;
    }

    function genericAppendRangeField(root, opts, ctx) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const { labelText, key, defaultValue, helpText = '', min = 0, max = 100, step = '1', displayScale = 1, displayUnit = '', displayPrecision = 0, onChange = null } = opts;
        const field = createElement('label', 'ntcfg-field ntcfg-range-field');
        const header = createElement('div', 'ntcfg-range-header');
        header.appendChild(createElement('div', 'ntcfg-field-title ntcfg-range-title', labelText));
        const valuePill = createElement('div', 'ntcfg-range-value');
        header.appendChild(valuePill);

        const input = document.createElement('input');
        input.className = 'ntcfg-range-input';
        input.type = 'range';
        input.setAttribute('data-field-key', key);
        input.setAttribute('data-field-type', 'range');
        if (min != null) input.min = String(min);
        if (max != null) input.max = String(max);
        if (step != null) input.step = String(step);

        const displayMeta = { displayScale, displayUnit, displayPrecision };
        const storedValue = Number(readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'number' }));
        const fallbackValue = Number(defaultValue);
        input.value = String(Number.isFinite(storedValue) ? storedValue : (Number.isFinite(fallbackValue) ? fallbackValue : 0));

        const rangeScale = createElement('div', 'ntcfg-range-scale');
        const minLabel = createElement('span', '', formatRangeDisplayValue(Number(input.min || min || 0), displayMeta));
        const maxLabel = createElement('span', '', formatRangeDisplayValue(Number(input.max || max || 0), displayMeta));
        rangeScale.append(minLabel, maxLabel);

        const updateDisplay = () => {
            const parsed = Number(input.value);
            valuePill.textContent = formatRangeDisplayValue(parsed, displayMeta);
            const minNum = Number(input.min);
            const maxNum = Number(input.max);
            if (Number.isFinite(minNum) && Number.isFinite(maxNum) && maxNum > minNum) {
                const pct = ((parsed - minNum) / (maxNum - minNum)) * 100;
                input.style.setProperty('--ntcfg-range-fill', `${Math.max(0, Math.min(100, pct))}%`);
            }
        };

        input.addEventListener('input', updateDisplay);
        input.addEventListener('change', () => {
            const parsed = Number(input.value);
            if (!Number.isFinite(parsed)) return;
            writeModuleFieldValue(module, fieldMap, key, parsed, { default: defaultValue, type: 'number' });
            updateDisplay();
            if (typeof onChange === 'function') onChange(parsed, field);
            refreshPreviews();
        });

        updateDisplay();
        field.append(header, input, rangeScale);
        if (helpText) field.appendChild(createElement('div', 'ntcfg-field-help', helpText));
        root.appendChild(field);
        return { field, input };
    }

    function genericAppendSelectField(root, opts, ctx) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const { labelText, key, defaultValue, options = [], helpText = '', onChange = null } = opts;
        const field = createElement('label', 'ntcfg-field');
        field.appendChild(createElement('div', 'ntcfg-field-title', labelText));
        const select = createElement('select', 'ntcfg-input');
        select.setAttribute('data-field-key', key);
        select.setAttribute('data-field-type', 'select');

        const safeOptions = Array.isArray(options) ? options.filter(Boolean) : [];
        const normalizeSelectedValue = (value) => {
            const raw = String(value ?? '').trim();
            const matched = safeOptions.find((option) => String(option.value) === raw);
            if (matched) return matched.value;
            const fallback = safeOptions.find((option) => String(option.value) === String(defaultValue).trim());
            return fallback ? fallback.value : (safeOptions[0]?.value ?? defaultValue);
        };

        safeOptions.forEach((optionDef) => {
            const option = document.createElement('option');
            option.value = String(optionDef.value);
            option.textContent = String(optionDef.label ?? optionDef.value);
            select.appendChild(option);
        });
        select.value = String(normalizeSelectedValue(readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'select', options })));
        select.addEventListener('change', () => {
            const normalized = normalizeSelectedValue(select.value);
            select.value = String(normalized);
            writeModuleFieldValue(module, fieldMap, key, normalized, { default: defaultValue, type: 'select', options });
            if (typeof onChange === 'function') onChange(normalized, field);
            refreshPreviews();
            refreshActiveModMenuFieldViews();
        });

        field.appendChild(select);
        if (helpText) field.appendChild(createElement('div', 'ntcfg-field-help', helpText));
        root.appendChild(field);
        return { field, select };
    }

    function genericAppendMultiSelectField(root, opts, ctx) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const { labelText, key, defaultValue = [], options = [], helpText = '', onChange = null } = opts;
        const field = createElement('div', 'ntcfg-field');
        field.appendChild(createElement('div', 'ntcfg-field-title', labelText));

        const safeOptions = Array.isArray(options)
            ? options.filter((option) => option && typeof option.value !== 'undefined')
            : [];
        const allowedValues = new Set(safeOptions.map((option) => String(option.value)));
        const normalizeSelectedValues = (value) => {
            const source = Array.isArray(value) ? value : [];
            const normalized = source
                .map((entry) => String(entry))
                .filter((entry, index, array) => allowedValues.has(entry) && array.indexOf(entry) === index);
            if (normalized.length > 0) return normalized;
            return Array.isArray(defaultValue)
                ? defaultValue.map((entry) => String(entry)).filter((entry, index, array) => allowedValues.has(entry) && array.indexOf(entry) === index)
                : [];
        };

        const list = createElement('div', 'ntcfg-multi-options');
        const status = createElement('div', 'ntcfg-multi-count');
        const inputs = new Map();

        const syncInputState = (value) => {
            const selectedValues = normalizeSelectedValues(value);
            const selectedSet = new Set(selectedValues);
            safeOptions.forEach((option) => {
                const input = inputs.get(String(option.value));
                if (input) input.checked = selectedSet.has(String(option.value));
            });
            status.textContent = `${selectedValues.length} selected`;
            if (typeof onChange === 'function') onChange(selectedValues, field);
        };

        safeOptions.forEach((optionDef) => {
            const optionValue = String(optionDef.value);
            const row = createElement('label', 'ntcfg-checkbox ntcfg-multi-option');
            const input = document.createElement('input');
            input.type = 'checkbox';
            input.value = optionValue;
            input.setAttribute('data-field-key', key);
            input.setAttribute('data-field-type', 'multi-select');
            inputs.set(optionValue, input);

            const label = createElement('span', 'ntcfg-checkbox-label', String(optionDef.label ?? optionDef.value));
            input.addEventListener('change', () => {
                const selectedValues = safeOptions
                    .map((entry) => String(entry.value))
                    .filter((trackId) => inputs.get(trackId)?.checked);
                if (selectedValues.length === 0) {
                    input.checked = true;
                    return;
                }
                writeModuleFieldValue(module, fieldMap, key, selectedValues, {
                    default: defaultValue,
                    type: 'multi-select',
                    options
                });
                syncInputState(selectedValues);
                refreshPreviews();
            });

            row.append(input, label);
            list.appendChild(row);
        });

        syncInputState(readModuleFieldValue(module, fieldMap, key, {
            default: defaultValue,
            type: 'multi-select',
            options
        }));

        field.append(list, status);
        if (helpText) field.appendChild(createElement('div', 'ntcfg-field-help', helpText));
        root.appendChild(field);
        return { field, inputs };
    }

    function genericAppendTextField(root, opts, ctx) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const { labelText, key, defaultValue = '', helpText = '', placeholder = '', onChange = null } = opts;
        const field = createElement('label', 'ntcfg-field');
        field.appendChild(createElement('div', 'ntcfg-field-title', labelText));

        const inputRow = createElement('div', 'ntcfg-text-input-row');
        inputRow.style.cssText = 'display:flex;gap:6px;align-items:center;';

        const input = document.createElement('input');
        input.className = 'ntcfg-input';
        input.type = 'text';
        input.setAttribute('data-field-key', key);
        input.setAttribute('data-field-type', 'text');
        input.placeholder = placeholder || '';
        input.spellcheck = false;
        input.value = String(readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'text' }) ?? '');

        const saveBtn = createElement('button', 'ntcfg-action ntcfg-action--primary ntcfg-text-save-btn', 'Save');
        saveBtn.type = 'button';
        saveBtn.style.cssText = 'padding:6px 14px;font-size:12px;white-space:nowrap;flex-shrink:0;';
        const clearBtn = createElement('button', 'ntcfg-action ntcfg-action--secondary ntcfg-text-clear-btn', 'Clear');
        clearBtn.type = 'button';
        clearBtn.style.cssText = 'padding:6px 10px;font-size:12px;white-space:nowrap;flex-shrink:0;';

        const commit = (value) => {
            writeModuleFieldValue(module, fieldMap, key, value, { default: defaultValue, type: 'text' });
            if (typeof onChange === 'function') onChange(value, field);
            refreshPreviews();
        };

        saveBtn.addEventListener('click', () => {
            commit(input.value.trim());
            showToast('Saved.');
        });

        clearBtn.addEventListener('click', () => {
            input.value = '';
            commit('');
            showToast('Cleared.');
        });

        // Also save on Enter key
        input.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault();
                commit(input.value.trim());
                showToast('Saved.');
            }
        });

        inputRow.append(input, saveBtn, clearBtn);
        field.appendChild(inputRow);
        if (helpText) field.appendChild(createElement('div', 'ntcfg-field-help', helpText));
        root.appendChild(field);
        return { field, input, saveBtn, clearBtn };
    }

    const IS_MAC = navigator.userAgentData?.platform === 'macOS' || /Mac|iPhone|iPad|iPod/i.test(navigator.platform || '');

    const MODIFIER_LABELS = IS_MAC
        ? { alt: '\u2325 Option +', ctrl: '\u2303 Ctrl +', shift: '\u21E7 Shift +', meta: '\u2318 Cmd +' }
        : { alt: 'Alt +', ctrl: 'Ctrl +', shift: 'Shift +', meta: 'Win +' };

    const MODIFIER_OPTIONS = IS_MAC
        ? [
            { value: 'alt', label: '\u2325 Option' },
            { value: 'ctrl', label: '\u2303 Ctrl' },
            { value: 'shift', label: '\u21E7 Shift' },
            { value: 'meta', label: '\u2318 Cmd' }
        ]
        : [
            { value: 'alt', label: 'Alt' },
            { value: 'ctrl', label: 'Ctrl' },
            { value: 'shift', label: 'Shift' },
            { value: 'meta', label: 'Win' }
        ];
    const MODIFIER2_OPTIONS = [
        { value: 'none', label: 'None' },
        ...MODIFIER_OPTIONS
    ];

    const SHORTCUT_CODE_TO_KEY = Object.freeze({
        Backquote: '`',
        Minus: '-',
        Equal: '=',
        BracketLeft: '[',
        BracketRight: ']',
        Backslash: '\\',
        Semicolon: ';',
        Quote: '\'',
        Comma: ',',
        Period: '.',
        Slash: '/',
        Space: ' ',
        NumpadDecimal: '.',
        NumpadDivide: '/',
        NumpadMultiply: '*',
        NumpadSubtract: '-',
        NumpadAdd: '+'
    });

    const SHORTCUT_CODE_LABELS = Object.freeze({
        Space: 'Space',
        Enter: 'Enter',
        NumpadEnter: 'Numpad Enter',
        Escape: 'Esc',
        Tab: 'Tab',
        Backspace: 'Backspace',
        Delete: 'Delete',
        Insert: 'Insert',
        Home: 'Home',
        End: 'End',
        PageUp: 'Page Up',
        PageDown: 'Page Down',
        ArrowUp: 'Up',
        ArrowDown: 'Down',
        ArrowLeft: 'Left',
        ArrowRight: 'Right'
    });

    const SHORTCUT_KEY_TO_CODE = Object.freeze({
        '`': 'Backquote',
        '-': 'Minus',
        '=': 'Equal',
        '[': 'BracketLeft',
        ']': 'BracketRight',
        '\\': 'Backslash',
        ';': 'Semicolon',
        '\'': 'Quote',
        ',': 'Comma',
        '.': 'Period',
        '/': 'Slash',
        ' ': 'Space',
        '*': 'NumpadMultiply',
        '+': 'NumpadAdd',
        ESC: 'Escape',
        ESCAPE: 'Escape',
        TAB: 'Tab',
        ENTER: 'Enter',
        RETURN: 'Enter',
        SPACE: 'Space',
        BACKSPACE: 'Backspace',
        DELETE: 'Delete',
        DEL: 'Delete',
        INSERT: 'Insert',
        HOME: 'Home',
        END: 'End',
        PAGEUP: 'PageUp',
        PAGEDOWN: 'PageDown',
        UP: 'ArrowUp',
        ARROWUP: 'ArrowUp',
        DOWN: 'ArrowDown',
        ARROWDOWN: 'ArrowDown',
        LEFT: 'ArrowLeft',
        ARROWLEFT: 'ArrowLeft',
        RIGHT: 'ArrowRight',
        ARROWRIGHT: 'ArrowRight'
    });

    const SHORTCUT_MODIFIER_CODES = new Set([
        'ShiftLeft', 'ShiftRight',
        'ControlLeft', 'ControlRight',
        'AltLeft', 'AltRight',
        'MetaLeft', 'MetaRight'
    ]);

    function getShortcutDisplayLabelFromCode(code) {
        const normalized = String(code || '').trim();
        if (!normalized) return '';
        if (/^Key[A-Z]$/i.test(normalized)) return normalized.slice(3).toUpperCase();
        if (/^Digit[0-9]$/.test(normalized)) return normalized.slice(5);
        if (/^Numpad[0-9]$/.test(normalized)) return normalized.slice(6);
        if (SHORTCUT_CODE_TO_KEY[normalized]) {
            return normalized === 'Space' ? 'Space' : SHORTCUT_CODE_TO_KEY[normalized];
        }
        if (/^F\d{1,2}$/i.test(normalized)) return normalized.toUpperCase();
        return SHORTCUT_CODE_LABELS[normalized] || normalized;
    }

    function inferShortcutCodeFromStoredKey(value) {
        const raw = String(value ?? '');
        if (!raw && raw !== ' ') return '';
        if (raw === ' ') return 'Space';

        const trimmed = raw.trim();
        if (!trimmed) return '';
        if (/^[A-Z]$/i.test(trimmed)) return `Key${trimmed.toUpperCase()}`;
        if (/^[0-9]$/.test(trimmed)) return `Digit${trimmed}`;
        if (/^F\d{1,2}$/i.test(trimmed)) return trimmed.toUpperCase();
        if (SHORTCUT_KEY_TO_CODE[trimmed]) return SHORTCUT_KEY_TO_CODE[trimmed];

        const upper = trimmed.toUpperCase();
        return SHORTCUT_KEY_TO_CODE[upper] || '';
    }

    function normalizeShortcutCodeValue(code, fallbackKey = '') {
        const rawCode = String(code || '').trim();
        if (!rawCode) return inferShortcutCodeFromStoredKey(fallbackKey);
        if (/^Key[A-Z]$/i.test(rawCode)) return `Key${rawCode.slice(3).toUpperCase()}`;
        if (/^Digit[0-9]$/.test(rawCode)) return rawCode;
        if (/^Numpad[0-9]$/.test(rawCode)) return rawCode;
        if (/^F\d{1,2}$/i.test(rawCode)) return rawCode.toUpperCase();
        return rawCode;
    }

    function normalizeShortcutStoredKey(value, fallbackCode = '') {
        const fallbackLabel = getShortcutDisplayLabelFromCode(fallbackCode);
        if (fallbackLabel) return fallbackLabel;

        const raw = String(value ?? '');
        if (!raw && raw !== ' ') return '';
        if (raw === ' ') return 'Space';

        const trimmed = raw.trim();
        if (!trimmed) return '';
        if (trimmed.length === 1) {
            return /^[a-z]$/i.test(trimmed) ? trimmed.toUpperCase() : trimmed;
        }

        const inferredCode = inferShortcutCodeFromStoredKey(trimmed);
        if (inferredCode) return getShortcutDisplayLabelFromCode(inferredCode);
        if (/^Key[A-Z]$/i.test(trimmed) || /^Digit[0-9]$/.test(trimmed) || /^Numpad[0-9]$/.test(trimmed) || /^F\d{1,2}$/i.test(trimmed)) {
            return getShortcutDisplayLabelFromCode(trimmed);
        }
        return trimmed;
    }

    function normalizeShortcutBindingEntry(entry = {}) {
        const path = String(entry.path || '');
        const action = String(entry.action || (path.startsWith('toggle:') ? 'toggle' : path.startsWith('action:') ? 'trigger' : 'navigate'));
        const code = normalizeShortcutCodeValue(entry.code, entry.key);
        const key = normalizeShortcutStoredKey(entry.key, code);
        return {
            code,
            key,
            path,
            mod1: String(entry.mod1 || 'alt'),
            mod2: String(entry.mod2 || 'none'),
            action,
            targetScriptId: String(entry.targetScriptId || ''),
            targetValue: String(entry.targetValue || '')
        };
    }

    function getShortcutBindingId(entry = {}) {
        const normalized = normalizeShortcutBindingEntry(entry);
        const primary = normalized.code || normalized.key;
        return primary ? [normalized.mod1, normalized.mod2, primary].join('::') : '';
    }

    function getShortcutCaptureLabel(entry = {}) {
        const normalized = normalizeShortcutBindingEntry(entry);
        return normalized.key || 'Press Key';
    }

    function parseShortcutTarget(path) {
        const raw = String(path || '');
        if (!raw) return { mode: 'navigate', scriptId: '', value: '' };
        if (raw.startsWith('toggle:') || raw.startsWith('action:')) {
            const parts = raw.split(':');
            if (parts.length >= 3) {
                return {
                    mode: raw.startsWith('toggle:') ? 'toggle' : 'trigger',
                    scriptId: parts[1],
                    value: `${parts[0]}:${parts[1]}:${parts.slice(2).join(':')}`
                };
            }
        }
        return { mode: 'navigate', scriptId: '', value: '' };
    }

    /** Gather all shortcut-capable targets grouped by script. */
    function getShortcutTargetsByScript() {
        const scripts = [];
        try {
            Object.keys(localStorage).forEach((lsKey) => {
                if (!lsKey.startsWith(MANIFEST_PREFIX)) return;
                const manifest = readJson(lsKey, null);
                if (!manifest || !manifest.id || !manifest.settings) return;
                const modLabel = manifest.name || manifest.id;
                const toggles = [];
                const actions = [];
                Object.entries(manifest.settings).forEach(([settingKey, meta]) => {
                    if (meta?.type === 'boolean') {
                        toggles.push({
                            key: settingKey,
                            label: meta.label || settingKey,
                            value: `toggle:${manifest.id}:${settingKey}`
                        });
                    }
                    if (meta?.type === 'action') {
                        actions.push({
                            key: settingKey,
                            label: meta.label || settingKey,
                            value: `action:${manifest.id}:${settingKey}`
                        });
                    }
                });
                if (toggles.length > 0 || actions.length > 0) {
                    scripts.push({ id: manifest.id, label: modLabel, toggles, actions });
                }
            });
        } catch { /* ignore */ }
        scripts.sort((a, b) => a.label.localeCompare(b.label));
        return scripts;
    }

    function genericAppendKeymapField(root, opts, ctx) {
        const { module, fieldMap, refreshPreviewFns, refreshPreviews } = ctx;
        const parentRefresh = refreshPreviews || (() => {});
        const { labelText, key, defaultValue = [], helpText = '', onChange = null } = opts;

        const field = createElement('label', 'ntcfg-field');
        field.appendChild(createElement('div', 'ntcfg-field-title', labelText));

        const container = createElement('div', 'ntcfg-keymap-container');

        const currentValue = (() => {
            const raw = readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'keymap' });
            return Array.isArray(raw) ? raw : defaultValue;
        })();

        const state = {
            entries: currentValue.map((entry) => {
                const normalized = normalizeShortcutBindingEntry(entry);
                const target = parseShortcutTarget(normalized.path);
                normalized.action = target.mode === 'navigate' ? normalized.action : target.mode;
                normalized.targetScriptId = target.scriptId;
                normalized.targetValue = target.value;
                return normalized;
            })
        };

        const commit = () => {
            const normalizedEntries = state.entries.map((entry) => {
                const normalized = normalizeShortcutBindingEntry(entry);
                normalized.path = normalized.action === 'navigate'
                    ? String(entry.path || '').trim()
                    : String(entry.targetValue || entry.path || '');
                return normalized;
            });

            const bindingCounts = new Map();
            normalizedEntries.forEach((entry) => {
                const bindingId = getShortcutBindingId(entry);
                if (!bindingId) return;
                bindingCounts.set(bindingId, (bindingCounts.get(bindingId) || 0) + 1);
            });

            const hasDuplicates = normalizedEntries.some((entry) => {
                const bindingId = getShortcutBindingId(entry);
                return bindingId && bindingCounts.get(bindingId) > 1;
            });
            if (hasDuplicates) {
                showToast('Resolve duplicate shortcut bindings before saving.');
                return;
            }

            const clean = normalizedEntries
                .filter((entry) => (entry.code || entry.key) && entry.path)
                .map((entry) => ({
                    code: entry.code,
                    key: entry.key,
                    path: entry.path,
                    mod1: entry.mod1,
                    mod2: entry.mod2,
                    action: entry.action
                }));
            writeModuleFieldValue(module, fieldMap, key, clean, { default: defaultValue, type: 'keymap' });
            if (typeof onChange === 'function') onChange(clean, field);
            parentRefresh();
        };

        const buildModSelect = (options, value, onChangeHandler) => {
            const select = createElement('select', 'ntcfg-input ntcfg-keymap-mod-select');
            options.forEach(opt => {
                const option = document.createElement('option');
                option.value = opt.value;
                option.textContent = opt.label;
                select.appendChild(option);
            });
            select.value = value;
            select.addEventListener('change', () => onChangeHandler(select.value));
            return select;
        };

        const getTargetsForMode = (scriptData, scriptId, mode) => {
            const script = scriptData.find((entry) => entry.id === scriptId);
            if (!script) return [];
            if (mode === 'toggle') return script.toggles || [];
            if (mode === 'trigger') return script.actions || [];
            return [];
        };

        const renderRows = () => {
            container.innerHTML = '';
            const scriptData = getShortcutTargetsByScript();
            const normalizedEntries = state.entries.map((entry) => {
                const normalized = normalizeShortcutBindingEntry(entry);
                normalized.path = normalized.action === 'navigate'
                    ? String(entry.path || '')
                    : String(entry.targetValue || entry.path || '');
                return normalized;
            });
            const bindingCounts = new Map();
            normalizedEntries.forEach((entry) => {
                const bindingId = getShortcutBindingId(entry);
                if (!bindingId) return;
                bindingCounts.set(bindingId, (bindingCounts.get(bindingId) || 0) + 1);
            });
            let hasDuplicateBindings = false;

            state.entries.forEach((entry, idx) => {
                entry.code = normalizeShortcutCodeValue(entry.code, entry.key);
                entry.key = normalizeShortcutStoredKey(entry.key, entry.code);

                // Main row: modifiers + key + action mode
                const row = createElement('div', 'ntcfg-keymap-row');

                const mod1Select = buildModSelect(MODIFIER_OPTIONS, entry.mod1, (val) => {
                    entry.mod1 = val;
                    renderRows();
                });
                const plus1 = createElement('span', 'ntcfg-keymap-plus', '+');
                const mod2Select = buildModSelect(MODIFIER2_OPTIONS, entry.mod2, (val) => {
                    entry.mod2 = val;
                    renderRows();
                });
                const plus2 = createElement('span', 'ntcfg-keymap-plus', '+');

                const keyButton = createElement('button', 'ntcfg-input ntcfg-keymap-key ntcfg-keymap-keycapture', getShortcutCaptureLabel(entry));
                keyButton.type = 'button';
                keyButton.title = entry.code
                    ? `Primary key: ${entry.code}. Press Backspace/Delete to clear.`
                    : 'Click and press any non-modifier key. Press Backspace/Delete to clear.';
                const syncKeyButtonLabel = (message = '') => {
                    keyButton.textContent = message || getShortcutCaptureLabel(entry);
                };
                keyButton.addEventListener('click', () => {
                    keyButton.classList.add('is-capturing');
                    syncKeyButtonLabel('Press Key');
                    keyButton.focus();
                });
                keyButton.addEventListener('blur', () => {
                    keyButton.classList.remove('is-capturing');
                    syncKeyButtonLabel();
                });
                keyButton.addEventListener('keydown', (event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    const pressedCode = String(event.code || '');
                    if (SHORTCUT_MODIFIER_CODES.has(pressedCode)) {
                        syncKeyButtonLabel('Pick Non-Modifier');
                        return;
                    }
                    if (pressedCode === 'Backspace' || pressedCode === 'Delete') {
                        entry.code = '';
                        entry.key = '';
                        renderRows();
                        return;
                    }
                    const normalizedCode = normalizeShortcutCodeValue(pressedCode, event.key);
                    if (!normalizedCode) {
                        syncKeyButtonLabel('Unsupported Key');
                        return;
                    }
                    entry.code = normalizedCode;
                    entry.key = normalizeShortcutStoredKey(event.key, normalizedCode);
                    renderRows();
                });

                // Action mode selector
                const actionSelect = createElement('select', 'ntcfg-input ntcfg-keymap-action-select');
                [
                    { value: 'navigate', label: 'URL' },
                    { value: 'toggle', label: 'Toggle' },
                    { value: 'trigger', label: 'Action' }
                ].forEach(opt => {
                    const option = document.createElement('option');
                    option.value = opt.value;
                    option.textContent = opt.label;
                    actionSelect.appendChild(option);
                });
                actionSelect.value = entry.action;

                // Path input (for navigate mode)
                const pathInput = document.createElement('input');
                pathInput.className = 'ntcfg-input ntcfg-keymap-path';
                pathInput.type = 'text';
                pathInput.value = entry.action === 'navigate' ? entry.path : '';
                pathInput.placeholder = '/page-url';
                pathInput.spellcheck = false;

                // Script selector (for toggle mode)
                const scriptSelect = createElement('select', 'ntcfg-input ntcfg-keymap-script-select');
                const emptyScriptOpt = document.createElement('option');
                emptyScriptOpt.value = '';
                emptyScriptOpt.textContent = 'Select script...';
                scriptSelect.appendChild(emptyScriptOpt);
                scriptData.forEach(s => {
                    const option = document.createElement('option');
                    option.value = s.id;
                    option.textContent = s.label;
                    scriptSelect.appendChild(option);
                });

                // Setting selector (for toggle mode)
                const settingSelect = createElement('select', 'ntcfg-input ntcfg-keymap-setting-select');

                const targetInfo = parseShortcutTarget(entry.path);
                if (!entry.targetScriptId && targetInfo.scriptId) entry.targetScriptId = targetInfo.scriptId;
                if (!entry.targetValue && targetInfo.value) entry.targetValue = targetInfo.value;
                scriptSelect.value = entry.targetScriptId || '';

                const populateSettings = (scriptId, mode) => {
                    settingSelect.innerHTML = '';
                    const emptyOpt = document.createElement('option');
                    emptyOpt.value = '';
                    emptyOpt.textContent = mode === 'trigger' ? 'Select action...' : 'Select setting...';
                    settingSelect.appendChild(emptyOpt);
                    const targets = getTargetsForMode(scriptData, scriptId, mode);
                    targets.forEach((target) => {
                        const option = document.createElement('option');
                        option.value = target.value;
                        option.textContent = target.label;
                        settingSelect.appendChild(option);
                    });
                };

                Array.from(scriptSelect.options).forEach((option, optionIndex) => {
                    if (optionIndex === 0) return;
                    option.disabled = getTargetsForMode(scriptData, option.value, entry.action).length === 0;
                });
                populateSettings(entry.targetScriptId || '', entry.action);
                if (entry.targetValue) settingSelect.value = entry.targetValue;

                scriptSelect.addEventListener('change', () => {
                    entry.targetScriptId = scriptSelect.value;
                    entry.targetValue = '';
                    entry.path = '';
                    renderRows();
                });

                settingSelect.addEventListener('change', () => {
                    entry.targetValue = settingSelect.value;
                    entry.path = settingSelect.value;
                    renderRows();
                });

                // Toggle visibility based on action mode
                const updateActionMode = () => {
                    if (entry.action === 'navigate') {
                        pathInput.style.display = '';
                        scriptSelect.style.display = 'none';
                        settingSelect.style.display = 'none';
                    } else {
                        pathInput.style.display = 'none';
                        scriptSelect.style.display = '';
                        settingSelect.style.display = '';
                    }
                };
                updateActionMode();

                actionSelect.addEventListener('change', () => {
                    entry.action = actionSelect.value;
                    entry.path = '';
                    entry.targetScriptId = '';
                    entry.targetValue = '';
                    pathInput.value = '';
                    renderRows();
                });

                const removeBtn = createElement('button', 'ntcfg-action ntcfg-action--danger ntcfg-keymap-remove', '\u00D7');
                removeBtn.type = 'button';
                removeBtn.title = 'Remove shortcut';

                pathInput.addEventListener('input', () => {
                    entry.path = pathInput.value;
                });
                pathInput.addEventListener('change', () => {
                    entry.path = pathInput.value.trim();
                    renderRows();
                });
                removeBtn.addEventListener('click', () => {
                    state.entries.splice(idx, 1);
                    renderRows();
                });

                const equals = createElement('span', 'ntcfg-keymap-plus', '=');
                row.append(mod1Select, plus1, mod2Select, plus2, keyButton, equals, actionSelect, pathInput, scriptSelect, settingSelect, removeBtn);
                const issues = [];
                const bindingId = getShortcutBindingId(entry);
                if (bindingId && (bindingCounts.get(bindingId) || 0) > 1) {
                    hasDuplicateBindings = true;
                    issues.push('Duplicate binding. Choose a different modifier or key.');
                }
                if (entry.action === 'navigate' && entry.path && !String(entry.path).trim().startsWith('/')) {
                    issues.push('Navigation targets should usually start with /.');
                }
                if (entry.action !== 'navigate' && entry.targetScriptId && getTargetsForMode(scriptData, entry.targetScriptId, entry.action).length === 0) {
                    issues.push('This script does not expose a compatible shortcut target for that action type.');
                }
                if (issues.length > 0) {
                    row.classList.add('is-invalid');
                }
                container.appendChild(row);
                if (issues.length > 0) {
                    container.appendChild(createElement('div', 'ntcfg-keymap-warning', issues.join(' ')));
                }
            });

            // Add button row
            const addRow = createElement('div', 'ntcfg-keymap-add-row');
            const addBtn = createElement('button', 'ntcfg-action ntcfg-action--secondary ntcfg-keymap-add', '+ Add Shortcut');
            addBtn.type = 'button';
            addBtn.addEventListener('click', () => {
                state.entries.push({
                    code: '',
                    key: '',
                    path: '',
                    mod1: 'alt',
                    mod2: 'none',
                    action: 'navigate',
                    targetScriptId: '',
                    targetValue: ''
                });
                renderRows();
            });

            const saveBtn = createElement('button', 'ntcfg-action ntcfg-action--primary ntcfg-keymap-save', 'Save');
            saveBtn.type = 'button';
            saveBtn.disabled = hasDuplicateBindings;
            saveBtn.addEventListener('click', () => {
                if (hasDuplicateBindings) {
                    showToast('Resolve duplicate shortcut bindings before saving.');
                    return;
                }
                commit();
                showToast('Shortcuts saved.');
            });

            addRow.append(addBtn, saveBtn);
            container.appendChild(addRow);
            if (hasDuplicateBindings) {
                container.appendChild(createElement('div', 'ntcfg-field-help ntcfg-keymap-summary', 'Duplicate bindings were found. Fix them before saving.'));
            }
        };

        renderRows();
        field.appendChild(container);
        if (helpText) field.appendChild(createElement('div', 'ntcfg-field-help', helpText));
        root.appendChild(field);

        return { field, container };
    }

    function genericAppendResetButton(root, labelText, defaults, ctx) {
        const { module, fieldMap, rerender } = ctx;
        const resetButton = createElement('button', 'ntcfg-action ntcfg-inline-action', labelText);
        resetButton.type = 'button';
        resetButton.addEventListener('click', () => {
            Object.entries(defaults).forEach(([settingKey, value]) => {
                writeModuleFieldValue(module, fieldMap, settingKey, value);
            });
            rerender();
        });
        root.appendChild(resetButton);
    }

    function genericAppendThemeSettingToggle(root, labelText, key, defaultValue, ctx, onChange = null) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const row = createElement('div', 'ntcfg-theme-setting');
        row.appendChild(createElement('div', 'ntcfg-theme-setting-title', labelText));
        const controls = createElement('div', 'ntcfg-theme-setting-controls');
        const switchControl = createSwitch(
            readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'toggle' }),
            (checked) => {
                writeModuleFieldValue(module, fieldMap, key, checked, { default: defaultValue, type: 'toggle' });
                if (typeof onChange === 'function') onChange(checked, row);
                refreshPreviews();
            }
        );
        switchControl.input.setAttribute('data-field-key', key);
        switchControl.input.setAttribute('data-field-type', 'toggle');
        switchControl.input.setAttribute('aria-label', labelText);
        controls.appendChild(switchControl.root);
        row.appendChild(controls);
        root.appendChild(row);
        return { row, input: switchControl.input, controls };
    }

    function genericAppendThemeSettingColor(root, labelText, toggleKey, toggleDefault, colorKey, colorDefault, ctx) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const row = createElement('div', 'ntcfg-theme-setting');
        row.appendChild(createElement('div', 'ntcfg-theme-setting-title', labelText));
        const controls = createElement('div', 'ntcfg-theme-setting-controls');
        const colorControl = createCompactColorControl(
            readModuleFieldValue(module, fieldMap, colorKey, { default: colorDefault, type: 'color' }),
            (value) => {
                writeModuleFieldValue(module, fieldMap, colorKey, value, { default: colorDefault, type: 'color' });
                refreshPreviews();
            }
        );
        const switchControl = createSwitch(
            readModuleFieldValue(module, fieldMap, toggleKey, { default: toggleDefault, type: 'toggle' }),
            (checked) => {
                writeModuleFieldValue(module, fieldMap, toggleKey, checked, { default: toggleDefault, type: 'toggle' });
                refreshPreviews();
            }
        );
        colorControl.colorInput.setAttribute('data-field-key', colorKey);
        colorControl.colorInput.setAttribute('data-field-type', 'color');
        colorControl.hexInput.setAttribute('data-field-key', colorKey);
        colorControl.hexInput.setAttribute('data-field-type', 'color');
        switchControl.input.setAttribute('data-field-key', toggleKey);
        switchControl.input.setAttribute('data-field-type', 'toggle');
        controls.append(colorControl.root, switchControl.root);
        row.append(controls);
        root.appendChild(row);
    }

    function genericAppendThemeSettingSelect(root, labelText, key, defaultValue, options, ctx, className = 'ntcfg-input ntcfg-theme-input-compact ntcfg-theme-input-select', onChange = null) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const row = createElement('div', 'ntcfg-theme-setting');
        row.appendChild(createElement('div', 'ntcfg-theme-setting-title', labelText));
        const controls = createElement('div', 'ntcfg-theme-setting-controls');
        const select = createElement('select', className);
        select.setAttribute('data-field-key', key);
        select.setAttribute('data-field-type', 'select');
        const safeOptions = Array.isArray(options) ? options.filter(Boolean) : [];
        const normalizeSelectedValue = (value) => {
            const raw = String(value ?? '').trim();
            const match = safeOptions.find((option) => String(option.value) === raw);
            if (match) return match.value;
            const fallback = safeOptions.find((option) => String(option.value) === String(defaultValue).trim());
            return fallback ? fallback.value : (safeOptions[0]?.value ?? defaultValue);
        };
        safeOptions.forEach((optionDef) => {
            const option = document.createElement('option');
            option.value = String(optionDef.value);
            option.textContent = String(optionDef.label ?? optionDef.value);
            select.appendChild(option);
        });
        select.value = String(normalizeSelectedValue(readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'select', options })));
        select.addEventListener('change', () => {
            const normalized = normalizeSelectedValue(select.value);
            select.value = String(normalized);
            writeModuleFieldValue(module, fieldMap, key, normalized, { default: defaultValue, type: 'select', options });
            if (typeof onChange === 'function') onChange(normalized, row, select);
            refreshPreviews();
        });
        controls.appendChild(select);
        row.appendChild(controls);
        root.appendChild(row);
        return { row, select, controls };
    }

    function genericAppendThemeSettingNumber(root, labelText, key, defaultValue, config, ctx) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const { min = 1, max = 60, step = 1, presets = [], normalizeValue = (value, fallback) => {
            const parsed = Number(value);
            return Number.isFinite(parsed) ? parsed : Number(fallback);
        } } = config || {};

        const row = createElement('div', 'ntcfg-theme-setting');
        row.appendChild(createElement('div', 'ntcfg-theme-setting-title', labelText));
        const controls = createElement('div', 'ntcfg-theme-setting-controls');

        const input = createElement('input', 'ntcfg-input ntcfg-theme-input-compact ntcfg-theme-input-number');
        input.type = 'number';
        input.min = String(min);
        input.max = String(max);
        input.step = String(step);
        input.setAttribute('data-field-key', key);
        input.setAttribute('data-field-type', 'number');
        input.value = String(normalizeValue(readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'number' }), defaultValue));

        const presetSelect = createElement('select', 'ntcfg-input ntcfg-theme-input-compact ntcfg-theme-input-preset');
        const customPresetValue = '__custom__';
        const customOption = document.createElement('option');
        customOption.value = customPresetValue;
        customOption.textContent = 'Custom';
        presetSelect.appendChild(customOption);

        const normalizedPresets = Array.isArray(presets)
            ? presets.map((preset) => {
                if (!preset || typeof preset !== 'object') return null;
                const value = normalizeValue(preset.value, defaultValue);
                return { value, label: String(preset.label || `${value}s`).trim() };
            }).filter(Boolean)
            : [];

        normalizedPresets.forEach((preset) => {
            const option = document.createElement('option');
            option.value = String(preset.value);
            option.textContent = `${preset.label} (${preset.value}s)`;
            presetSelect.appendChild(option);
        });

        const syncPresetFromValue = (value) => {
            const matched = normalizedPresets.find((preset) => preset.value === value);
            presetSelect.value = matched ? String(matched.value) : customPresetValue;
        };

        const save = () => {
            const normalized = normalizeValue(input.value, defaultValue);
            input.value = String(normalized);
            syncPresetFromValue(normalized);
            writeModuleFieldValue(module, fieldMap, key, normalized, { default: defaultValue, type: 'number' });
            refreshPreviews();
        };

        input.addEventListener('change', save);
        input.addEventListener('blur', save);
        presetSelect.addEventListener('change', () => {
            if (presetSelect.value === customPresetValue) return;
            const normalized = normalizeValue(presetSelect.value, defaultValue);
            input.value = String(normalized);
            writeModuleFieldValue(module, fieldMap, key, normalized, { default: defaultValue, type: 'number' });
            refreshPreviews();
        });

        controls.appendChild(input);
        if (normalizedPresets.length) {
            controls.appendChild(presetSelect);
            syncPresetFromValue(normalizeValue(input.value, defaultValue));
        }

        row.appendChild(controls);
        root.appendChild(row);
        return { row, input, presetSelect };
    }

    // ── Preview registry ──
    const applyStickyTop = (el) => {
        requestAnimationFrame(() => {
            const header = document.querySelector('.site-header, .structure-header, header');
            const headerBottom = header ? header.getBoundingClientRect().bottom : 0;
            el.style.top = Math.max(0, headerBottom) + 'px';
        });
    };

    function mountHtmlStickyPreview(fields, refreshPreviewFns, buildHtml, labelText = 'Preview:') {
        const stickyWrapper = createElement('div', 'ntcfg-sticky-preview');
        applyStickyTop(stickyWrapper);
        stickyWrapper.appendChild(createElement('div', 'ntcfg-theme-preview-label', labelText));
        const previewHost = createElement('div', 'ntcfg-preview-host');
        stickyWrapper.appendChild(previewHost);
        fields.appendChild(stickyWrapper);
        refreshPreviewFns.push(() => {
            previewHost.innerHTML = buildHtml();
        });
    }

    function buildPreviewPillHtml(label, active = true, tone = '') {
        const toneClass = tone ? ` ntcfg-preview-pill--${tone}` : '';
        return `<span class="ntcfg-preview-pill${active ? ' is-active' : ' is-off'}${toneClass}">${escapeHtml(label)}</span>`;
    }

    function buildPreviewTileHtml(label, value, active = true, tone = '') {
        const toneClass = tone ? ` ntcfg-preview-tile--${tone}` : '';
        return `
            <div class="ntcfg-preview-tile${active ? '' : ' is-off'}${toneClass}">
                <span class="ntcfg-preview-tile-label">${escapeHtml(label)}</span>
                <strong class="ntcfg-preview-tile-value">${escapeHtml(value)}</strong>
            </div>
        `;
    }

    function truncatePreviewText(value, maxLength = 56) {
        const text = String(value || '').trim();
        if (!text) return '';
        if (text.length <= maxLength) return text;
        return `${text.slice(0, Math.max(0, maxLength - 3))}...`;
    }

    function readPreviewToggle(module, fieldMap, key, defaultValue = false) {
        return !!readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'toggle' });
    }

    function readPreviewText(module, fieldMap, key, defaultValue = '') {
        return String(readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'text' }) ?? defaultValue);
    }

    function readPreviewSelect(module, fieldMap, key, defaultValue = '', options = []) {
        return String(readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'select', options }) ?? defaultValue);
    }

    function readPreviewNumber(module, fieldMap, key, defaultValue = 0) {
        const parsed = Number(readModuleFieldValue(module, fieldMap, key, { default: defaultValue, type: 'number' }));
        return Number.isFinite(parsed) ? parsed : Number(defaultValue);
    }

    function getPreviewUrlHost(url) {
        try {
            return new URL(String(url || '').trim()).host;
        } catch {
            return truncatePreviewText(url, 44) || 'Not configured';
        }
    }

    function getMaskedPreviewObserverId() {
        try {
            const observerId = String(localStorage.getItem('startrack-observer-id') || '').trim();
            if (!observerId) return 'Generated on first send';
            if (observerId.length <= 12) return observerId;
            return `${observerId.slice(0, 8)}...${observerId.slice(-4)}`;
        } catch {
            return 'Generated on first send';
        }
    }

    function getMusicPreviewPlatform(url, fallbackPlatform = 'spotify') {
        const raw = String(url || '').toLowerCase();
        if (raw.includes('youtube') || raw.includes('youtu.be')) return 'youtube';
        if (raw.includes('apple.com') || raw.includes('music.apple')) return 'apple-music';
        if (raw.includes('spotify')) return 'spotify';
        return fallbackPlatform || 'spotify';
    }

    function getMusicPreviewPlatformLabel(platform) {
        if (platform === 'youtube') return 'YouTube';
        if (platform === 'apple-music') return 'Apple Music';
        return 'Spotify';
    }

    function getMusicPreviewSourceType(url) {
        const raw = String(url || '').toLowerCase();
        if (!raw) return 'Source';
        if (raw.includes('playlist') || raw.includes('list=')) return 'Playlist';
        if (raw.includes('/album')) return 'Album';
        if (raw.includes('/track') || raw.includes('/song') || raw.includes('/video') || raw.includes('watch?v=')) return 'Track';
        return 'Source';
    }

    function getSectionPreviewRenderer(module, section) {
        const previewType = String(section?.preview?.type || '').trim();
        if (!previewType) return null;
        const moduleScopedKey = previewType.includes(':') ? previewType : `${module.id}:${previewType}`;
        return PREVIEW_RENDERERS[moduleScopedKey] || PREVIEW_RENDERERS[previewType] || null;
    }

    const PREVIEW_RENDERERS = {
        'race-options:theme': function (fields, refreshPreviewFns, module, fieldMap) {
            const stickyWrapper = createElement('div', 'ntcfg-sticky-preview');
            applyStickyTop(stickyWrapper);
            stickyWrapper.appendChild(createElement('div', 'ntcfg-theme-preview-label', 'Preview:'));
            const previewGrid = createElement('div', 'ntcfg-theme-preview-grid');
            const normalPreview = createElement('div', 'ntcfg-theme-preview');
            const incorrectPreview = createElement('div', 'ntcfg-theme-preview');
            previewGrid.append(normalPreview, incorrectPreview);
            stickyWrapper.appendChild(previewGrid);
            fields.appendChild(stickyWrapper);
            refreshPreviewFns.push(() => renderThemePreview(normalPreview, 7, false, module, fieldMap));
            refreshPreviewFns.push(() => renderThemePreview(incorrectPreview, 7, true, module, fieldMap));
        },
        'race-options:perfect-nitro': function (fields, refreshPreviewFns, module, fieldMap) {
            const stickyWrapper = createElement('div', 'ntcfg-sticky-preview');
            applyStickyTop(stickyWrapper);
            stickyWrapper.appendChild(createElement('div', 'ntcfg-theme-preview-label', 'Preview:'));
            const preview = createElement('div', 'ntcfg-pn-preview');
            stickyWrapper.appendChild(preview);
            fields.appendChild(stickyWrapper);
            refreshPreviewFns.push(() => renderPerfectNitroPreview(preview, module, fieldMap));
        },
        'racer-badges:general': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const showRacerBadges = readPreviewToggle(module, fieldMap, 'SHOW_RACER_BADGES', true);
                const showTeamBadges = readPreviewToggle(module, fieldMap, 'SHOW_TEAM_BADGES', true);
                const showRacerBanners = readPreviewToggle(module, fieldMap, 'SHOW_RACER_BANNERS', true);
                const showTeamBanners = readPreviewToggle(module, fieldMap, 'SHOW_TEAM_BANNERS', true);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Inline placements</div>
                            <div class="ntcfg-badge-inline-preview">
                                <div class="ntcfg-badge-name-chip">
                                    <span class="ntcfg-badge-name-text">Captain.Loveridge</span>
                                    ${showRacerBadges ? '<span class="ntcfg-badge-chip ntcfg-badge-chip--racer">Top 10</span>' : buildPreviewPillHtml('Racer badges off', false)}
                                </div>
                                <div class="ntcfg-badge-name-chip">
                                    <span class="ntcfg-badge-name-text">STAR</span>
                                    ${showTeamBadges ? '<span class="ntcfg-badge-chip ntcfg-badge-chip--team">Top 50 Team</span>' : buildPreviewPillHtml('Team badges off', false)}
                                </div>
                            </div>
                        </div>
                        <div class="ntcfg-preview-grid ntcfg-preview-grid--two">
                            <div class="ntcfg-preview-card${showRacerBanners ? '' : ' is-off'}">
                                <div class="ntcfg-preview-card-title">Racer banner</div>
                                <div class="ntcfg-badge-banner ntcfg-badge-banner--racer">Top Racer Banner</div>
                            </div>
                            <div class="ntcfg-preview-card${showTeamBanners ? '' : ' is-off'}">
                                <div class="ntcfg-preview-card-title">Team banner</div>
                                <div class="ntcfg-badge-banner ntcfg-badge-banner--team">Top Team Banner</div>
                            </div>
                        </div>
                        <div class="ntcfg-preview-pill-row">
                            ${buildPreviewPillHtml('Racer badges', showRacerBadges, 'blue')}
                            ${buildPreviewPillHtml('Team badges', showTeamBadges, 'gold')}
                            ${buildPreviewPillHtml('Racer banners', showRacerBanners, 'blue')}
                            ${buildPreviewPillHtml('Team banners', showTeamBanners, 'gold')}
                        </div>
                    </div>
                `;
            });
        },
        'racer-badges:pages': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const pageStates = [
                    ['Race', readPreviewToggle(module, fieldMap, 'ENABLE_RACE_BADGES', true)],
                    ['Profile', readPreviewToggle(module, fieldMap, 'ENABLE_PROFILE_BADGES', true)],
                    ['Team', readPreviewToggle(module, fieldMap, 'ENABLE_TEAM_PAGE_BADGES', true)],
                    ['Garage', readPreviewToggle(module, fieldMap, 'ENABLE_GARAGE_BADGES', true)],
                    ['Friends', readPreviewToggle(module, fieldMap, 'ENABLE_FRIENDS_BADGES', true)],
                    ['Leagues', readPreviewToggle(module, fieldMap, 'ENABLE_LEAGUES_BADGES', true)],
                    ['Header', readPreviewToggle(module, fieldMap, 'ENABLE_HEADER_BADGE', true)]
                ];
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Page coverage</div>
                            <div class="ntcfg-preview-matrix">
                                ${pageStates.map(([label, enabled]) => buildPreviewTileHtml(label, enabled ? 'Enabled' : 'Hidden', enabled, enabled ? 'blue' : '')).join('')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'racer-badges:advanced': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const debugLogging = readPreviewToggle(module, fieldMap, 'DEBUG_LOGGING', false);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Diagnostics</div>
                            <div class="ntcfg-preview-card-copy">Use debug logging when checking why a badge did or did not appear on a page.</div>
                            <div class="ntcfg-preview-pill-row">
                                ${buildPreviewPillHtml('Debug logging', debugLogging, 'danger')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'bot-hunter:general': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const enabled = readPreviewToggle(module, fieldMap, 'ENABLED', true);
                const showFloatingToggle = readPreviewToggle(module, fieldMap, 'SHOW_FLOATING_TOGGLE', true);
                const loggingEnabled = readPreviewToggle(module, fieldMap, 'ENABLE_LOGGING', true);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-grid ntcfg-preview-grid--two">
                            <div class="ntcfg-preview-card">
                                <div class="ntcfg-preview-card-title">Floating control</div>
                                ${showFloatingToggle
                                    ? `<button type="button" class="ntcfg-bot-toggle-preview${enabled ? ' is-on' : ' is-off'}">Bot Hunter: ${enabled ? 'ON' : 'OFF'}</button>`
                                    : '<div class="ntcfg-preview-empty">Floating toggle hidden.</div>'}
                            </div>
                            <div class="ntcfg-preview-card">
                                <div class="ntcfg-preview-card-title">Runtime state</div>
                                <div class="ntcfg-preview-card-copy">${enabled ? 'Race tracking is active and listening for race events.' : 'Race tracking is paused until you turn it back on.'}</div>
                                <div class="ntcfg-preview-pill-row">
                                    ${buildPreviewPillHtml('Tracking enabled', enabled, 'blue')}
                                    ${buildPreviewPillHtml('Floating toggle', showFloatingToggle, 'blue')}
                                    ${buildPreviewPillHtml('Console logging', loggingEnabled, 'gold')}
                                </div>
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'bot-hunter:observer': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const endpoint = readPreviewText(module, fieldMap, 'API_ENDPOINT', 'https://ntstartrack.org/api/bot-observations');
                const keepalive = readPreviewToggle(module, fieldMap, 'USE_KEEPALIVE', true);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-grid ntcfg-preview-grid--two">
                            <div class="ntcfg-preview-card">
                                <div class="ntcfg-preview-card-title">Destination</div>
                                <div class="ntcfg-preview-card-copy">${escapeHtml(getPreviewUrlHost(endpoint))}</div>
                                <div class="ntcfg-preview-card-subcopy">${escapeHtml(truncatePreviewText(endpoint, 64))}</div>
                            </div>
                            <div class="ntcfg-preview-card">
                                <div class="ntcfg-preview-card-title">Observer identity</div>
                                <div class="ntcfg-preview-card-copy">${escapeHtml(getMaskedPreviewObserverId())}</div>
                                <div class="ntcfg-preview-pill-row">
                                    ${buildPreviewPillHtml('Keepalive delivery', keepalive, 'blue')}
                                </div>
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'bot-hunter:analysis': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const detect1000Ms = readPreviewToggle(module, fieldMap, 'DETECT_1000MS_PATTERN', true);
                const detectSmoothing = readPreviewToggle(module, fieldMap, 'DETECT_EXCESSIVE_SMOOTHING', true);
                const tolerance = readPreviewNumber(module, fieldMap, 'BOUNDARY_TOLERANCE_MS', 100);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Pattern detection</div>
                            <div class="ntcfg-preview-pill-row">
                                ${buildPreviewPillHtml('1000ms boundary detection', detect1000Ms, 'danger')}
                                ${buildPreviewPillHtml('Smoothing detection', detectSmoothing, 'gold')}
                            </div>
                            <div class="ntcfg-preview-matrix">
                                ${buildPreviewTileHtml('Tolerance', `${tolerance} ms`, true, 'blue')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'bot-flag:display': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const showNotFlagged = readPreviewToggle(module, fieldMap, 'SHOW_NOT_FLAGGED', true);
                const showUntracked = readPreviewToggle(module, fieldMap, 'SHOW_UNTRACKED', true);
                const useRobotIcon = readPreviewToggle(module, fieldMap, 'USE_ROBOT_ICON', false);
                const flaggedLabel = useRobotIcon ? 'BOT' : 'FLAG';
                const legend = [
                    { label: 'Flagged racer', indicator: flaggedLabel, tone: 'danger', enabled: true },
                    { label: 'Not flagged', indicator: 'OK', tone: 'success', enabled: showNotFlagged },
                    { label: 'Untracked', indicator: '?', tone: 'warning', enabled: showUntracked }
                ].filter((entry) => entry.enabled);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Legend preview</div>
                            <div class="ntcfg-flag-preview-row">
                                ${legend.map((entry) => `
                                    <div class="ntcfg-flag-sample">
                                        <span class="ntcfg-flag-indicator ntcfg-flag-indicator--${escapeHtml(entry.tone)}">${escapeHtml(entry.indicator)}</span>
                                        <span>${escapeHtml(entry.label)}</span>
                                    </div>
                                `).join('')}
                            </div>
                            <div class="ntcfg-preview-pill-row">
                                ${buildPreviewPillHtml('Robot icon', useRobotIcon, 'danger')}
                                ${buildPreviewPillHtml('Show not flagged', showNotFlagged, 'success')}
                                ${buildPreviewPillHtml('Show untracked', showUntracked, 'warning')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'bot-flag:sources': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const useStartrack = readPreviewToggle(module, fieldMap, 'USE_STARTRACK', true);
                const useLegacy = readPreviewToggle(module, fieldMap, 'USE_NTL_LEGACY', true);
                const concurrency = readPreviewNumber(module, fieldMap, 'NETWORK_CONCURRENCY_LIMIT', 6);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Lookup sources</div>
                            <div class="ntcfg-preview-pill-row">
                                ${buildPreviewPillHtml('NT StarTrack', useStartrack, 'blue')}
                                ${buildPreviewPillHtml('NTL Legacy', useLegacy, 'gold')}
                            </div>
                            <div class="ntcfg-preview-matrix">
                                ${buildPreviewTileHtml('Concurrency', `${concurrency} requests`, true, 'blue')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'bot-flag:cache': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const cacheDays = readPreviewNumber(module, fieldMap, 'STARTRACK_CACHE_DAYS', 7);
                const crossTab = readPreviewToggle(module, fieldMap, 'CROSS_TAB_SYNC', true);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Cache behavior</div>
                            <div class="ntcfg-preview-matrix">
                                ${buildPreviewTileHtml('StarTrack cache', `${cacheDays} day${cacheDays === 1 ? '' : 's'}`, true, 'blue')}
                            </div>
                            <div class="ntcfg-preview-pill-row">
                                ${buildPreviewPillHtml('Cross-tab sync', crossTab, 'blue')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'bot-flag:advanced': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const debugLogging = readPreviewToggle(module, fieldMap, 'DEBUG_LOGGING', false);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Diagnostics</div>
                            <div class="ntcfg-preview-card-copy">Verbose logging helps trace source lookups, cache hits, and icon placement.</div>
                            <div class="ntcfg-preview-pill-row">
                                ${buildPreviewPillHtml('Debug logging', debugLogging, 'danger')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'leaderboards:views': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const view = readPreviewSelect(module, fieldMap, 'DEFAULT_VIEW', 'individual');
                const timeframe = readPreviewSelect(module, fieldMap, 'DEFAULT_TIMEFRAME', 'season');
                const showManualRefresh = readPreviewToggle(module, fieldMap, 'SHOW_MANUAL_REFRESH', true);
                const highlightPositionChange = readPreviewToggle(module, fieldMap, 'HIGHLIGHT_POSITION_CHANGE', true);
                const timeframeButtons = [
                    ['season', 'Season'],
                    ['24hr', 'Last 24 Hours'],
                    ['7day', 'Last 7 Days']
                ];
                return `
                    <div class="ntcfg-preview-surface ntcfg-preview-surface--leaderboards">
                        <div class="ntcfg-leader-preview-tabs">
                            <div class="ntcfg-leader-preview-tab${view === 'individual' ? ' is-active' : ''}">Top Racers</div>
                            <div class="ntcfg-leader-preview-tab${view === 'team' ? ' is-active' : ''}">Top Teams</div>
                        </div>
                        <div class="ntcfg-leader-preview-panel">
                            <div class="ntcfg-leader-preview-row">
                                ${timeframeButtons.map(([value, label]) => `<span class="ntcfg-leader-preview-btn${timeframe === value ? ' is-active' : ''}">${escapeHtml(label)}</span>`).join('')}
                                ${showManualRefresh ? '<span class="ntcfg-leader-preview-refresh">Refresh</span>' : ''}
                            </div>
                            <div class="ntcfg-preview-card">
                                <div class="ntcfg-preview-card-title">Sample entry</div>
                                <div class="ntcfg-leader-preview-entry">
                                    ${highlightPositionChange ? '<span class="ntcfg-leader-preview-change">+2</span>' : ''}
                                    <span class="ntcfg-preview-name-text">Captain.Loveridge</span>
                                    <span class="ntcfg-preview-entry-meta">178 WPM</span>
                                </div>
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'leaderboards:sync': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const cacheMinutes = readPreviewNumber(module, fieldMap, 'CACHE_DURATION_MINUTES', 60);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Refresh window</div>
                            <div class="ntcfg-preview-card-copy">Leaderboard responses stay warm for the configured cache window before a fresh fetch is needed.</div>
                            <div class="ntcfg-preview-matrix">
                                ${buildPreviewTileHtml('Cache duration', `${cacheMinutes} min`, true, 'blue')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'leaderboards:presentation': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const antiFlicker = readPreviewToggle(module, fieldMap, 'ANTI_FLICKER_MODE', true);
                const showRouteTab = readPreviewToggle(module, fieldMap, 'SHOW_ROUTE_TAB', true);
                const showDropdownLink = readPreviewToggle(module, fieldMap, 'SHOW_DROPDOWN_LINK', false);
                const hideClassTab = readPreviewToggle(module, fieldMap, 'HIDE_CLASS_TAB', false);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Navigation + page behavior</div>
                            <div class="ntcfg-preview-pill-row">
                                ${buildPreviewPillHtml('Anti-flicker mode', antiFlicker, 'blue')}
                                ${buildPreviewPillHtml('Nav tab', showRouteTab, 'blue')}
                                ${buildPreviewPillHtml('Dropdown link', showDropdownLink, 'gold')}
                                ${buildPreviewPillHtml('Hide class tab', hideClassTab, 'danger')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'leaderboards:advanced': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const debugLogging = readPreviewToggle(module, fieldMap, 'DEBUG_LOGGING', false);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Diagnostics</div>
                            <div class="ntcfg-preview-card-copy">Enable debug logging when checking cache refreshes, season metadata, and SPA route takeover behavior.</div>
                            <div class="ntcfg-preview-pill-row">
                                ${buildPreviewPillHtml('Debug logging', debugLogging, 'danger')}
                            </div>
                        </div>
                    </div>
                `;
            });
        },
        'music-player:source': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const sourceUrl = readPreviewText(module, fieldMap, 'SOURCE_URL', '');
                const defaultPlatform = readPreviewSelect(module, fieldMap, 'DEFAULT_PLATFORM', 'spotify');
                const sessionResume = readPreviewToggle(module, fieldMap, 'SESSION_RESUME', true);
                const autoActivate = readPreviewToggle(module, fieldMap, 'AUTO_ACTIVATE', true);
                const platform = getMusicPreviewPlatform(sourceUrl, defaultPlatform);
                const platformLabel = getMusicPreviewPlatformLabel(platform);
                const sourceType = getMusicPreviewSourceType(sourceUrl);
                const sourceConfigured = !!String(sourceUrl || '').trim();
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-music-preview-card">
                            <div class="ntcfg-music-preview-cover">${escapeHtml(platformLabel.charAt(0))}</div>
                            <div class="ntcfg-music-preview-meta">
                                <div class="ntcfg-music-preview-title">${escapeHtml(sourceConfigured ? `${platformLabel} ${sourceType}` : 'No source configured')}</div>
                                <div class="ntcfg-music-preview-subtitle">${escapeHtml(sourceConfigured ? truncatePreviewText(sourceUrl, 68) : 'Save a Spotify, YouTube, or Apple Music URL to prime the player.')}</div>
                            </div>
                        </div>
                        <div class="ntcfg-preview-pill-row">
                            ${buildPreviewPillHtml(platformLabel, true, 'blue')}
                            ${buildPreviewPillHtml('Session resume', sessionResume, 'gold')}
                            ${buildPreviewPillHtml('Auto activate', autoActivate, 'blue')}
                        </div>
                    </div>
                `;
            });
        },
        'music-player:playback': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const queueMode = readPreviewSelect(module, fieldMap, 'QUEUE_MODE', 'shuffle');
                const showAlbumArt = readPreviewToggle(module, fieldMap, 'SHOW_ALBUM_ART', true);
                const muteNative = readPreviewToggle(module, fieldMap, 'MUTE_NATIVE_STATION', true);
                const tickMs = readPreviewNumber(module, fieldMap, 'PROGRESS_TICK_MS', 1000);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-music-preview-card">
                            ${showAlbumArt ? '<div class="ntcfg-music-preview-cover ntcfg-music-preview-cover--art">NT</div>' : ''}
                            <div class="ntcfg-music-preview-meta">
                                <div class="ntcfg-music-preview-title">Now Playing</div>
                                <div class="ntcfg-music-preview-subtitle">${escapeHtml(queueMode === 'shuffle' ? 'Shuffle queue ready for race start.' : 'Sequential queue ready for race start.')}</div>
                            </div>
                        </div>
                        <div class="ntcfg-music-control-row">
                            <span class="ntcfg-music-control-pill">Prev</span>
                            <span class="ntcfg-music-control-pill">Play</span>
                            <span class="ntcfg-music-control-pill">Next</span>
                            <span class="ntcfg-music-control-pill${queueMode === 'shuffle' ? ' is-active' : ''}">${escapeHtml(queueMode === 'shuffle' ? 'Shuffle' : 'Sequential')}</span>
                        </div>
                        <div class="ntcfg-preview-pill-row">
                            ${buildPreviewPillHtml('Album art', showAlbumArt, 'blue')}
                            ${buildPreviewPillHtml('Mute native station', muteNative, 'danger')}
                            ${buildPreviewPillHtml(`Progress tick ${tickMs} ms`, true, 'gold')}
                        </div>
                    </div>
                `;
            });
        },
        'music-player:advanced': function (fields, refreshPreviewFns, module, fieldMap) {
            mountHtmlStickyPreview(fields, refreshPreviewFns, () => {
                const debugLogging = readPreviewToggle(module, fieldMap, 'DEBUG_LOGGING', false);
                return `
                    <div class="ntcfg-preview-surface">
                        <div class="ntcfg-preview-card">
                            <div class="ntcfg-preview-card-title">Diagnostics</div>
                            <div class="ntcfg-preview-card-copy">Verbose music logs help when checking queue construction, source parsing, and player handoff behavior.</div>
                            <div class="ntcfg-preview-pill-row">
                                ${buildPreviewPillHtml('Debug logging', debugLogging, 'danger')}
                            </div>
                        </div>
                    </div>
                `;
            });
        }
    };

    // ── Generic section mount ──

    // Cache rendered section DOM nodes so tab switches just re-attach instead of rebuilding.
    // Key: "moduleId::sectionId", Value: { root: DOMNode, refreshPreviews: Function }
    const _sectionDomCache = new Map();
    const SECTION_DOM_CACHE_MAX = 12;

    function syncRenderedToggleFieldValue(fieldKey, checked) {
        const selector = `[data-field-key="${CSS.escape(String(fieldKey))}"][data-field-type="toggle"]`;
        const roots = new Set([document]);
        _sectionDomCache.forEach((entry) => {
            if (entry?.root) roots.add(entry.root);
        });
        roots.forEach((root) => {
            root.querySelectorAll(selector).forEach((input) => {
                input.checked = !!checked;
                input.setAttribute('aria-checked', checked ? 'true' : 'false');
            });
        });
    }

    function clearSectionDomCache() {
        _sectionDomCache.clear();
    }

    function mountGenericSection(module, section) {
        const host = document.querySelector('[data-ntmods-generic-stage]');
        if (!host || !module || !section) return;

        const cacheKey = `${module.id}::${section.id}`;
        const cached = _sectionDomCache.get(cacheKey);
        if (cached) {
            // Re-attach cached DOM — instant, no rebuilding
            host.innerHTML = '';
            host.appendChild(cached.root);
            // Refresh previews to pick up any value changes
            if (cached.refreshPreviews) cached.refreshPreviews();
            return;
        }

        const fieldMap = getModuleFieldMap(module);
        const rerender = () => {
            _sectionDomCache.delete(cacheKey);
            mountGenericSection(module, section);
        };
        const refreshPreviewFns = [];
        const refreshPreviews = () => { refreshPreviewFns.forEach((fn) => fn()); };
        const ctx = { module, fieldMap, refreshPreviewFns, refreshPreviews, rerender };

        const sectionRoot = createElement('div', 'ntmods-ro-panel');
        const title = createElement('h2', 'ntcfg-panel-title', section.title);
        const subtitle = createElement('p', 'ntcfg-panel-subtitle', section.subtitle || '');
        const fields = createElement('div', 'ntcfg-fields');
        sectionRoot.append(title, subtitle, fields);
        host.innerHTML = '';
        host.appendChild(sectionRoot);

        // Render preview widget if section declares one
        if (section.preview && typeof section.preview === 'object' && section.preview.type) {
            const renderer = getSectionPreviewRenderer(module, section);
            if (typeof renderer === 'function') {
                renderer(fields, refreshPreviewFns, module, fieldMap);
            }
        }

        const isCompact = section.layout === 'compact';

        // Container for compact layout items
        let compactRoot = null;
        if (isCompact) {
            compactRoot = createElement('div', 'ntcfg-theme-settings');
            fields.appendChild(compactRoot);
        }

        // Track rendered elements for visibleWhen / disabledWhen
        const visibilityMap = new Map();
        const disabledMap = new Map();
        const renderedElements = new Map();

        const items = Array.isArray(section.items) ? section.items : [];
        let currentSubSection = null;

        items.forEach((item) => {
            // Render section sub-headers when the section property changes
            if (item.section && item.section !== currentSubSection) {
                currentSubSection = item.section;
                const subHeader = createElement('h3', 'ntcfg-subsection-title', item.section);
                subHeader.style.cssText = 'font-size:14px;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:#8e9aaf;margin:20px 0 8px;padding-bottom:6px;border-bottom:1px solid rgba(255,255,255,0.06);';
                if (currentSubSection !== items[0]?.section) {
                    subHeader.style.marginTop = '28px';
                }
                (isCompact ? compactRoot : fields).appendChild(subHeader);
            }
            if (item.type === 'note') {
                const noteEl = createElement('div', `ntmods-note ntmods-note--${item.tone || 'info'}`, item.message || '');
                (isCompact ? compactRoot : fields).appendChild(noteEl);
                return;
            }

            const targetRoot = isCompact ? compactRoot : fields;

            // Handle compound items (toggle with inline child controls)
            if (item.compound && Array.isArray(item.compound) && item.compound.length) {
                renderCompoundRow(targetRoot, item, ctx);
                return;
            }

            let rendered = null;

            if (isCompact) {
                // Compact layout uses ntcfg-theme-setting rows
                if (item.type === 'toggle') {
                    rendered = genericAppendThemeSettingToggle(targetRoot, item.label, item.key, item.default, ctx);
                } else if (item.type === 'select') {
                    rendered = genericAppendThemeSettingSelect(targetRoot, item.label, item.key, item.default, item.options || [], ctx);
                } else if (item.type === 'number') {
                    const numConfig = {
                        min: item.min,
                        max: item.max,
                        step: item.step,
                        presets: item.presets || [],
                        normalizeValue: item.normalize || undefined
                    };
                    rendered = genericAppendThemeSettingNumber(targetRoot, item.label, item.key, item.default, numConfig, ctx);
                } else if (item.type === 'range') {
                    rendered = genericAppendRangeField(targetRoot, {
                        labelText: item.label,
                        key: item.key,
                        defaultValue: item.default,
                        helpText: item.help || '',
                        min: item.min,
                        max: item.max,
                        step: item.step || '1',
                        displayScale: item.displayScale,
                        displayUnit: item.displayUnit,
                        displayPrecision: item.displayPrecision
                    }, ctx);
                } else if (item.type === 'color') {
                    // Render as a compact theme color row (toggle always on)
                    const row = createElement('div', 'ntcfg-theme-setting');
                    row.appendChild(createElement('div', 'ntcfg-theme-setting-title', item.label));
                    const controls = createElement('div', 'ntcfg-theme-setting-controls');
                    const colorControl = createCompactColorControl(
                        readModuleFieldValue(module, fieldMap, item.key, { default: item.default, type: 'color' }),
                        (value) => {
                            writeModuleFieldValue(module, fieldMap, item.key, value, { default: item.default, type: 'color' });
                            refreshPreviews();
                        }
                    );
                    controls.appendChild(colorControl.root);
                    row.appendChild(controls);
                    targetRoot.appendChild(row);
                    rendered = { row };
                }
            } else {
                // Standard layout
                if (item.type === 'toggle') {
                    rendered = genericAppendCheckbox(targetRoot, {
                        labelText: item.label,
                        key: item.key,
                        defaultValue: item.default,
                        helpText: item.help || '',
                        confirmOnEnable: item.confirmOnEnable || null,
                        exclusiveWith: item.exclusiveWith || null
                    }, ctx);
                } else if (item.type === 'number') {
                    const warnOpts = item.warn || {};
                    rendered = genericAppendNumberField(targetRoot, {
                        labelText: item.label,
                        key: item.key,
                        defaultValue: item.default,
                        helpText: item.help || '',
                        min: item.min,
                        max: item.max,
                        step: item.step || '1',
                        minRecommended: warnOpts.below != null ? warnOpts.below : null,
                        warningText: warnOpts.message || ''
                    }, ctx);
                } else if (item.type === 'range') {
                    rendered = genericAppendRangeField(targetRoot, {
                        labelText: item.label,
                        key: item.key,
                        defaultValue: item.default,
                        helpText: item.help || '',
                        min: item.min,
                        max: item.max,
                        step: item.step || '1',
                        displayScale: item.displayScale,
                        displayUnit: item.displayUnit,
                        displayPrecision: item.displayPrecision
                    }, ctx);
                } else if (item.type === 'select') {
                    rendered = genericAppendSelectField(targetRoot, {
                        labelText: item.label,
                        key: item.key,
                        defaultValue: item.default,
                        options: item.options || [],
                        helpText: item.help || ''
                    }, ctx);
                } else if (item.type === 'multi-select') {
                    rendered = genericAppendMultiSelectField(targetRoot, {
                        labelText: item.label,
                        key: item.key,
                        defaultValue: item.default || [],
                        options: item.options || [],
                        helpText: item.help || ''
                    }, ctx);
                } else if (item.type === 'color') {
                    rendered = genericAppendColorField(targetRoot, {
                        labelText: item.label,
                        key: item.key,
                        defaultValue: item.default,
                        helpText: item.help || ''
                    }, ctx);
                } else if (item.type === 'text' && item.key === 'SAVED_SOURCES' && module.id === 'music-player') {
                    rendered = renderMusicSavedSourcesField(targetRoot, module, item);
                } else if (item.type === 'mute-list' && module.id === 'race-options') {
                    rendered = renderSafeSpaceMutedUsersField(targetRoot, item);
                } else if (item.type === 'text') {
                    rendered = genericAppendTextField(targetRoot, {
                        labelText: item.label,
                        key: item.key,
                        defaultValue: item.default || '',
                        helpText: item.help || '',
                        placeholder: item.placeholder || ''
                    }, ctx);
                } else if (item.type === 'keymap') {
                    rendered = genericAppendKeymapField(targetRoot, {
                        labelText: item.label,
                        key: item.key,
                        defaultValue: item.default || [],
                        helpText: item.help || ''
                    }, ctx);
                } else if (item.type === 'action') {
                    const btn = createElement('button', `ntcfg-action ntcfg-inline-action ntcfg-action--${item.style || 'secondary'}`, item.label);
                    btn.type = 'button';
                    btn.addEventListener('click', () => {
                        document.dispatchEvent(new CustomEvent('ntcfg:action', {
                            detail: { script: module.id, key: item.key }
                        }));
                    });
                    targetRoot.appendChild(btn);
                    rendered = { row: btn };
                }
            }

            // Store for visibility tracking
            if (item.key && rendered) {
                const el = rendered.row || rendered.field || null;
                if (el) {
                    renderedElements.set(item.key, el);
                }
            }

            // Handle visibleWhen
            if (item.visibleWhen && item.key && rendered) {
                const el = rendered.row || rendered.field || null;
                if (el) {
                    visibilityMap.set(item.key, { element: el, condition: item.visibleWhen });
                }
            }

            // Handle disabledWhen
            if (item.disabledWhen && item.key && rendered) {
                const el = rendered.row || rendered.field || null;
                if (el) {
                    disabledMap.set(item.key, { element: el, condition: item.disabledWhen, toggleKey: item.key });
                }
            }
        });

        // Helper to evaluate a condition against a field value
        const evalCondition = (val, condition) =>
            condition.eq != null ? val === condition.eq : condition.neq != null ? val !== condition.neq : !!val;

        // Apply/refresh disabledWhen state
        const applyDisabledState = () => {
            disabledMap.forEach(({ element, condition, toggleKey }) => {
                const depItem = fieldMap.get(condition.key);
                if (!depItem) return;
                const val = readModuleFieldValue(module, fieldMap, condition.key, { default: depItem.default, type: depItem.type });
                const shouldDisable = evalCondition(val, condition);
                element.style.opacity = shouldDisable ? '0.4' : '';
                element.style.pointerEvents = shouldDisable ? 'none' : '';

                // When disabled (master is on), visually force the toggle on
                if (shouldDisable) {
                    const toggle = element.querySelector('.ntcfg-toggle-track, input[type="checkbox"]');
                    if (toggle && toggle.classList.contains('ntcfg-toggle-track') && !toggle.classList.contains('is-on')) {
                        toggle.classList.add('is-on');
                    }
                } else {
                    // Restore actual saved state
                    const toggle = element.querySelector('.ntcfg-toggle-track');
                    if (toggle) {
                        const savedVal = readModuleFieldValue(module, fieldMap, toggleKey, { default: fieldMap.get(toggleKey)?.default, type: 'toggle' });
                        toggle.classList.toggle('is-on', !!savedVal);
                    }
                }
            });
        };
        applyDisabledState();

        // Apply initial visibleWhen state and wire onChange cascades
        // Note: use style.display instead of element.hidden because author CSS
        // (.ntcfg-field, .ntcfg-checkbox) overrides the UA [hidden] rule.
        visibilityMap.forEach(({ element, condition }) => {
            const depItem = fieldMap.get(condition.key);
            if (!depItem) return;
            const val = readModuleFieldValue(module, fieldMap, condition.key, { default: depItem.default, type: depItem.type });
            element.style.display = evalCondition(val, condition) ? '' : 'none';
        });

        // Wire up onChange cascades: find items that other items depend on
        const depKeys = new Set();
        visibilityMap.forEach(({ condition }) => depKeys.add(condition.key));

        // For each dependency key, we need to re-check visibility on change
        // This is already handled via rerender on toggle changes, but we need to also
        // hook into the initial item rendering. Since all toggle changes trigger refreshPreviews,
        // we hook into refreshPreviews to also update visibility.
        const originalRefreshPreviews = refreshPreviews;
        const augmentedRefreshPreviews = () => {
            originalRefreshPreviews();
            visibilityMap.forEach(({ element, condition }) => {
                const depItem = fieldMap.get(condition.key);
                if (!depItem) return;
                const val = readModuleFieldValue(module, fieldMap, condition.key, { default: depItem.default, type: depItem.type });
                element.style.display = evalCondition(val, condition) ? '' : 'none';
            });
            applyDisabledState();
        };
        // Patch the ctx refresh so all child controls use the augmented version
        ctx.refreshPreviews = augmentedRefreshPreviews;
        refreshPreviewFns.push(() => {
            visibilityMap.forEach(({ element, condition }) => {
                const depItem = fieldMap.get(condition.key);
                if (!depItem) return;
                const val = readModuleFieldValue(module, fieldMap, condition.key, { default: depItem.default, type: depItem.type });
                element.style.display = evalCondition(val, condition) ? '' : 'none';
            });
            applyDisabledState();
        });

        // Render reset button
        if (section.resetButton) {
            const label = typeof section.resetButton === 'string'
                ? section.resetButton
                : `Reset ${section.title} to Defaults`;
            // Compute defaults from item metadata
            const defaults = {};
            items.forEach((item) => {
                if (item.key && item.default !== undefined) {
                    defaults[item.key] = item.default;
                }
                // Also collect compound child defaults
                if (item.compound && Array.isArray(item.compound)) {
                    item.compound.forEach((child) => {
                        if (child.key && child.default !== undefined) {
                            defaults[child.key] = child.default;
                        }
                    });
                }
            });
            genericAppendResetButton(fields, label, defaults, ctx);
        }

        refreshPreviews();

        // Store in DOM cache for instant re-attach on future tab switches
        if (_sectionDomCache.size >= SECTION_DOM_CACHE_MAX) {
            // Evict oldest entry
            const firstKey = _sectionDomCache.keys().next().value;
            _sectionDomCache.delete(firstKey);
        }
        _sectionDomCache.set(cacheKey, { root: sectionRoot, refreshPreviews });
    }

    function renderCompoundRow(root, item, ctx) {
        const { module, fieldMap, refreshPreviews } = ctx;
        const isCompactLayout = root.classList.contains('ntcfg-theme-settings');

        if (isCompactLayout) {
            // Compound in compact layout: ntcfg-theme-setting row with inline controls
            const row = createElement('div', 'ntcfg-theme-setting');
            row.appendChild(createElement('div', 'ntcfg-theme-setting-title', item.label));
            const controls = createElement('div', 'ntcfg-theme-setting-controls');

            item.compound.forEach((child) => {
                if (child.type === 'color') {
                    const colorControl = createCompactColorControl(
                        readModuleFieldValue(module, fieldMap, child.key, { default: child.default, type: 'color' }),
                        (value) => {
                            writeModuleFieldValue(module, fieldMap, child.key, value, { default: child.default, type: 'color' });
                            refreshPreviews();
                        }
                    );
                    colorControl.colorInput.setAttribute('data-field-key', child.key);
                    colorControl.colorInput.setAttribute('data-field-type', 'color');
                    colorControl.hexInput.setAttribute('data-field-key', child.key);
                    colorControl.hexInput.setAttribute('data-field-type', 'color');
                    controls.appendChild(colorControl.root);
                } else if (child.type === 'select') {
                    const safeOptions = Array.isArray(child.options) ? child.options.filter(Boolean) : [];
                    const select = createElement('select', 'ntcfg-input ntcfg-theme-input-compact ntcfg-theme-input-select');
                    const isDarkModeSyncSelect = child.key === 'THEME_DARK_MODE_SYNC_SYSTEM';
                    select.setAttribute('data-field-key', child.key);
                    select.setAttribute('data-field-type', 'select');
                    safeOptions.forEach((optionDef) => {
                        const option = document.createElement('option');
                        option.value = String(optionDef.value);
                        option.textContent = String(optionDef.label ?? optionDef.value);
                        select.appendChild(option);
                    });
                    const normalizeSelectedValue = (value) => {
                        if (isDarkModeSyncSelect) {
                            return normalizeRaceOptionsDarkModeSyncSelectionValue(value, 'always');
                        }
                        const raw = String(value ?? '').trim();
                        const matched = safeOptions.find((option) => String(option.value) === raw);
                        if (matched) return matched.value;
                        const fallback = safeOptions.find((option) => String(option.value) === String(child.default).trim());
                        return fallback ? fallback.value : (safeOptions[0]?.value ?? child.default);
                    };
                    select.value = String(normalizeSelectedValue(readModuleFieldValue(module, fieldMap, child.key, { default: child.default, type: 'select', options: child.options })));
                    select.addEventListener('change', () => {
                        const normalized = normalizeSelectedValue(select.value);
                        select.value = String(normalized);
                        const storedValue = isDarkModeSyncSelect
                            ? normalizeRaceOptionsDarkModeSyncStoredValue(normalized)
                            : normalized;
                        writeModuleFieldValue(module, fieldMap, child.key, storedValue, { default: child.default, type: 'select', options: child.options });
                        if (child.onChange) child.onChange(normalized, row, select);
                        refreshPreviews();
                    });
                    controls.appendChild(select);
                } else if (child.type === 'toggle') {
                    const switchControl = createSwitch(
                        readModuleFieldValue(module, fieldMap, child.key, { default: child.default, type: 'toggle' }),
                        (checked) => {
                            writeModuleFieldValue(module, fieldMap, child.key, checked, { default: child.default, type: 'toggle' });
                            if (child.onChange) child.onChange(checked, row);
                            refreshPreviews();
                        }
                    );
                    switchControl.input.setAttribute('data-field-key', child.key);
                    switchControl.input.setAttribute('data-field-type', 'toggle');
                    switchControl.input.setAttribute('aria-label', child.label || item.label);
                    controls.appendChild(switchControl.root);
                }
            });

            // The main item itself is the primary toggle
            if (item.type === 'toggle') {
                const mainSwitch = createSwitch(
                    readModuleFieldValue(module, fieldMap, item.key, { default: item.default, type: 'toggle' }),
                    (checked) => {
                        writeModuleFieldValue(module, fieldMap, item.key, checked, { default: item.default, type: 'toggle' });
                        refreshPreviews();
                    }
                );
                mainSwitch.input.setAttribute('data-field-key', item.key);
                mainSwitch.input.setAttribute('data-field-type', 'toggle');
                mainSwitch.input.setAttribute('aria-label', item.label);
                controls.appendChild(mainSwitch.root);
            }

            row.appendChild(controls);
            root.appendChild(row);
        } else {
            // Compound in standard layout: ntcfg-checkbox row with inline controls
            const row = createElement('div', 'ntcfg-checkbox');
            const copy = createElement('span', 'ntcfg-checkbox-copy');
            copy.append(
                createElement('span', 'ntcfg-checkbox-label', item.label),
                createElement('span', 'ntcfg-checkbox-help', item.help || '')
            );
            const controls = createElement('span', 'ntcfg-checkbox-controls');

            item.compound.forEach((child) => {
                if (child.type === 'color') {
                    const colorControl = createCompactColorControl(
                        readModuleFieldValue(module, fieldMap, child.key, { default: child.default, type: 'color' }),
                        (value) => {
                            writeModuleFieldValue(module, fieldMap, child.key, value, { default: child.default, type: 'color' });
                            refreshPreviews();
                        },
                        'ntcfg-checkbox-controls'
                    );
                    colorControl.colorInput.setAttribute('data-field-key', child.key);
                    colorControl.colorInput.setAttribute('data-field-type', 'color');
                    colorControl.hexInput.setAttribute('data-field-key', child.key);
                    colorControl.hexInput.setAttribute('data-field-type', 'color');
                    controls.appendChild(colorControl.root);
                } else if (child.type === 'select') {
                    const safeOptions = Array.isArray(child.options) ? child.options.filter(Boolean) : [];
                    const select = createElement('select', 'ntcfg-input ntcfg-theme-input-compact');
                    const isDarkModeSyncSelect = child.key === 'THEME_DARK_MODE_SYNC_SYSTEM';
                    select.setAttribute('data-field-key', child.key);
                    select.setAttribute('data-field-type', 'select');
                    safeOptions.forEach((optionDef) => {
                        const option = document.createElement('option');
                        option.value = String(optionDef.value);
                        option.textContent = String(optionDef.label ?? optionDef.value);
                        select.appendChild(option);
                    });
                    const normalizeSelectedValue = (value) => {
                        if (isDarkModeSyncSelect) {
                            return normalizeRaceOptionsDarkModeSyncSelectionValue(value, 'always');
                        }
                        const raw = String(value ?? '').trim();
                        const matched = safeOptions.find((option) => String(option.value) === raw);
                        if (matched) return matched.value;
                        const fallback = safeOptions.find((option) => String(option.value) === String(child.default).trim());
                        return fallback ? fallback.value : (safeOptions[0]?.value ?? child.default);
                    };
                    select.value = String(normalizeSelectedValue(readModuleFieldValue(module, fieldMap, child.key, { default: child.default, type: 'select', options: child.options })));
                    select.addEventListener('change', () => {
                        const normalized = normalizeSelectedValue(select.value);
                        select.value = String(normalized);
                        const storedValue = isDarkModeSyncSelect
                            ? normalizeRaceOptionsDarkModeSyncStoredValue(normalized)
                            : normalized;
                        writeModuleFieldValue(module, fieldMap, child.key, storedValue, { default: child.default, type: 'select', options: child.options });
                        refreshPreviews();
                    });
                    controls.appendChild(select);
                }
            });

            // Main toggle
            if (item.type === 'toggle') {
                const mainSwitch = createSwitch(
                    readModuleFieldValue(module, fieldMap, item.key, { default: item.default, type: 'toggle' }),
                    (checked) => {
                        writeModuleFieldValue(module, fieldMap, item.key, checked, { default: item.default, type: 'toggle' });
                        // Hide/show compound children based on toggle state
                        item.compound.forEach((child) => {
                            // Find the child control in controls and toggle visibility
                        });
                        refreshPreviews();
                    }
                );
                mainSwitch.input.setAttribute('data-field-key', item.key);
                mainSwitch.input.setAttribute('data-field-type', 'toggle');
                mainSwitch.input.setAttribute('aria-label', item.label);
                controls.appendChild(mainSwitch.root);
            }

            row.append(copy, controls);
            root.appendChild(row);
        }
    }

    function buildModuleCountPill(value, label) {
        return `
            <div class="ntmods-cap-stat">
                <span class="ntmods-cap-stat-value">${escapeHtml(value)}</span>
                <span class="ntmods-cap-stat-label">${escapeHtml(label)}</span>
            </div>
        `;
    }

    function getTopTabGrow(module) {
        const label = String(module?.label || '');
        return Math.max(1, Math.min(2.15, (label.length + 4) / 7.5));
    }

    function getModuleIconMarkup(module, variant = 'tab') {
        const iconIdClass = module?.id ? ` ntmods-mod-icon--${slugify(module.id)}` : '';
        const iconClass = (variant === 'chip'
            ? 'ntmods-mod-icon ntmods-mod-icon--chip'
            : 'ntmods-mod-icon ntmods-mod-icon--tab') + iconIdClass;
        const useBuiltInIcon = [
            'race-options',
            'music-player',
            'bot-flag',
            'bot-hunter',
            'leaderboards',
            'racer-badges',
            'enhanced-stats',
            'toolkit'
        ].includes(module?.id);

        // Allow script authors to set custom icons via manifest.icon (SVG path content)
        if (module?.icon && !useBuiltInIcon) {
            return `
                <svg class="${iconClass}" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
                    ${module.icon}
                </svg>
            `;
        }

        switch (module?.id) {
            case 'race-options':
                return `
                <svg class="${iconClass}" viewBox="0 0 512 512" aria-hidden="true" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
                    <line x1="368" y1="128" x2="448" y2="128" stroke-width="32"></line>
                    <line x1="64" y1="128" x2="304" y2="128" stroke-width="32"></line>
                    <line x1="368" y1="384" x2="448" y2="384" stroke-width="32"></line>
                    <line x1="64" y1="384" x2="304" y2="384" stroke-width="32"></line>
                    <line x1="208" y1="256" x2="448" y2="256" stroke-width="32"></line>
                    <line x1="64" y1="256" x2="144" y2="256" stroke-width="32"></line>
                    <circle cx="336" cy="128" r="32" stroke-width="32"></circle>
                    <circle cx="176" cy="256" r="32" stroke-width="32"></circle>
                    <circle cx="336" cy="384" r="32" stroke-width="32"></circle>
                </svg>
            `;
            case 'music-player':
                return `
                <svg class="${iconClass}" viewBox="0 0 24 24" aria-hidden="true" fill="currentColor" stroke="none">
                    <path fill-rule="evenodd" clip-rule="evenodd" d="M19.4506 1.22841C20.7282 0.863369 22 1.8227 22 3.15145V16C22 18.2091 20.2091 20 18 20C15.7909 20 14 18.2091 14 16C14 13.7909 15.7909 12 18 12C18.7286 12 19.4117 12.1948 20 12.5351V7.07142L10 9.92857V19C10 21.2091 8.20914 23 6 23C3.79086 23 2 21.2091 2 19C2 16.7909 3.79086 15 6 15C6.72857 15 7.41165 15.1948 8 15.5351V6.0086C8 5.11564 8.59196 4.33086 9.45056 4.08555L19.4506 1.22841ZM18.7669 3.36991C19.3815 3.18555 20 3.64573 20 4.28734C20 4.71033 19.7225 5.08323 19.3174 5.20477L11.2331 7.63008C10.6185 7.81444 10 7.35426 10 6.71265C10 6.28966 10.2775 5.91676 10.6826 5.79522L18.7669 3.36991ZM16.0167 16C16.0167 17.0953 16.9047 17.9833 18 17.9833C19.0954 17.9833 19.9833 17.0953 19.9833 16C19.9833 14.9046 19.0954 14.0167 18 14.0167C16.9047 14.0167 16.0167 14.9046 16.0167 16ZM4.0167 19C4.0167 20.0953 4.90465 20.9833 6 20.9833C7.09535 20.9833 7.9833 20.0953 7.9833 19C7.9833 17.9046 7.09535 17.0167 6 17.0167C4.90465 17.0167 4.0167 17.9046 4.0167 19Z"></path>
                </svg>
            `;
            case 'bot-flag':
                return `
                <svg class="${iconClass}" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M4 21V15.6871M4 15.6871C9.81818 11.1377 14.1818 20.2363 20 15.6869V4.31347C14.1818 8.86284 9.81818 -0.236103 4 4.31327V15.6871Z" stroke-width="2"></path>
                </svg>
            `;
            case 'bot-hunter':
                return `
                <svg class="${iconClass}" viewBox="0 0 512 512" aria-hidden="true" fill="currentColor" stroke="none">
                    <path d="M321.85,250.69c-4-33.61-30.39-61-65.85-61-36,0-66.34,30.31-66.34,66.34S220,322.34,256,322.34c35.47,0,61.84-27.41,65.85-61a18.39,18.39,0,0,0,.49-5.32A18.71,18.71,0,0,0,321.85,250.69ZM225.12,256c0-39.95,59.88-39.6,61.76,0C285,295.55,225.12,296,225.12,256Z"></path>
                    <path d="M433.3,238.27H395c-7.27-51.81-41.57-96.15-91.93-114.52a133.34,133.34,0,0,0-29.29-7v-38c0-22.82-35.46-22.86-35.46,0V117c-34.91,4.65-68,22.22-90.69,50.08a141.57,141.57,0,0,0-30.44,71.24H78.7c-22.82,0-22.86,35.46,0,35.46H117a137.24,137.24,0,0,0,18.61,54.45c22.57,37.45,60.88,61.14,102.69,66.63V433.3c0,22.82,35.46,22.86,35.46,0V395.07c2.92-.35,5.85-.75,8.76-1.28,60.25-10.79,104-61.36,112.44-120.06H433.3C456.12,273.73,456.16,238.27,433.3,238.27ZM291.38,354.83a106,106,0,0,1-115.8-31.39c-62-73.5,5.19-188.93,99.84-170.61,49.75,9.63,84.51,53,85.5,103.17C360.05,299.87,333.32,340,291.38,354.83Z"></path>
                </svg>
            `;
            case 'leaderboards':
                return `
                <svg class="${iconClass}" viewBox="0 0 1920 1920" aria-hidden="true" fill="currentColor" stroke="none" fill-rule="evenodd" clip-rule="evenodd">
                    <path d="M1034.59 564.21L959.198 320L885.413 562.527H640L838.898 714.104L761.906 960L960.801 808.422L1159.7 960L1081.1 715.79L1280 564.21H1034.59Z"></path>
                    <path d="M357.542 0H1562.46V119.181H1920V542.868C1920 655.236 1875.36 763.003 1795.91 842.46C1731.12 907.242 1647.52 948.879 1557.95 962.049C1544.78 1051.62 1503.15 1135.22 1438.36 1200C1360.45 1277.91 1255.32 1322.35 1145.33 1324.05V1496.31C1145.33 1510.38 1150.91 1523.87 1160.86 1533.81C1170.81 1543.76 1184.3 1549.35 1198.36 1549.35C1263.32 1549.35 1325.61 1575.15 1371.54 1621.08C1417.47 1667.01 1443.28 1729.31 1443.28 1794.26V1920H476.723V1794.26C476.723 1729.31 502.528 1667.01 548.458 1621.08C594.388 1575.15 656.682 1549.35 721.639 1549.35C735.705 1549.35 749.195 1543.76 759.14 1533.81C769.086 1523.87 774.674 1510.38 774.674 1496.31V1324.05C664.677 1322.35 559.547 1277.91 481.637 1200C416.854 1135.22 375.218 1051.62 362.048 962.049C272.477 948.879 188.877 907.242 124.095 842.46C44.6379 763.003 0 655.236 0 542.868V119.181H357.542V0ZM489.832 132.29V900.41C489.832 977.693 520.533 1051.81 575.18 1106.46C629.828 1161.11 703.946 1191.81 781.229 1191.81H906.964V1496.31C906.964 1545.46 887.439 1592.6 852.684 1627.36C817.928 1662.11 770.79 1681.64 721.639 1681.64C691.77 1681.64 663.123 1693.5 642.001 1714.63C622.429 1734.2 610.805 1760.23 609.204 1787.71H1310.8C1309.2 1760.23 1297.57 1734.2 1278 1714.63C1256.88 1693.5 1228.23 1681.64 1198.36 1681.64C1149.21 1681.64 1102.07 1662.11 1067.32 1627.36C1032.56 1592.6 1013.04 1545.46 1013.04 1496.31V1191.81H1138.77C1216.05 1191.81 1290.17 1161.11 1344.82 1106.46C1399.47 1051.81 1430.17 977.693 1430.17 900.41V132.29H489.832ZM357.542 251.471H132.29V542.868C132.29 620.151 162.991 694.269 217.638 748.917C256.412 787.69 304.988 814.409 357.542 826.659V251.471ZM1562.46 826.659V251.471H1787.71V542.868C1787.71 620.151 1757.01 694.269 1702.36 748.917C1663.59 787.69 1615.01 814.409 1562.46 826.659Z"></path>
                </svg>
            `;
            case 'racer-badges':
                return `
                <svg class="${iconClass}" viewBox="0 0 149.751 149.752" aria-hidden="true" fill="currentColor" stroke="none">
                    <path d="M28.52,25.847v108.454l15.458,15.446l15.45-15.446l15.45,15.451l15.448-15.447l15.455,15.447l15.456-15.447V25.847H28.52z M92.974,108.8l-18.097-9.521l-18.1,9.521l3.453-20.163L45.577,74.364l20.248-2.952l9.053-18.339l9.046,18.339l20.252,2.952L89.522,88.637L92.974,108.8z"></path>
                    <path d="M127.935,4.241H21.817c-1.842,0-3.338,1.502-3.338,3.346c0,1.836,1.497,3.333,3.338,3.333h106.118c1.844,0,3.338-1.497,3.338-3.333C131.269,5.743,129.779,4.241,127.935,4.241z"></path>
                    <path d="M99.595,15.16H50.159c-4.192,0-7.587-3.39-7.587-7.573c0-4.191,3.39-7.586,7.587-7.586h49.436c4.183,0,7.585,3.395,7.585,7.586C107.18,11.765,103.778,15.16,99.595,15.16z"></path>
                    <path d="M74.877,25.976c-1.975,0-3.941-0.813-5.365-2.225c-1.411-1.406-2.218-3.327-2.218-5.352c0-1.973,0.812-3.945,2.218-5.357c2.846-2.829,7.934-2.829,10.722,0c1.412,1.412,2.223,3.385,2.223,5.357c0,2.024-0.821,3.945-2.223,5.352C78.815,25.163,76.892,25.976,74.877,25.976z"></path>
                </svg>
            `;
            case 'enhanced-stats':
                return `
                <svg class="${iconClass}" viewBox="0 0 24 24" aria-hidden="true" fill="currentColor" stroke="none">
                    <path fill-rule="evenodd" clip-rule="evenodd" d="M1091,264h-22a1,1,0,0,1-1-1V241a1,1,0,0,1,2,0v21h21A1,1,0,0,1,1091,264Zm-5-4a1,1,0,0,1-1-1V248a1,1,0,0,1,2,0v11A1,1,0,0,1,1086,260Zm-4,0h0a1,1,0,0,1-1-1v-7a1,1,0,0,1,1-1h0a1,1,0,0,1,1,1v7A1,1,0,0,1,1082,260Zm-4,0a1,1,0,0,1-1-1V243a1,1,0,0,1,2,0v16A1,1,0,0,1,1078,260Zm-4,0a1,1,0,0,1-1-1V249a1,1,0,0,1,2,0v10A1,1,0,0,1,1074,260Z" transform="translate(-1068 -240)"></path>
                </svg>
            `;
            case 'toolkit':
                return `
                <svg class="${iconClass}" viewBox="0 0 16 16" aria-hidden="true" fill="currentColor" stroke="none">
                    <g transform="translate(0 1)">
                        <path d="M14.217,14.949 C14.748,15.482 15.076,15.103 15.512,14.668 C15.948,14.233 16.326,13.904 15.795,13.372 C15.795,13.372 8.393,5.977 6.565,4.154 L4.987,5.731 L14.217,14.949 L14.217,14.949 Z"></path>
                        <path d="M2.048,7.015 L2.662,6.411 C2.662,6.411 2.391,5.668 2.788,5.312 C3.185,4.956 3.855,5.176 3.855,5.176 L6.01,3.093 C6.01,3.093 5.859,2.01 6.059,1.81 C6.259,1.61 8.494,0.521 8.71,0.303 L8.251,-0.156 C8.251,-0.156 5.123,0.22 4.784,0.558 C4.585,0.758 3.096,2.262 2.034,3.324 C2.034,3.324 2.3,4.083 1.95,4.433 C1.599,4.784 0.809,4.533 0.809,4.533 C0.436,4.906 0.186,5.155 0.186,5.155 C-0.077,5.42 0.078,5.792 0.401,6.115 L1.087,6.801 C1.412,7.125 1.785,7.278 2.048,7.015 L2.048,7.015 Z"></path>
                        <path d="M11.733,5.515 C12.81,6.026 14.161,5.869 15.055,4.975 C15.745,4.285 16.019,3.336 15.872,2.444 L14.351,3.963 L13.057,4.284 L11.595,2.842 L11.938,1.505 L13.445,0.017 C12.552,-0.129 11.543,0.082 10.853,0.773 C9.958,1.668 9.836,3.052 10.347,4.13 L9.353,5.123 C9.79,5.558 10.257,6.025 10.741,6.508 L11.733,5.515 L11.733,5.515 Z"></path>
                        <path d="M7.432,10.119 L5.927,8.615 L4.619,9.924 C4.537,10.004 4.479,10.095 4.438,10.19 C4.361,10.16 4.318,10.159 4.291,10.17 C4.041,10.087 3.777,10.031 3.5,10.031 C2.119,10.031 1,11.136 1,12.5 C1,13.864 2.119,14.969 3.5,14.969 C4.881,14.969 6,13.864 6,12.5 C6,12.217 5.941,11.949 5.854,11.696 C5.849,11.672 5.848,11.65 5.834,11.615 C5.938,11.572 6.036,11.514 6.122,11.427 L7.432,10.119 L7.432,10.119 Z M3.5,13.938 C2.688,13.938 2.031,13.295 2.031,12.5 C2.031,11.705 2.687,11.062 3.5,11.062 C4.313,11.062 4.969,11.705 4.969,12.5 C4.969,13.295 4.312,13.938 3.5,13.938 L3.5,13.938 Z"></path>
                    </g>
                </svg>
            `;
            default:
                return `
                <svg class="${iconClass}" viewBox="3 3 18 18" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M4 6h16M4 12h16M4 18h16"></path>
                    <circle cx="9" cy="6" r="2"></circle>
                    <circle cx="15" cy="12" r="2"></circle>
                    <circle cx="11" cy="18" r="2"></circle>
                </svg>
            `;
        }
    }

    function buildTopTabHtml(module, isActive) {
        const isOutdated = module.source === 'outdated';
        const isPreview = module.source === 'preview';
        const tabClass = `tab ntmods-top-tab${isActive ? ' is-active' : ''}${isPreview ? ' is-preview' : ''}${isOutdated ? ' is-outdated' : ''}`;
        const updateDot = '';
        return `
            <button
                type="button"
                class="${tabClass}"
                data-module-id="${escapeHtml(module.id)}"
                style="--ntmods-tab-grow:${getTopTabGrow(module)}"
            >
                <div class="bucket bucket--c bucket--xs">
                    <div class="bucket-media">
                        <span class="ntmods-top-tab-glyph">${getModuleIconMarkup(module, 'tab')}</span>
                    </div>
                    <div class="bucket-content">${escapeHtml(module.label)}${updateDot}</div>
                </div>
            </button>
        `;
    }

    function buildSidebarButtonHtml(section, isActive) {
        return `
            <button type="button" class="ntmods-nav-btn${isActive ? ' is-active' : ''}" data-section-id="${escapeHtml(section.id)}">
                <span class="ntmods-nav-btn-title">${escapeHtml(section.title)}</span>
            </button>
        `;
    }

    function buildToggleHtml(module, item) {
        const value = Boolean(getFieldValue(module, item));
        return `
            <label class="ntmods-toggle-card">
                <span class="ntmods-toggle-copy">
                    <span class="ntmods-field-title">${escapeHtml(item.label)}</span>
                    <span class="ntmods-field-help">${escapeHtml(item.help || '')}</span>
                </span>
                <span class="ntmods-switch">
                    <input type="checkbox" data-field-key="${escapeHtml(item.key)}" ${value ? 'checked' : ''}>
                    <span class="ntmods-switch-track"></span>
                </span>
            </label>
        `;
    }

    function buildSelectOptions(item, value) {
        return (item.options || []).map((option) => {
            const selected = String(option.value) === String(value) ? 'selected' : '';
            return `<option value="${escapeHtml(option.value)}" ${selected}>${escapeHtml(option.label)}</option>`;
        }).join('');
    }

    function buildMultiSelectOptions(item, value) {
        const selectedValues = new Set((Array.isArray(value) ? value : (Array.isArray(item.default) ? item.default : [])).map((entry) => String(entry)));
        return (item.options || []).map((option) => {
            const optionValue = String(option.value);
            const checked = selectedValues.has(optionValue) ? 'checked' : '';
            return `
                <label class="ntcfg-checkbox ntcfg-multi-option">
                    <input type="checkbox" value="${escapeHtml(optionValue)}" data-field-key="${escapeHtml(item.key)}" data-field-type="multi-select" ${checked}>
                    <span class="ntcfg-checkbox-label">${escapeHtml(option.label)}</span>
                </label>
            `;
        }).join('');
    }

    function buildFieldHtml(module, item) {
        const value = getFieldValue(module, item);

        if (item.type === 'toggle') {
            return buildToggleHtml(module, item);
        }

        if (item.type === 'note') {
            return `<div class="ntmods-note ntmods-note--${escapeHtml(item.tone || 'info')}">${escapeHtml(item.message || '')}</div>`;
        }

        if (item.type === 'action') {
            return `
                <div class="ntmods-action-card">
                    <button type="button" class="ntmods-action-btn ntmods-action-btn--${escapeHtml(item.style || 'secondary')}" data-action-key="${escapeHtml(item.key)}">
                        ${escapeHtml(item.label)}
                    </button>
                    ${item.help ? `<div class="ntmods-field-help">${escapeHtml(item.help)}</div>` : ''}
                </div>
            `;
        }

        if (item.type === 'color') {
            const normalized = String(value || item.default || '#1C99F4');
            return `
                <label class="ntmods-field-card">
                    <span class="ntmods-field-title">${escapeHtml(item.label)}</span>
                    <span class="ntmods-color-row">
                        <input type="color" class="ntmods-color-picker" value="${escapeHtml(normalized)}" data-field-key="${escapeHtml(item.key)}" data-field-type="color">
                        <input type="text" class="ntmods-input ntmods-color-input" value="${escapeHtml(normalized)}" data-field-key="${escapeHtml(item.key)}" data-field-type="color-text" spellcheck="false">
                    </span>
                    ${item.help ? `<span class="ntmods-field-help">${escapeHtml(item.help)}</span>` : ''}
                </label>
            `;
        }

        if (item.type === 'select') {
            return `
                <label class="ntmods-field-card">
                    <span class="ntmods-field-title">${escapeHtml(item.label)}</span>
                    <select class="ntmods-input" data-field-key="${escapeHtml(item.key)}" data-field-type="select">
                        ${buildSelectOptions(item, value)}
                    </select>
                    ${item.help ? `<span class="ntmods-field-help">${escapeHtml(item.help)}</span>` : ''}
                </label>
            `;
        }

        if (item.type === 'multi-select') {
            const selectedCount = Array.isArray(value) ? value.length : (Array.isArray(item.default) ? item.default.length : 0);
            return `
                <div class="ntmods-field-card">
                    <span class="ntmods-field-title">${escapeHtml(item.label)}</span>
                    <div class="ntcfg-multi-options">
                        ${buildMultiSelectOptions(item, value)}
                    </div>
                    <span class="ntcfg-multi-count">${escapeHtml(selectedCount)} selected</span>
                    ${item.help ? `<span class="ntmods-field-help">${escapeHtml(item.help)}</span>` : ''}
                </div>
            `;
        }

        if (item.type === 'number') {
            const minAttr = item.min == null ? '' : `min="${escapeHtml(item.min)}"`;
            const maxAttr = item.max == null ? '' : `max="${escapeHtml(item.max)}"`;
            const stepAttr = item.step == null ? '' : `step="${escapeHtml(item.step)}"`;
            return `
                <label class="ntmods-field-card">
                    <span class="ntmods-field-title">${escapeHtml(item.label)}</span>
                    <input type="number" class="ntmods-input" value="${escapeHtml(value)}" data-field-key="${escapeHtml(item.key)}" data-field-type="number" ${minAttr} ${maxAttr} ${stepAttr}>
                    ${item.help ? `<span class="ntmods-field-help">${escapeHtml(item.help)}</span>` : ''}
                </label>
            `;
        }

        if (item.type === 'mute-list') {
            return `
                <div class="ntmods-field-card" data-field-key="${escapeHtml(item.key)}">
                    <span class="ntmods-field-title">${escapeHtml(item.label)}</span>
                    ${item.help ? `<span class="ntmods-field-help">${escapeHtml(item.help)}</span>` : ''}
                    <div class="ntmods-note ntmods-note--info">Loading muted racers...</div>
                </div>
            `;
        }

        return `
            <label class="ntmods-field-card">
                <span class="ntmods-field-title">${escapeHtml(item.label)}</span>
                <input type="text" class="ntmods-input" value="${escapeHtml(value ?? '')}" data-field-key="${escapeHtml(item.key)}" data-field-type="text" placeholder="${escapeHtml(item.placeholder || '')}" spellcheck="false">
                ${item.help ? `<span class="ntmods-field-help">${escapeHtml(item.help)}</span>` : ''}
            </label>
        `;
    }

    function buildSectionHtml(module, section) {
        // Manifest modules use the generic mount (DOM rendered after innerHTML)
        if (module?.source === 'manifest') {
            return `<div class="ntmods-generic-stage" data-ntmods-generic-stage="1"></div>`;
        }

        // Preview (non-manifest) modules use the card layout fallback
        const settingItems = section.items.filter((item) => item.type !== 'note');
        const toggleCount = settingItems.filter((item) => item.type === 'toggle').length;
        const inputCount = settingItems.length - toggleCount;
        const modeLabel = 'Preview Layout';

        return `
            <div class="ntmods-stage-intro">
                <div class="ntmods-stage-intro-copy">
                    <h2 class="ntmods-stage-title">${escapeHtml(section.title)}</h2>
                    <p class="ntmods-stage-subtitle">${escapeHtml(section.subtitle || '')}</p>
                </div>
                <div class="ntmods-stage-pills">
                    <span class="ntmods-pill">${escapeHtml(modeLabel)}</span>
                    <span class="ntmods-pill">${escapeHtml(toggleCount)} toggles</span>
                    <span class="ntmods-pill">${escapeHtml(inputCount)} inputs</span>
                </div>
            </div>
            <div class="ntmods-grid">
                ${section.items.map((item) => buildFieldHtml(module, item)).join('')}
            </div>
        `;
    }

    // Build only the inner content of .ntmods-module-card for a given active module/section.
    function buildModuleCardInner(activeModule, activeSection) {
        const activeModuleSections = getRenderableSections(activeModule);
        const isBotHunterModule = activeModule.id === BOT_HUNTER_MODULE_ID;
        return `
            <div class="ntmods-module-head">
                <div class="ntmods-module-head-copy">
                    <div class="ntmods-module-overline">${
                        activeModule.source === 'manifest'
                            ? `Connected Module${activeModule.manifestVersion ? ' &middot; v' + escapeHtml(activeModule.manifestVersion) : ''}`
                            : activeModule.source === 'outdated'
                            ? 'Script Inactive'
                            : 'Not Installed'
                    }</div>
                    <h2 class="ntmods-module-title">${escapeHtml(activeModule.label)}</h2>
                    <p class="ntmods-module-description">${escapeHtml(activeModule.description || '')}</p>
                </div>
                <div class="ntmods-module-actions">
                    ${isBotHunterModule
                        ? '<button type="button" class="ntmods-rescan-btn" data-action-key="check-bot-hunter-updates">Check for Updates</button>'
                        : `<a href="${escapeHtml(MOD_SUITE_EXTENSION_URL)}" target="_blank" rel="noopener noreferrer" class="ntmods-rescan-btn ntmods-extension-card-btn">Extension Version</a>`}
                    <button type="button" class="ntmods-rescan-btn ntmods-clear-btn" data-action-key="clear-settings">Clear Settings</button>
                </div>
            </div>
            ${isBotHunterModule && activeModule._hasUpdate ? `
                <div class="ntmods-update-banner">
                    <div class="ntmods-update-banner-text">
                        <strong>Bot Hunter Update Available</strong>
                        <span class="ntmods-version-arrow">
                            <span class="ntmods-version-current">v${escapeHtml(activeModule.manifestVersion)}</span>
                            <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>
                            <span class="ntmods-version-latest">v${escapeHtml(activeModule._latestVersion)}</span>
                        </span>
                    </div>
                    <a href="${escapeHtml(BOT_HUNTER_INSTALL_URL)}" target="_blank" rel="noopener noreferrer" class="ntmods-update-banner-link">
                        Update on GreasyFork &rarr;
                    </a>
                </div>
            ` : ''}
            ${activeModule.source === 'outdated' ? `
                <div class="ntmods-not-installed ntmods-outdated-card">
                    <div class="ntmods-not-installed-icon">
                        ${getModuleIconMarkup(activeModule, 'large')}
                    </div>
                    <h3 class="ntmods-not-installed-title ntmods-outdated-title">${isBotHunterModule ? 'Script Inactive' : 'Moved to Extension'}</h3>
                    <p class="ntmods-not-installed-copy">${isBotHunterModule
                        ? `<strong>${escapeHtml(activeModule.label)}</strong> is not active in this page session. Reload Nitro Type or verify that the userscript is enabled.`
                        : `<strong>${escapeHtml(activeModule.label)}</strong> is not active in this page session. This standalone module has entered end-of-life; future fixes and features will ship through Nitro Type Mod Suite.`}</p>
                    <a href="${escapeHtml(isBotHunterModule ? BOT_HUNTER_INSTALL_URL : MOD_SUITE_EXTENSION_URL)}" target="_blank" rel="noopener noreferrer" class="ntmods-install-link ntmods-update-link">
                        ${isBotHunterModule ? 'Open Bot Hunter on GreasyFork' : 'Open Extension Version'} &rarr;
                    </a>
                    <p class="ntmods-not-installed-copy" style="margin-top:12px;opacity:0.6;">Uninstalled this mod?</p>
                    <button type="button" class="ntmods-dismiss-stale-btn" data-dismiss-stale="${escapeHtml(activeModule.id)}">Remove from Mod Menu</button>
                </div>
            ` : activeModule.source === 'preview' ? `
                <div class="ntmods-not-installed">
                    <div class="ntmods-not-installed-icon">
                        ${getModuleIconMarkup(activeModule, 'large')}
                    </div>
                    <h3 class="ntmods-not-installed-title">Available in the Extension</h3>
                    <p class="ntmods-not-installed-copy">This standalone module has entered end-of-life. Install Nitro Type Mod Suite to get the maintained version of <strong>${escapeHtml(activeModule.label)}</strong>.</p>
                    <a href="${escapeHtml(MOD_SUITE_EXTENSION_URL)}" target="_blank" rel="noopener noreferrer" class="ntmods-install-link">
                        Open Extension Version &rarr;
                    </a>
                </div>
            ` : `
                <div class="ntmods-layout">
                    <aside class="ntmods-sidebar">
                        ${activeModuleSections.map((section) => buildSidebarButtonHtml(section, section.id === activeSection.id)).join('')}
                    </aside>
                    <section class="ntmods-stage">
                        ${buildSectionHtml(activeModule, activeSection)}
                    </section>
                </div>
            `}
        `;
    }

    function buildSelfUpdateButtonHtml() {
        return `
            <a href="${escapeHtml(MOD_SUITE_EXTENSION_URL)}" target="_blank" rel="noopener noreferrer" class="ntmods-header-btn ntmods-self-update-btn" title="Install the maintained Nitro Type Mod Suite extension">
                <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path></svg>
                Extension Version
            </a>
        `;
    }

    function buildReorderButtonHtml() {
        return `
            <button type="button" class="ntmods-header-btn ntmods-reorder-btn" data-action-key="toggle-reorder">
                <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 13 12 18 17 13"></polyline><polyline points="7 6 12 11 17 6"></polyline></svg>
                Reorder Tabs
            </button>
        `;
    }

    function buildSearchControlHtml() {
        return `
            <div class="ntmods-search" data-ntmods-search>
                <button type="button" class="ntmods-header-btn ntmods-search-toggle" data-action-key="toggle-search" aria-label="Search settings" aria-expanded="false" title="Search settings">
                    <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"></circle><path d="M20 20l-3.5-3.5"></path></svg>
                </button>
                <div class="ntmods-search-panel" data-ntmods-search-panel hidden>
                    <label class="ntmods-search-input-shell">
                        <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"></circle><path d="M20 20l-3.5-3.5"></path></svg>
                        <input type="search" class="ntmods-search-input" data-ntmods-search-input placeholder="Search settings" autocomplete="off" spellcheck="false">
                        <button type="button" class="ntmods-search-clear" data-ntmods-search-clear aria-label="Clear search" title="Clear search" hidden>
                            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg>
                        </button>
                    </label>
                    <div class="ntmods-search-results" data-ntmods-search-results></div>
                </div>
            </div>
        `;
    }

    function normalizeSearchText(value) {
        return String(value ?? '')
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, ' ')
            .replace(/\s+/g, ' ')
            .trim();
    }

    function buildSearchOptionText(item) {
        const options = [];
        if (Array.isArray(item?.options)) {
            item.options.forEach((option) => {
                if (!option) return;
                options.push(option.label ?? option.value);
            });
        }
        if (Array.isArray(item?.presets)) {
            item.presets.forEach((preset) => {
                if (!preset) return;
                options.push(preset.label ?? preset.value);
            });
        }
        return options.filter((option) => String(option ?? '').trim()).join(' ');
    }

    function buildSearchEntriesForItem(module, section, item) {
        if (!item || item.type === 'note') return [];
        const entries = [];
        const baseLabel = item.label || titleCaseKey(item.key || item.type || 'setting');
        const addEntry = (entryItem, label, fieldKey, parentLabel = '') => {
            if (!fieldKey) return;
            const help = entryItem.help || item.help || '';
            const optionText = buildSearchOptionText(entryItem);
            const text = normalizeSearchText([
                module.id,
                module.label,
                module.description,
                section.id,
                section.title,
                section.subtitle,
                fieldKey,
                parentLabel,
                label,
                help,
                entryItem.type,
                optionText
            ].join(' '));
            entries.push({
                moduleId: module.id,
                moduleLabel: module.label,
                sectionId: section.id,
                sectionTitle: section.title,
                fieldKey,
                label,
                parentLabel,
                help,
                optionText,
                text
            });
        };

        addEntry(item, baseLabel, item.key);
        if (Array.isArray(item.compound)) {
            item.compound.forEach((child) => {
                const childLabel = child?.label || `${baseLabel} ${titleCaseKey(child?.key || '')}`.trim();
                addEntry(child, childLabel, child?.key, baseLabel);
            });
        }
        return entries;
    }

    function scoreSearchEntry(entry, query, terms) {
        const label = normalizeSearchText(entry.label);
        const parent = normalizeSearchText(entry.parentLabel);
        const moduleLabel = normalizeSearchText(entry.moduleLabel);
        const sectionTitle = normalizeSearchText(entry.sectionTitle);
        const help = normalizeSearchText(entry.help);
        const optionText = normalizeSearchText(entry.optionText);
        let score = 0;

        if (label === query) score += 120;
        else if (label.startsWith(query)) score += 90;
        else if (label.includes(query)) score += 70;
        if (parent && parent.includes(query)) score += 32;
        if (sectionTitle.includes(query)) score += 28;
        if (moduleLabel.includes(query)) score += 24;
        if (optionText.includes(query)) score += 18;
        if (help.includes(query)) score += 12;

        terms.forEach((term) => {
            if (label.includes(term)) score += 12;
            if (parent && parent.includes(term)) score += 7;
            if (sectionTitle.includes(term)) score += 6;
            if (moduleLabel.includes(term)) score += 5;
            if (optionText.includes(term)) score += 4;
            if (help.includes(term)) score += 2;
        });
        return score;
    }

    function searchModSettings(modules, rawQuery) {
        const query = normalizeSearchText(rawQuery);
        if (query.length < 2) return [];
        const terms = query.split(' ').filter(Boolean);
        const results = [];

        (modules || []).forEach((module) => {
            getRenderableSections(module).forEach((section) => {
                (section.items || []).forEach((item) => {
                    buildSearchEntriesForItem(module, section, item).forEach((entry) => {
                        if (!terms.every((term) => entry.text.includes(term))) return;
                        results.push({
                            ...entry,
                            score: scoreSearchEntry(entry, query, terms)
                        });
                    });
                });
            });
        });

        return results
            .sort((a, b) => {
                if (b.score !== a.score) return b.score - a.score;
                const moduleSort = String(a.moduleLabel).localeCompare(String(b.moduleLabel));
                if (moduleSort) return moduleSort;
                const sectionSort = String(a.sectionTitle).localeCompare(String(b.sectionTitle));
                if (sectionSort) return sectionSort;
                return String(a.label).localeCompare(String(b.label));
            })
            .slice(0, 24);
    }

    function truncateSearchHint(value, maxLength = 132) {
        const text = String(value || '').replace(/\s+/g, ' ').trim();
        if (!text) return '';
        if (text.length <= maxLength) return text;
        return `${text.slice(0, Math.max(0, maxLength - 3))}...`;
    }

    function buildSearchResultsHtml(modules, rawQuery) {
        const query = normalizeSearchText(rawQuery);
        if (!query) return '';
        const results = searchModSettings(modules, rawQuery);
        if (!results.length) {
            return '<div class="ntmods-search-empty">No results</div>';
        }

        return results.map((result) => {
            const hint = truncateSearchHint(result.help || result.optionText || '');
            return `
                <button type="button" class="ntmods-search-result" data-search-module-id="${escapeHtml(result.moduleId)}" data-search-section-id="${escapeHtml(result.sectionId)}" data-search-field-key="${escapeHtml(result.fieldKey)}">
                    <span class="ntmods-search-result-path">${escapeHtml(result.moduleLabel)} / ${escapeHtml(result.sectionTitle)}</span>
                    <span class="ntmods-search-result-title">${escapeHtml(result.parentLabel ? `${result.parentLabel}: ${result.label}` : result.label)}</span>
                    ${hint ? `<span class="ntmods-search-result-hint">${escapeHtml(hint)}</span>` : ''}
                </button>
            `;
        }).join('');
    }

    let pendingSearchFocus = null;
    let cleanupActiveSearchOutsideClick = null;

    function escapeCssIdent(value) {
        if (window.CSS && typeof window.CSS.escape === 'function') {
            return window.CSS.escape(String(value));
        }
        return String(value).replace(/["\\]/g, '\\$&');
    }

    function focusSearchResultTarget(fieldKey) {
        if (!fieldKey) return;
        const card = document.querySelector('.ntmods-module-card');
        if (!card) return;
        const safeKey = escapeCssIdent(fieldKey);
        const control = card.querySelector(`[data-field-key="${safeKey}"], [data-action-key="${safeKey}"]`);
        if (!control) return;
        const target = control.closest('.ntcfg-field, .ntcfg-checkbox, .ntcfg-theme-setting, .ntmods-toggle-card, .ntmods-field-card, .ntmods-action-card, .ntcfg-action') || control;

        requestAnimationFrame(() => {
            target.scrollIntoView({ behavior: 'smooth', block: 'center' });
            target.classList.remove('ntmods-search-hit-focus');
            void target.offsetWidth;
            target.classList.add('ntmods-search-hit-focus');
            if (typeof control.focus === 'function') {
                try {
                    control.focus({ preventScroll: true });
                } catch {
                    control.focus();
                }
            }
            setTimeout(() => {
                target.classList.remove('ntmods-search-hit-focus');
            }, 1800);
        });
    }

    function focusPendingSearchResult(moduleId, sectionId) {
        if (!pendingSearchFocus) return;
        if (pendingSearchFocus.moduleId !== moduleId || pendingSearchFocus.sectionId !== sectionId) return;
        const fieldKey = pendingSearchFocus.fieldKey;
        pendingSearchFocus = null;
        setTimeout(() => focusSearchResultTarget(fieldKey), 40);
    }

    function buildModMenuCapHtml({ includeReorder = false } = {}) {
        const selfUpdateButton = buildSelfUpdateButtonHtml();
        const searchButton = includeReorder ? buildSearchControlHtml() : '';
        const reorderButton = includeReorder ? buildReorderButtonHtml() : '';
        const actions = selfUpdateButton || searchButton || reorderButton
            ? `<div class="ntmods-cap-actions">${selfUpdateButton}${searchButton}${reorderButton}</div>`
            : '';

        return `
            <div class="card-cap bg--gradient ntmods-cap">
                <h1 class="h2 tbs ntmods-cap-title">Mod Menu</h1>
                ${actions}
            </div>
        `;
    }

    function buildStandaloneRuntimeWarningHtml() {
        const detections = getStandaloneRuntimeDetections();
        if (!detections.length) return '';
        const names = detections.map((item) => {
            const version = item.version ? ` v${escapeHtml(item.version)}` : '';
            const manager = item.manager ? ` (${escapeHtml(item.manager)})` : '';
            return `<strong>${escapeHtml(item.name)}</strong>${version}${manager}`;
        }).join(', ');
        const hasMultiple = detections.length !== 1;
        const title = hasMultiple ? 'Script versions still installed' : 'Script version still installed';
        const actionCopy = hasMultiple ? 'Turn off these old script versions' : 'Turn off this old script version';
        return `
            <div class="ntmods-runtime-warning ntmods-note ntmods-note--warning" role="status">
                <div class="ntmods-runtime-warning-icon" aria-hidden="true">!</div>
                <div class="ntmods-runtime-warning-copy">
                    <div class="ntmods-runtime-warning-title">${title}</div>
                    <div class="ntmods-runtime-warning-body">${actionCopy} in Tampermonkey or your userscript manager: ${names}. Keep the Nitro Type Mod Suite extension enabled. Running both versions at once can cause settings and page glitches.</div>
                </div>
            </div>
        `;
    }

    function buildPageHtml(modules, activeModuleId, activeSectionId) {
        const activeModule = modules.find((module) => module.id === activeModuleId) || modules[0] || null;
        const activeModuleSections = getRenderableSections(activeModule);
        const activeSection = activeModule
            ? activeModuleSections.find((section) => section.id === activeSectionId) || activeModuleSections[0]
            : null;


        if (!activeModule || !activeSection) {
            return `
                <section id="ntmods-app" class="card card--b card--o card--shadow card--f card--grit well well--b well--l ntmods-shell">
                    ${buildModMenuCapHtml()}
                    <div class="well--p well--l_p">
                        ${buildStandaloneRuntimeWarningHtml()}
                        <div class="ntmods-empty">No installed mods were detected yet.</div>
                    </div>
                </section>
            `;
        }

        return `
            <section id="ntmods-app" class="card card--b card--o card--shadow card--f card--grit well well--b well--l ntmods-shell">
                ${buildModMenuCapHtml({ includeReorder: true })}
                <div class="well--p well--l_p">
                    ${buildStandaloneRuntimeWarningHtml()}
                    <div class="tabs tabs--a ntmods-top-tabs">
                        ${modules.map((module) => buildTopTabHtml(module, module.id === activeModule.id)).join('')}
                    </div>
                    <div class="ntmods-module-card">
                        ${buildModuleCardInner(activeModule, activeSection)}
                    </div>
                </div>
            </section>
        `;
    }

    function buildRecoveryHtml(error) {
        const message = error?.message || String(error || 'Unknown render error');
        return `
            <section id="ntmods-app" class="card card--b card--o card--shadow card--f card--grit well well--b well--l ntmods-shell" data-ntmods-recovery="1">
                ${buildModMenuCapHtml()}
                <div class="well--p well--l_p">
                    <div class="ntmods-empty">
                        <strong>Mod Menu had trouble loading.</strong>
                        <div class="ntmods-recovery-copy">Nitro Type may still be finishing its settings page render, or one stored module manifest may be malformed.</div>
                        <div class="ntmods-recovery-error">${escapeHtml(message)}</div>
                        <button type="button" class="ntcfg-action ntcfg-action--primary" data-ntmods-retry-render>Retry Loading Mod Menu</button>
                    </div>
                </div>
            </section>
        `;
    }

    function bindRecoveryEvents() {
        const retryButton = document.querySelector('[data-ntmods-retry-render]');
        if (!retryButton) return;
        retryButton.addEventListener('click', () => {
            invalidateModuleCache();
            renderPage('', '', true);
        });
    }

    function showToast(message) {
        let toast = document.getElementById('ntmods-toast');
        if (!toast) {
            toast = document.createElement('div');
            toast.id = 'ntmods-toast';
            toast.className = 'ntmods-toast';
            document.body.appendChild(toast);
        }
        toast.textContent = message;
        toast.classList.add('is-visible');
        window.clearTimeout(showToast._timer);
        showToast._timer = window.setTimeout(() => {
            toast.classList.remove('is-visible');
        }, 1800);
    }

    function isScriptEolNoticeSuppressed() {
        try {
            if (localStorage.getItem(SCRIPT_EOL_NOTICE_DISMISSED_KEY) === '1') return true;
            const snoozeUntil = Number(localStorage.getItem(SCRIPT_EOL_NOTICE_SNOOZE_KEY) || 0);
            return Number.isFinite(snoozeUntil) && snoozeUntil > Date.now();
        } catch {
            return false;
        }
    }

    function markScriptEolNoticeSeen() {
        try {
            localStorage.setItem(SCRIPT_EOL_NOTICE_KEY, '1');
            localStorage.setItem(SCRIPT_EOL_NOTICE_DISMISSED_KEY, '1');
            localStorage.removeItem(SCRIPT_EOL_NOTICE_SNOOZE_KEY);
        } catch {
            // ignore storage errors
        }
    }

    function snoozeScriptEolNotice() {
        try {
            localStorage.removeItem(SCRIPT_EOL_NOTICE_KEY);
            localStorage.setItem(SCRIPT_EOL_NOTICE_SNOOZE_KEY, String(Date.now() + SCRIPT_EOL_NOTICE_SNOOZE_MS));
        } catch {
            // ignore storage errors
        }
    }

    function closeScriptEolNotice() {
        const modal = document.getElementById('ntmods-eol-modal');
        if (!modal) return;
        modal.classList.remove('is-visible');
        window.setTimeout(() => {
            if (modal.parentNode) modal.parentNode.removeChild(modal);
        }, 160);
    }

    function showScriptEolNotice() {
        if (!isModMenuRoute() || scriptEolNoticeClosedThisVisit || isScriptEolNoticeSuppressed()) return;
        if (document.getElementById('ntmods-eol-modal')) return;

        const modal = document.createElement('div');
        modal.id = 'ntmods-eol-modal';
        modal.className = 'ntmods-eol-modal';
        modal.setAttribute('role', 'dialog');
        modal.setAttribute('aria-modal', 'true');
        modal.setAttribute('aria-labelledby', 'ntmods-eol-title');
        modal.innerHTML = `
            <div class="ntmods-eol-backdrop" data-ntmods-eol-close></div>
            <div class="ntmods-eol-card">
                <button type="button" class="ntmods-eol-x" data-ntmods-eol-close aria-label="Close end-of-life notice">
                    <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round"><path d="M6 6l12 12M18 6 6 18"></path></svg>
                </button>
                <div class="ntmods-eol-logo-shell">
                    <img src="${escapeHtml(MOD_SUITE_LOGO_URL)}" alt="" class="ntmods-eol-logo">
                </div>
                <div class="ntmods-eol-copy">
                    <p class="ntmods-eol-kicker">Final userscript update</p>
                    <h2 id="ntmods-eol-title">Nitro Type Mod Suite has moved</h2>
                    <p>Most standalone GreasyFork modules have entered end-of-life and now ship through the browser extension. Startrack Bot Hunter remains maintained separately as the Mod Suite’s external companion userscript.</p>
                </div>
                <div class="ntmods-eol-actions">
                    <a href="${escapeHtml(MOD_SUITE_EXTENSION_URL)}" target="_blank" rel="noopener noreferrer" class="ntmods-eol-primary" data-ntmods-eol-install>
                        Install Extension Version
                    </a>
                    <button type="button" class="ntmods-eol-secondary" data-ntmods-eol-snooze>Not Now</button>
                    <button type="button" class="ntmods-eol-secondary" data-ntmods-eol-dismiss>Don't Show Again</button>
                </div>
            </div>
        `;
        document.body.appendChild(modal);

        modal.querySelectorAll('[data-ntmods-eol-close]').forEach((button) => {
            button.addEventListener('click', () => {
                scriptEolNoticeClosedThisVisit = true;
                closeScriptEolNotice();
            });
        });
        const snoozeButton = modal.querySelector('[data-ntmods-eol-snooze]');
        if (snoozeButton) {
            snoozeButton.addEventListener('click', () => {
                snoozeScriptEolNotice();
                closeScriptEolNotice();
            });
        }
        const installLink = modal.querySelector('[data-ntmods-eol-install]');
        if (installLink) {
            installLink.addEventListener('click', () => {
                markScriptEolNoticeSeen();
                closeScriptEolNotice();
            });
        }
        const dismissButton = modal.querySelector('[data-ntmods-eol-dismiss]');
        if (dismissButton) {
            dismissButton.addEventListener('click', () => {
                markScriptEolNoticeSeen();
                closeScriptEolNotice();
            });
        }
        window.requestAnimationFrame(() => modal.classList.add('is-visible'));
    }

    function queueScriptEolNotice() {
        if (!isModMenuRoute() || scriptEolNoticeClosedThisVisit || isScriptEolNoticeSuppressed()) return;
        window.setTimeout(showScriptEolNotice, 180);
    }

    function clearModMenuOwnedStorage() {
        removePersistentJson(UI_STATE_KEY);
        removePersistentJson(TAB_ORDER_KEY);
        removePersistentJson(VERSION_CACHE_KEY);
        removePersistentJson(BOT_HUNTER_VERSION_CHECK_TIMESTAMP_KEY);
        try {
            Object.keys(localStorage).forEach((key) => {
                if (key.startsWith(PREVIEW_VALUE_PREFIX)) {
                    localStorage.removeItem(key);
                }
            });
        } catch { /* ignore */ }
    }

    function createGlobalResetRequestId() {
        return `ntcfg-reset-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
    }

    function finalizeGlobalReset(timedOut = false) {
        if (!globalResetState) return;
        if (globalResetState.timeoutId) {
            clearTimeout(globalResetState.timeoutId);
        }
        const responded = globalResetState.responded.size;
        const expected = globalResetState.expected.size;
        const failed = globalResetState.failed.size;
        globalResetState = null;
        _versionCheckDone = false;
        invalidateModuleCache();
        renderPage('', '', true);
        if (timedOut) {
            showToast(`Clear finished with partial responses (${responded}/${expected}).`);
        } else if (failed > 0) {
            showToast(`Clear finished with issues in ${failed} of ${expected} installed mods.`);
        } else {
            showToast('Settings and caches cleared.');
        }
    }

    function beginGlobalReset(modules) {
        if (globalResetState) {
            showToast('Clear already in progress...');
            return;
        }
        const expected = new Set(
            modules
                .filter((module) => module.source === 'manifest' && module.manifest?.supportsGlobalReset !== false)
                .map((module) => module.id)
        );
        const requestId = createGlobalResetRequestId();
        globalResetState = {
            requestId,
            expected,
            responded: new Set(),
            failed: new Map(),
            timeoutId: window.setTimeout(() => finalizeGlobalReset(true), 4000)
        };

        clearModMenuOwnedStorage();

        document.dispatchEvent(new CustomEvent('ntcfg:action', {
            detail: {
                script: '*',
                key: 'clear-settings',
                scope: 'prefs+caches',
                requestId
            }
        }));

        if (expected.size === 0) {
            finalizeGlobalReset(false);
            return;
        }

        showToast(`Clearing settings for ${expected.size} installed mods...`);
    }

    document.addEventListener('ntcfg:action-result', (event) => {
        const detail = event?.detail || {};
        if (!globalResetState || detail.requestId !== globalResetState.requestId) return;
        if (!globalResetState.expected.has(detail.script)) return;
        globalResetState.responded.add(detail.script);
        if (detail.status !== 'success') {
            globalResetState.failed.set(detail.script, detail.error || 'Unknown reset error');
        }
        if (globalResetState.responded.size >= globalResetState.expected.size) {
            finalizeGlobalReset(false);
        }
    });

    function ensureStyles() {
        const style = document.getElementById('ntmods-style') || document.createElement('style');
        const shouldAppend = !style.parentNode;
        style.id = 'ntmods-style';
        style.textContent = `
.ntmods-dropdown-item.is-current .dropdown-link {
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
}
.ntmods-dropdown-item .dropdown-link .icon {
    opacity: 0.92;
}
.ntmods-dropdown-item .dropdown-link .ntmods-inline-icon {
    opacity: 1;
    color: #9fb2c6 !important;
    fill: currentColor !important;
}
.ntmods-dropdown-item .dropdown-link:hover .ntmods-inline-icon,
.ntmods-dropdown-item.is-current .dropdown-link .ntmods-inline-icon {
    color: #c8d5e2 !important;
    fill: currentColor !important;
}
.ntmods-inline-icon {
    width: 16px;
    height: 16px;
    display: inline-block;
    vertical-align: -3px;
    shape-rendering: geometricPrecision;
}
.ntmods-shell {
    color: #eef3ff;
    display: flex;
    flex-direction: column;
    --ntmods-panel-dark-top: #202433;
    --ntmods-panel-dark-mid: #1d2130;
    --ntmods-panel-dark-bottom: #1b1f2d;
    --ntmods-module-panel-top: #3b3e52;
    --ntmods-module-panel-bottom: #34384a;
    --ntmods-module-panel-edge: rgba(145, 160, 210, 0.21);
}
.ntmods-shell > .well--p.well--l_p {
    padding-bottom: 0 !important;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    background: url(/dist/site/images/backgrounds/bg-noise.png) top left repeat, linear-gradient(180deg, var(--ntmods-panel-dark-top) 0%, var(--ntmods-panel-dark-mid) 100%);
    background-attachment: scroll, scroll;
}
.ntmods-cap {
    display: flex !important;
    flex-direction: row !important;
    justify-content: flex-start !important;
    gap: 0;
    align-items: center !important;
    padding: 12px 18px;
    background: url(/dist/site/images/backgrounds/bg-noise.png) top left repeat, linear-gradient(90deg, #1c99f4 0%, #167ac3 42%, #0f4f86 100%);
    background-attachment: scroll, scroll;
}
.ntmods-cap-copy {
    min-width: 0;
    max-width: 720px;
}
.ntmods-cap-kicker {
    margin-bottom: 8px;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 11px;
    font-weight: 700;
    color: rgba(255, 255, 255, 0.82);
}
.ntmods-cap-title {
    margin: 0;
    text-align: left !important;
}
.ntmods-cap-subtitle {
    margin: 0;
    font-size: 12px;
    line-height: 1.45;
    color: rgba(255, 255, 255, 0.92);
}
.ntmods-cap-stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(96px, 1fr));
    gap: 10px;
    min-width: 290px;
}
.ntmods-cap-stat {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 12px 14px;
    border-radius: 12px;
    background: rgba(10, 18, 30, 0.24);
    backdrop-filter: blur(1px);
    border: 1px solid rgba(255, 255, 255, 0.12);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.ntmods-cap-stat-value {
    font-family: "Montserrat", sans-serif;
    font-size: 24px;
    line-height: 1;
    font-weight: 700;
    color: #fff;
}
.ntmods-cap-stat-label {
    margin-top: 5px;
    font-size: 11px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.76);
}
.ntmods-banner {
    margin-bottom: 14px;
    padding: 12px 14px;
    border-radius: 12px;
    background: linear-gradient(135deg, rgba(255, 193, 7, 0.18), rgba(255, 146, 43, 0.16));
    border: 1px solid rgba(255, 196, 79, 0.26);
    color: #ffe8b6;
}
.ntmods-banner-title {
    font-family: "Montserrat", sans-serif;
    font-size: 14px;
    font-weight: 700;
    margin-bottom: 5px;
    color: #fff0c9;
}
.ntmods-banner-copy {
    font-size: 12px;
    line-height: 1.55;
}
.ntmods-top-tabs {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
    align-items: flex-end;
    overflow: visible;
    margin: 0 0 -1px 0;
    padding: 4px 10px 0;
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
    position: relative;
    z-index: 2;
    border-bottom: 0 !important;
}
.ntmods-top-tabs::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -3px;
    height: 9px;
    background: var(--ntmods-module-panel-top);
    opacity: 1;
    pointer-events: none;
    z-index: 2;
}
.ntmods-top-tab {
    appearance: none;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1 1 auto;
    min-width: 0;
    border: 1px solid transparent;
    border-bottom: 0;
    border-radius: 8px 8px 0 0;
    background: transparent;
    color: #aeb4c7;
    padding: 15px 5px 14px;
    cursor: pointer;
    position: relative;
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
    box-shadow: none;
    z-index: 1;
}
.ntmods-top-tab:hover {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.05);
    color: #eff3ff;
}
.ntmods-top-tab.is-active {
    background: var(--ntmods-module-panel-top) !important;
    background-image: none !important;
    color: #fff;
    border-color: rgba(150, 165, 220, 0.18);
    border-bottom-color: transparent !important;
    box-shadow: none;
    opacity: 1 !important;
    z-index: 3;
    margin-top: -4px;
    padding-bottom: 18px;
}
.ntmods-top-tab.is-active::before {
    display: none !important;
}
.ntmods-top-tab.is-active::after {
    content: "";
    position: absolute;
    left: -1px;
    right: -1px;
    bottom: -4px;
    height: 10px;
    background: var(--ntmods-module-panel-top) !important;
    opacity: 1 !important;
    pointer-events: none;
    z-index: 4;
}
.ntmods-top-tab .bucket {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    width: 100% !important;
    gap: 7px !important;
    margin: 0 !important;
    padding: 0 !important;
    opacity: 0.8;
    transform: translateY(-4px);
    transition: opacity 0.12s ease, transform 0.12s ease;
}
.ntmods-top-tab:hover .bucket {
    opacity: 0.96;
}
.ntmods-top-tab.is-active .bucket {
    opacity: 1;
}
.ntmods-top-tab .bucket-media {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    margin: 0 !important;
    padding: 0 !important;
    flex: 0 0 auto;
}
.ntmods-top-tab .bucket-content {
    margin: 0 !important;
    padding: 0 !important;
    flex: 0 0 auto;
    font-family: "Montserrat", sans-serif;
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
}
.ntmods-top-tab-glyph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    color: currentColor;
}
.ntmods-top-tab.is-active .ntmods-top-tab-glyph {
    color: currentColor;
    filter: drop-shadow(0 0 3px rgba(140, 170, 255, 0.3));
}
.ntmods-top-tab.is-active .bucket-content {
    color: #fff;
    text-shadow: 0 0 8px rgba(160, 185, 255, 0.2);
}
.ntmods-mod-icon {
    display: block;
    width: 100%;
    height: 100%;
}
.ntmods-mod-icon--tab {
    width: 19px;
    height: 19px;
    flex: 0 0 19px;
}
.ntmods-top-tab-glyph .ntmods-mod-icon--tab {
    max-width: 19px;
    max-height: 19px;
    stroke-width: 1.9;
    overflow: visible;
    transform-origin: center;
}
.ntmods-mod-icon--chip {
    width: 20px;
    height: 20px;
}
.ntmods-module-card {
    display: flex;
    flex-direction: column;
    min-height: 900px;
    flex: 1 1 auto;
    border-radius: 0 0 18px 18px;
    background: url(/dist/site/images/backgrounds/bg-noise.png) top left repeat, linear-gradient(180deg, #292c3c 0%, var(--ntmods-panel-dark-bottom) 100%);
    background-attachment: scroll, scroll;
    border: 1px solid var(--ntmods-module-panel-edge);
    border-top-color: rgba(145, 160, 210, 0.16);
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.32), inset 0 1px 0 rgba(255, 255, 255, 0.035);
    overflow: visible;
    margin-top: 0;
    position: relative;
    z-index: 1;
}
.ntmods-module-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    margin: 0 -1px;
    padding: 22px 24px 24px;
    background: linear-gradient(180deg, var(--ntmods-module-panel-top) 0%, var(--ntmods-module-panel-top) 16%, var(--ntmods-module-panel-bottom) 100%);
    border-top: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.105);
    box-shadow: inset 1px 0 0 var(--ntmods-module-panel-edge), inset -1px 0 0 var(--ntmods-module-panel-edge), inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 12px 28px rgba(0, 0, 0, 0.2);
}
.ntmods-module-head-copy {
    min-width: 0;
}
.ntmods-module-overline {
    margin-bottom: 4px;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    font-size: 11px;
    color: #8ea4d0;
}
.ntmods-module-title {
    margin: 0;
    font-family: "Montserrat", sans-serif;
    font-size: 28px;
    line-height: 1.08;
    color: #fff;
}
.ntmods-module-description {
    margin: 6px 0 0;
    font-size: 13px;
    line-height: 1.5;
    color: #adbbdb;
}
.ntmods-module-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    flex: 0 0 auto;
    flex-wrap: wrap;
}
.ntmods-rescan-btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 38px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    background: rgba(255, 255, 255, 0.08);
    color: rgba(255, 255, 255, 0.8);
    border-radius: 9px;
    padding: 9px 14px;
    cursor: pointer;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.01em;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.10),
        0 8px 18px rgba(0, 0, 0, 0.22);
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;
    text-decoration: none;
}
.ntmods-rescan-btn:hover {
    background: rgba(255, 255, 255, 0.14);
    border-color: rgba(255, 255, 255, 0.3);
    color: #fff;
    text-decoration: none;
    box-shadow:
        inset 0 2px 5px rgba(0, 0, 0, 0.22),
        inset 0 1px 0 rgba(255, 255, 255, 0.07),
        0 5px 12px rgba(0, 0, 0, 0.18);
}
.ntmods-rescan-btn:active {
    box-shadow:
        inset 0 3px 7px rgba(0, 0, 0, 0.28),
        0 3px 8px rgba(0, 0, 0, 0.16);
}
.ntmods-clear-btn {
    border-color: rgba(214, 47, 58, 0.42);
    background: rgba(214, 47, 58, 0.18);
    color: #ffe3e6;
}
.ntmods-clear-btn:hover {
    background: rgba(214, 47, 58, 0.25);
    border-color: rgba(214, 47, 58, 0.54);
    color: #fff;
}
.ntmods-extension-card-btn {
    border-color: rgba(87, 191, 255, 0.38);
    background: rgba(28, 153, 244, 0.16);
    color: #d9f0ff;
}
.ntmods-extension-card-btn:hover {
    border-color: rgba(87, 191, 255, 0.58);
    background: rgba(28, 153, 244, 0.26);
    color: #fff;
}
.ntmods-layout {
    display: flex;
    gap: 18px;
    padding: 18px;
    background: transparent;
}
.ntmods-sidebar {
    width: 264px;
    max-width: 264px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.ntmods-nav-btn {
    box-shadow: none;
    justify-content: flex-start;
    width: 100%;
    backface-visibility: hidden;
    background: #393c50;
    border: 1px solid transparent;
    color: #a6aac1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    font-family: "Montserrat", sans-serif;
    font-size: 14px;
    overflow: hidden;
    padding: 13px 16px;
    position: relative;
    text-align: left;
    transition: all 0.12s linear;
}
.ntmods-nav-btn:first-child {
    border-radius: 5px 5px 0 0;
}
.ntmods-nav-btn:last-child {
    border-radius: 0 0 5px 5px;
}
.ntmods-nav-btn:hover {
    background: #585e7d;
    color: #e2e3eb;
}
.ntmods-nav-btn.is-active {
    background: #167ac3 !important;
    color: #fff;
    text-shadow: 0 2px 2px rgba(2, 2, 2, 0.25);
}
.ntmods-nav-btn-title {
    font-size: 13px;
    font-weight: 500;
}
.ntmods-stage {
    min-width: 0;
    flex: 1;
    border-radius: 10px;
    background: #2b2e3f;
    border: 1px solid rgba(255, 255, 255, 0.08);
    padding: 18px;
}
.ntmods-stage-intro {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    align-items: flex-start;
    margin-bottom: 18px;
}
.ntmods-stage-title {
    margin: 0;
    font-family: "Montserrat", sans-serif;
    font-size: 22px;
    line-height: 1.1;
    color: #fff;
}
.ntmods-stage-subtitle {
    margin: 6px 0 0;
    font-size: 13px;
    line-height: 1.55;
    color: #aebadc;
}
.ntmods-stage-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: flex-end;
}
.ntmods-pill {
    display: inline-flex;
    align-items: center;
    border-radius: 999px;
    padding: 7px 10px;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.08);
    font-size: 11px;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: #c5d4f6;
}
.ntmods-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
}
@media (max-width: 1220px) {
    .ntmods-top-tabs {
        gap: 4px;
        padding-left: 8px;
        padding-right: 8px;
    }
    .ntmods-top-tab {
        padding: 13px 6px 12px;
    }
    .ntmods-top-tab .bucket {
        gap: 3px !important;
    }
    .ntmods-top-tab .bucket-content {
        font-size: 12px;
    }
    .ntmods-top-tab-glyph {
        width: 16px;
        height: 16px;
        flex-basis: 16px;
    }
    .ntmods-mod-icon--tab {
        width: 15px;
        height: 15px;
        flex-basis: 15px;
    }
}
.ntmods-field-card,
.ntmods-action-card,
.ntmods-note,
.ntmods-toggle-card {
    box-sizing: border-box;
    min-width: 0;
}
.ntmods-field-card,
.ntmods-action-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 14px;
    border-radius: 14px;
    background: #2e3346;
    border: 1px solid rgba(255, 255, 255, 0.08);
}
.ntmods-toggle-card {
    display: flex;
    justify-content: space-between;
    gap: 14px;
    padding: 14px;
    border-radius: 14px;
    background: #2e3346;
    border: 1px solid rgba(255, 255, 255, 0.08);
}
.ntmods-toggle-copy {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}
.ntmods-field-title {
    font-size: 13px;
    font-weight: 700;
    color: #edf3ff;
}
.ntmods-field-help {
    font-size: 12px;
    line-height: 1.45;
    color: #9aa8c9;
}
.ntmods-input {
    width: 100%;
    box-sizing: border-box;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 10px;
    background: #1d2030;
    color: #eef3ff;
    padding: 10px 11px;
    font-size: 13px;
}
.ntmods-input:focus {
    outline: none;
    border-color: #1c99f4;
    box-shadow: 0 0 0 2px rgba(28, 153, 244, 0.22);
}
.ntmods-color-row {
    display: flex;
    align-items: center;
    gap: 8px;
}
.ntmods-color-picker {
    width: 48px;
    min-width: 48px;
    height: 42px;
    border-radius: 10px;
    background: #1d2030;
    border: 1px solid rgba(255, 255, 255, 0.14);
    padding: 0;
}
.ntmods-color-input {
    text-transform: uppercase;
}
.ntmods-switch {
    position: relative;
    width: 42px;
    height: 24px;
    flex: 0 0 auto;
}
.ntmods-switch input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
    z-index: 2;
}
.ntmods-switch-track {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 999px;
    background: #5b627f;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
    transition: background 0.14s ease;
}
.ntmods-switch-track::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #fff;
    transition: transform 0.12s ease;
}
.ntmods-switch input:checked + .ntmods-switch-track {
    background: #d62f3a;
    box-shadow: 0 4px 18px rgba(214, 47, 58, 0.28);
}
.ntmods-switch input:checked + .ntmods-switch-track::after {
    transform: translateX(18px);
}
.ntmods-action-btn {
    appearance: none;
    border: 0;
    border-radius: 10px;
    padding: 11px 14px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 700;
    color: #fff;
}
.ntmods-action-btn--primary {
    background: #167ac3;
}
.ntmods-action-btn--secondary {
    background: #434965;
}
.ntmods-action-btn--danger {
    background: #a5333d;
}
.ntmods-action-btn:hover {
    filter: brightness(1.08);
}
.ntmods-note {
    grid-column: 1 / -1;
    padding: 14px;
    border-radius: 14px;
    font-size: 12px;
    line-height: 1.55;
    border: 1px solid transparent;
}
.ntmods-note--info {
    background: rgba(28, 153, 244, 0.1);
    border-color: rgba(28, 153, 244, 0.22);
    color: #cde6ff;
}
.ntmods-note--warning {
    background: rgba(255, 176, 32, 0.12);
    border-color: rgba(255, 176, 32, 0.22);
    color: #ffe1a6;
}
.ntmods-runtime-warning {
    display: flex !important;
    align-items: flex-start !important;
    gap: 12px !important;
    margin: 0 14px 14px !important;
    padding: 14px 16px !important;
    box-sizing: border-box !important;
    border-radius: 8px !important;
}
.ntmods-runtime-warning-icon {
    width: 22px !important;
    height: 22px !important;
    border-radius: 50% !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    flex: 0 0 22px !important;
    margin-top: 2px !important;
    color: #1b1f2d !important;
    background: #ffcf66 !important;
    font-size: 14px !important;
    line-height: 1 !important;
    font-weight: 900 !important;
}
.ntmods-runtime-warning-copy {
    min-width: 0 !important;
    flex: 1 1 auto !important;
}
.ntmods-runtime-warning-title {
    margin: 0 0 4px !important;
    color: #fff0c9 !important;
    font-size: 13px !important;
    line-height: 1.25 !important;
    font-weight: 800 !important;
}
.ntmods-runtime-warning-body {
    color: #ffe1a6 !important;
    font-size: 12px !important;
    line-height: 1.45 !important;
}
.ntmods-empty {
    padding: 28px;
    text-align: center;
    border-radius: 16px;
    background: rgba(255, 255, 255, 0.04);
    color: #d2dcf5;
}
.ntmods-recovery-copy {
    max-width: 640px;
    margin: 10px auto 0;
    color: rgba(210, 220, 245, 0.78);
    line-height: 1.45;
}
.ntmods-recovery-error {
    max-width: 640px;
    margin: 14px auto;
    padding: 10px 12px;
    border-radius: 8px;
    background: rgba(214, 47, 58, 0.12);
    color: #ffc6cc;
    font-family: monospace;
    font-size: 12px;
    line-height: 1.35;
    overflow-wrap: anywhere;
}
.ntmods-race-options-stage,
.ntmods-generic-stage {
    min-width: 0;
}
.ntmods-ro-panel {
    min-width: 0;
}
.ntcfg-panel-title {
    margin: 0;
    font-size: 20px;
    font-family: "Montserrat", sans-serif;
    color: #fff;
}
.ntcfg-panel-subtitle {
    margin: 6px 0 16px;
    color: #b5bad3;
    font-size: 13px;
    line-height: 1.45;
}
.ntcfg-fields {
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.ntcfg-field {
    display: block;
}
.ntcfg-field-title {
    color: #d8dcf2;
    font-size: 13px;
    margin-bottom: 6px;
}
.ntcfg-field-help {
    margin-top: 5px;
    color: #99a4c5;
    font-size: 12px;
    line-height: 1.35;
}
.ntcfg-field-warning {
    color: #ffb8b8;
}
.ntcfg-input {
    width: 100%;
    box-sizing: border-box;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 8px;
    background: #1d2030;
    color: #eef3ff;
    padding: 10px;
    font-size: 13px;
}
.ntcfg-input:focus {
    outline: none;
    border-color: #1c99f4;
    box-shadow: 0 0 0 2px rgba(28, 153, 244, 0.28);
}
.ntcfg-input.ntcfg-input-warning {
    border-color: rgba(255, 90, 90, 0.85);
    box-shadow: 0 0 0 2px rgba(255, 90, 90, 0.2);
}
.ntcfg-range-field {
    display: block;
    padding: 14px 16px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.025);
}
.ntcfg-range-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}
.ntcfg-range-title {
    margin-bottom: 0;
}
.ntcfg-range-value {
    flex: 0 0 auto;
    min-width: 68px;
    padding: 4px 9px;
    border-radius: 999px;
    background: rgba(28, 153, 244, 0.16);
    color: #d9ecff;
    font-size: 12px;
    font-weight: 700;
    text-align: center;
}
.ntcfg-range-input {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 22px;
    margin: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
}
.ntcfg-range-input:focus {
    outline: none;
}
.ntcfg-range-input::-webkit-slider-runnable-track {
    -webkit-appearance: none;
    height: 6px;
    border-radius: 999px;
    background: linear-gradient(
        90deg,
        #1c99f4 0%,
        #1c99f4 var(--ntcfg-range-fill, 0%),
        rgba(255, 255, 255, 0.14) var(--ntcfg-range-fill, 0%),
        rgba(255, 255, 255, 0.14) 100%
    );
}
.ntcfg-range-input::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 20px;
    height: 20px;
    margin-top: -7px;
    border: 3px solid #f5f8ff;
    border-radius: 50%;
    background: #1c99f4;
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.35);
}
.ntcfg-range-input:focus::-webkit-slider-thumb {
    box-shadow: 0 0 0 4px rgba(28, 153, 244, 0.22), 0 3px 10px rgba(0, 0, 0, 0.35);
}
.ntcfg-range-scale {
    display: flex;
    justify-content: space-between;
    margin-top: 4px;
    color: #7f8aaa;
    font-size: 11px;
    font-weight: 600;
}
.ntcfg-color-row {
    display: flex;
    gap: 8px;
}
.ntcfg-color-picker {
    width: 52px;
    min-width: 52px;
    padding: 0;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.18);
    background: #1d2030;
    cursor: pointer;
}
.ntcfg-color-hex {
    flex: 1;
    text-transform: uppercase;
}
.ntcfg-confirm-overlay {
    position: fixed;
    inset: 0;
    z-index: 2147483647;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    padding: 20px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 160ms ease;
    font-family: Montserrat, Arial, sans-serif;
}
.ntcfg-confirm-overlay.is-visible {
    opacity: 1;
    pointer-events: auto;
}
.ntcfg-confirm-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(5, 8, 18, 0.76);
    backdrop-filter: blur(4px);
}
.ntcfg-confirm-dialog {
    position: relative;
    width: min(560px, calc(100vw - 32px));
    overflow: hidden;
    border: 1px solid rgba(127, 140, 178, 0.42);
    border-radius: 10px;
    background: #232636 url('/dist/site/images/backgrounds/bg-noise.png') top left repeat;
    color: #fff;
    box-shadow: 0 26px 70px rgba(0, 0, 0, 0.58), inset 0 1px 0 rgba(255, 255, 255, 0.06);
    transform: translateY(12px) scale(0.98);
    transition: transform 180ms ease;
}
.ntcfg-confirm-overlay.is-visible .ntcfg-confirm-dialog {
    transform: translateY(0) scale(1);
}
.ntcfg-confirm-dialog:focus {
    outline: none;
}
.ntcfg-confirm-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    padding: 18px 20px;
    background: url('/dist/site/images/backgrounds/bg-noise.png') top left repeat, linear-gradient(90deg, #1c99f4 0%, #167ac3 52%, #0f4f86 100%);
    box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.12);
}
.ntcfg-confirm-heading-group {
    display: flex;
    align-items: center;
    min-width: 0;
    gap: 13px;
}
.ntcfg-confirm-logo {
    width: 42px;
    height: 42px;
    flex: 0 0 42px;
    object-fit: contain;
    filter: drop-shadow(0 4px 7px rgba(0, 0, 0, 0.28));
}
.ntcfg-confirm-title {
    min-width: 0;
    margin: 0;
    color: #fff;
    font-size: 23px;
    font-weight: 800;
    line-height: 1.2;
    text-shadow: 0 2px 2px rgba(2, 2, 2, 0.25);
}
.ntcfg-confirm-close {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    flex: 0 0 36px;
    margin: 0;
    padding: 0 0 3px;
    border: 0;
    border-radius: 7px;
    background: #d62f3a;
    color: rgba(255, 255, 255, 0.92);
    box-shadow: 0 5px 12px rgba(89, 12, 20, 0.32);
    font: 400 28px/1 Arial, sans-serif;
    cursor: pointer;
}
.ntcfg-confirm-close:hover {
    background: #e23d48;
}
.ntcfg-confirm-close:focus-visible,
.ntcfg-confirm-button:focus-visible {
    outline: 3px solid rgba(255, 221, 55, 0.9);
    outline-offset: 2px;
}
.ntcfg-confirm-body {
    padding: 26px 28px 22px;
    background: linear-gradient(180deg, rgba(42, 45, 61, 0.97) 0%, rgba(35, 38, 54, 0.97) 100%);
}
.ntcfg-confirm-message {
    margin: 0;
    color: #e3e7f7;
    font-size: 15px;
    line-height: 1.65;
    white-space: pre-line;
}
.ntcfg-confirm-note {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-top: 20px;
    padding: 14px 15px;
    border: 1px solid rgba(28, 153, 244, 0.3);
    border-left: 3px solid #1c99f4;
    border-radius: 7px;
    background: rgba(10, 24, 41, 0.48);
    color: #c9d5ea;
    font-size: 13px;
    line-height: 1.55;
}
.ntcfg-confirm-note-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    margin-top: 1px;
    border: 2px solid #ffdb24;
    border-radius: 50%;
    color: #ffdb24;
    font: 800 12px/1 Arial, sans-serif;
}
.ntcfg-confirm-note-copy {
    min-width: 0;
}
.ntcfg-confirm-overlay--warning .ntcfg-confirm-note {
    border-color: rgba(255, 219, 36, 0.28);
    border-left-color: #ffdb24;
    background: rgba(55, 44, 8, 0.3);
}
.ntcfg-confirm-overlay--danger .ntcfg-confirm-note {
    border-color: rgba(214, 47, 58, 0.32);
    border-left-color: #d62f3a;
    background: rgba(63, 15, 22, 0.3);
}
.ntcfg-confirm-footer {
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    padding: 18px 28px 22px;
    border-top: 1px solid rgba(255, 255, 255, 0.07);
    background: #232636 url('/dist/site/images/backgrounds/bg-noise.png') top left repeat;
}
.ntcfg-confirm-button {
    appearance: none;
    min-width: 118px;
    margin: 0;
    padding: 11px 20px;
    border-radius: 6px;
    font: 700 14px/1.2 Montserrat, Arial, sans-serif;
    cursor: pointer;
}
.ntcfg-confirm-button--secondary {
    border: 1px solid #565c74;
    background: rgba(255, 255, 255, 0.04);
    color: #d7dced;
}
.ntcfg-confirm-button--secondary:hover {
    background: rgba(255, 255, 255, 0.09);
}
.ntcfg-confirm-button--primary {
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: linear-gradient(180deg, #21a5ff 0%, #1688d6 100%);
    color: #fff;
    box-shadow: 0 7px 16px rgba(0, 89, 154, 0.32);
}
.ntcfg-confirm-button--primary:hover {
    background: linear-gradient(180deg, #34afff 0%, #1c92e4 100%);
}
.ntcfg-confirm-overlay--danger .ntcfg-confirm-button--primary {
    background: linear-gradient(180deg, #e23d48 0%, #c72732 100%);
    box-shadow: 0 7px 16px rgba(89, 12, 20, 0.32);
}
.ntcfg-confirm-overlay--danger .ntcfg-confirm-button--primary:hover {
    background: linear-gradient(180deg, #ed4b56 0%, #d62f3a 100%);
}
@media (max-width: 560px) {
    .ntcfg-confirm-overlay {
        padding: 14px;
    }
    .ntcfg-confirm-header,
    .ntcfg-confirm-body,
    .ntcfg-confirm-footer {
        padding-left: 18px;
        padding-right: 18px;
    }
    .ntcfg-confirm-title {
        font-size: 19px;
    }
    .ntcfg-confirm-footer {
        flex-direction: column-reverse;
    }
    .ntcfg-confirm-button {
        width: 100%;
    }
}
@media (prefers-reduced-motion: reduce) {
    .ntcfg-confirm-overlay,
    .ntcfg-confirm-dialog {
        transition: none;
    }
}
.ntcfg-checkbox {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    background: #252839;
    padding: 10px 12px;
}
.ntcfg-checkbox-label {
    color: #d8dcf2;
    font-size: 13px;
}
.ntcfg-checkbox-copy {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.ntcfg-checkbox-help {
    color: #99a4c5;
    font-size: 12px;
    line-height: 1.35;
}
.ntcfg-checkbox-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}
.ntcfg-checkbox-controls .ntcfg-color-picker {
    width: 38px;
    min-width: 38px;
    height: 30px;
}
.ntcfg-checkbox-controls .ntcfg-input {
    width: 84px;
    padding: 6px 8px;
    font-size: 12px;
}
.ntcfg-checkbox-controls select.ntcfg-input {
    width: auto;
}
.ntcfg-multi-options {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 8px;
    margin-top: 8px;
}
.ntcfg-multi-option {
    justify-content: flex-start;
    cursor: pointer;
}
.ntcfg-multi-option input {
    width: 18px;
    height: 18px;
    accent-color: #1c99f4;
    flex: 0 0 auto;
}
.ntcfg-multi-count {
    color: #99a4c5;
    font-size: 12px;
    margin-top: 8px;
}
.ntcfg-switch {
    position: relative;
    width: 40px;
    height: 24px;
    flex: 0 0 auto;
    display: inline-block;
    cursor: pointer;
}
.ntcfg-switch.is-pending {
    opacity: 0.58;
    cursor: wait;
}
.ntcfg-switch input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    margin: 0;
    z-index: 2;
}
.ntcfg-switch-track {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 999px;
    background: #585e7d;
    transition: background 0.15s ease;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
    pointer-events: none;
}
.ntcfg-switch-track::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #fff;
    transition: transform 0.1s ease;
}
.ntcfg-switch input:checked + .ntcfg-switch-track {
    background: #d62f3a;
    box-shadow: 0 2px 20px rgba(214, 47, 58, 0.35);
}
.ntcfg-switch input:checked + .ntcfg-switch-track::after {
    transform: translateX(16px);
}
.ntcfg-action {
    border: 0;
    border-radius: 8px;
    padding: 10px 14px;
    font-size: 13px;
    cursor: pointer;
    color: #fff;
    background: #393c50;
}
.ntcfg-action:hover {
    filter: brightness(1.1);
}
.ntcfg-action.ntcfg-primary {
    background: #167ac3;
}
.ntcfg-inline-action {
    align-self: flex-start;
}
.ntcfg-action--primary {
    background: #167ac3;
}
.ntcfg-action--secondary {
    background: #393c50;
}
.ntcfg-action--danger {
    background: transparent;
    color: #ff6b6b;
    font-size: 18px;
    padding: 4px 8px;
    line-height: 1;
}
.ntcfg-action--danger:hover {
    color: #ff4444;
}
.ntcfg-keymap-container {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.ntcfg-keymap-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.ntcfg-keymap-row.is-invalid {
    padding: 6px 8px;
    border-radius: 8px;
    background: rgba(255, 107, 107, 0.08);
    border: 1px solid rgba(255, 107, 107, 0.16);
}
.ntcfg-keymap-mod-select {
    width: auto !important;
    min-width: 0 !important;
    max-width: 110px;
    padding: 8px 6px !important;
    font-size: 12px !important;
    flex-shrink: 0;
}
.ntcfg-keymap-plus {
    color: #99a4c5;
    font-size: 13px;
    flex-shrink: 0;
}
.ntcfg-keymap-action-select {
    width: auto !important;
    min-width: 0 !important;
    max-width: 96px;
    padding: 8px 6px !important;
    font-size: 12px !important;
    flex-shrink: 0;
}
.ntcfg-keymap-script-select,
.ntcfg-keymap-setting-select {
    width: auto !important;
    min-width: 0 !important;
    flex: 1;
    padding: 8px 6px !important;
    font-size: 12px !important;
}
.ntcfg-keymap-key {
    width: 110px !important;
    min-width: 110px;
    max-width: 110px;
    text-align: center;
    font-weight: 600;
    padding: 8px 4px !important;
}
.ntcfg-keymap-keycapture {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ntcfg-keymap-keycapture.is-capturing {
    border-color: #1c99f4;
    box-shadow: 0 0 0 2px rgba(28, 153, 244, 0.28);
    color: #ffffff;
}
.ntcfg-keymap-path {
    flex: 1;
    min-width: 0;
}
.ntcfg-keymap-remove {
    flex-shrink: 0;
}
.ntcfg-keymap-warning {
    margin: -2px 0 2px;
    color: #ffb3b3;
    font-size: 12px;
    line-height: 1.35;
}
.ntcfg-keymap-add-row {
    display: flex;
    gap: 6px;
    margin-top: 4px;
}
.ntcfg-keymap-add-row .ntcfg-action {
    padding: 6px 14px;
    font-size: 12px;
    white-space: nowrap;
}
.ntcfg-keymap-summary {
    color: #ffb3b3;
}
.ntcfg-theme-preview-label {
    color: #d8dcf2;
    font-size: 13px;
    margin-bottom: 4px;
}
.ntcfg-theme-preview-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 8px;
    margin-bottom: 12px;
}
.ntcfg-theme-preview {
    width: 100%;
    box-sizing: border-box;
    padding: 12px;
    margin-bottom: 0;
    border-radius: 5px;
    background: #e9eaeb;
    color: #2e3141;
}
.ntcfg-theme-preview span {
    font-family: "Roboto Mono", "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace;
    display: inline-block;
    padding: 2px;
    font-size: 18px;
    white-space: pre;
}
.ntcfg-theme-preview span.ntcfg-theme-char-active {
    background: #1c99f4;
    color: #fff;
}
.ntcfg-theme-preview span.ntcfg-theme-char-incorrect {
    background: #d62f3a;
    color: #fff;
}
.ntcfg-theme-preview span.ntcfg-theme-char-typed {
    color: #2e3141;
    opacity: 0.5;
}
.ntcfg-theme-preview span.ntcfg-theme-char-rainbow {
    animation: ntcfg-preview-rainbow-text 10s infinite alternate;
    -webkit-animation: ntcfg-preview-rainbow-text 10s infinite alternate;
    opacity: 1;
}
@keyframes ntcfg-preview-rainbow-text {
    0% { color: blue; }
    10% { color: #ff005d; }
    20% { color: #f0f; }
    30% { color: black; }
    40% { color: #7500ff; }
    50% { color: blue; }
    60% { color: #f0f; }
    70% { color: black; }
    80% { color: black; }
    90% { color: red; }
    100% { color: red; }
}
.ntcfg-sticky-preview {
    position: sticky;
    top: 0;
    z-index: 10;
    background: #2b2e3f;
    padding-bottom: 12px;
    margin-bottom: 4px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.ntcfg-pn-preview {
    width: 100%;
    box-sizing: border-box;
    padding: 12px;
    border-radius: 5px;
    background: #e9eaeb;
    color: #2e3141;
    margin-bottom: 0;
    overflow: hidden;
    word-wrap: break-word;
}
.ntcfg-pn-preview span {
    font-family: "Roboto Mono", "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace;
    display: inline-block;
    padding: 2px;
    font-size: 18px;
    white-space: pre;
}
.ntcfg-pn-preview-rainbow {
    animation: ntcfg-pn-preview-rainbow 3s linear infinite;
    -webkit-animation: ntcfg-pn-preview-rainbow 3s linear infinite;
}
@keyframes ntcfg-pn-preview-rainbow {
    0% { color: #FF0000; }
    14% { color: #FF8C00; }
    28% { color: #FFD700; }
    42% { color: #00CC00; }
    57% { color: #0066FF; }
    71% { color: #7B00FF; }
    85% { color: #FF00FF; }
    100% { color: #FF0000; }
}
.ntcfg-theme-settings {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.ntcfg-theme-setting {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    background: #252839;
    padding: 8px 10px;
}
.ntcfg-theme-setting-title {
    color: #d8dcf2;
    font-size: 13px;
}
.ntcfg-theme-setting-controls {
    display: flex;
    align-items: center;
    gap: 8px;
}
.ntcfg-theme-color-compact {
    display: flex;
    align-items: center;
    gap: 6px;
}
.ntcfg-theme-color-compact .ntcfg-color-picker {
    width: 38px;
    min-width: 38px;
    height: 30px;
}
.ntcfg-theme-color-compact .ntcfg-input {
    width: 96px;
    padding: 6px 8px;
    font-size: 12px;
}
.ntcfg-theme-input-compact {
    width: auto;
    padding: 6px 8px;
    font-size: 12px;
}
.ntcfg-theme-input-select {
    width: 180px;
}
.ntcfg-theme-input-number {
    width: 96px;
}
.ntcfg-theme-input-preset {
    width: 132px;
}
.ntcfg-preview-host {
    display: block;
}
.ntcfg-preview-surface {
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 10px;
    background:
        linear-gradient(180deg, rgba(88, 95, 126, 0.18), rgba(37, 40, 57, 0.9)),
        #252839;
    padding: 12px;
    margin-bottom: 12px;
}
.ntcfg-preview-grid {
    display: grid;
    gap: 10px;
}
.ntcfg-preview-grid--two {
    grid-template-columns: 1fr;
}
.ntcfg-preview-card {
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    background: rgba(9, 12, 20, 0.22);
    padding: 10px 12px;
}
.ntcfg-preview-card.is-off {
    opacity: 0.5;
}
.ntcfg-preview-card-title {
    color: #aab2d4;
    font-size: 11px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    margin-bottom: 6px;
}
.ntcfg-preview-card-copy {
    color: #eef2ff;
    font-size: 14px;
    line-height: 1.45;
}
.ntcfg-preview-card-subcopy {
    color: #aab2d4;
    font-size: 12px;
    line-height: 1.4;
    margin-top: 4px;
}
.ntcfg-preview-empty {
    color: #aab2d4;
    font-size: 13px;
    line-height: 1.4;
}
.ntcfg-preview-pill-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
}
.ntcfg-preview-pill {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: rgba(255, 255, 255, 0.04);
    color: #d8dcf2;
    font-size: 11px;
    line-height: 1;
    white-space: nowrap;
}
.ntcfg-preview-pill.is-off {
    opacity: 0.48;
}
.ntcfg-preview-pill--blue.is-active {
    border-color: rgba(28, 153, 244, 0.5);
    background: rgba(28, 153, 244, 0.18);
}
.ntcfg-preview-pill--gold.is-active {
    border-color: rgba(243, 168, 27, 0.45);
    background: rgba(243, 168, 27, 0.14);
}
.ntcfg-preview-pill--danger.is-active {
    border-color: rgba(214, 47, 58, 0.48);
    background: rgba(214, 47, 58, 0.16);
}
.ntcfg-preview-pill--success.is-active {
    border-color: rgba(74, 222, 128, 0.45);
    background: rgba(74, 222, 128, 0.14);
}
.ntcfg-preview-pill--warning.is-active {
    border-color: rgba(255, 194, 74, 0.42);
    background: rgba(255, 194, 74, 0.14);
}
.ntcfg-preview-matrix {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
    gap: 8px;
    margin-top: 10px;
}
.ntcfg-preview-tile {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-height: 64px;
    padding: 10px;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.04);
}
.ntcfg-preview-tile.is-off {
    opacity: 0.5;
}
.ntcfg-preview-tile--blue {
    background: rgba(28, 153, 244, 0.12);
}
.ntcfg-preview-tile-label {
    color: #aab2d4;
    font-size: 11px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.ntcfg-preview-tile-value {
    color: #ffffff;
    font-size: 15px;
    font-weight: 600;
}
.ntcfg-preview-name-text,
.ntcfg-preview-entry-meta {
    color: #eef2ff;
    font-size: 13px;
}
.ntcfg-badge-inline-preview {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}
.ntcfg-badge-name-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.04);
}
.ntcfg-badge-name-text {
    color: #eef2ff;
    font-size: 13px;
}
.ntcfg-badge-chip {
    display: inline-flex;
    align-items: center;
    padding: 3px 8px;
    border-radius: 999px;
    color: #fff;
    font-size: 11px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.ntcfg-badge-chip--racer {
    background: linear-gradient(90deg, rgba(28, 153, 244, 0.95), rgba(86, 180, 255, 0.95));
}
.ntcfg-badge-chip--team {
    background: linear-gradient(90deg, rgba(243, 168, 27, 0.95), rgba(255, 210, 92, 0.95));
    color: #3f3210;
}
.ntcfg-badge-banner {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 52px;
    border-radius: 8px;
    color: #ffffff;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.ntcfg-badge-banner--racer {
    background: linear-gradient(120deg, rgba(28, 153, 244, 0.75), rgba(94, 204, 255, 0.45));
}
.ntcfg-badge-banner--team {
    background: linear-gradient(120deg, rgba(243, 168, 27, 0.75), rgba(255, 221, 129, 0.48));
    color: #3f3210;
}
.ntcfg-bot-toggle-preview {
    appearance: none;
    border: none;
    border-radius: 8px;
    padding: 9px 14px;
    color: #fff;
    font-size: 13px;
    font-weight: 600;
    cursor: default;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.ntcfg-bot-toggle-preview.is-on {
    background: #1976d2;
}
.ntcfg-bot-toggle-preview.is-off {
    background: #666;
}
.ntcfg-flag-preview-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.ntcfg-flag-sample {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.04);
    color: #eef2ff;
    font-size: 13px;
}
.ntcfg-flag-indicator {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 28px;
    height: 22px;
    padding: 0 8px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.ntcfg-flag-indicator--danger {
    background: rgba(214, 47, 58, 0.22);
    color: #ffd4d8;
}
.ntcfg-flag-indicator--success {
    background: rgba(74, 222, 128, 0.18);
    color: #c7ffd8;
}
.ntcfg-flag-indicator--warning {
    background: rgba(243, 168, 27, 0.18);
    color: #ffe2a7;
}
.ntcfg-leader-preview-tabs {
    display: flex;
    gap: 10px;
    align-items: flex-end;
    margin-bottom: 0;
}
.ntcfg-leader-preview-tab {
    min-width: 150px;
    padding: 12px 16px;
    border-radius: 8px 8px 0 0;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-bottom: none;
    background: #3b3e50;
    color: #aeb4cb;
    font-size: 13px;
    font-weight: 600;
}
.ntcfg-leader-preview-tab.is-active {
    background: #4a4f66;
    color: #fff;
}
.ntcfg-leader-preview-panel {
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 0 10px 10px 10px;
    background: #4a4f66;
    padding: 12px;
}
.ntcfg-leader-preview-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 10px;
}
.ntcfg-leader-preview-btn,
.ntcfg-leader-preview-refresh {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 34px;
    padding: 0 12px;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.16);
    background: rgba(0, 0, 0, 0.14);
    color: #d8dcf2;
    font-size: 12px;
    white-space: nowrap;
}
.ntcfg-leader-preview-btn.is-active {
    background: #1c99f4;
    border-color: #1c99f4;
    color: #fff;
}
.ntcfg-leader-preview-refresh {
    margin-left: auto;
}
.ntcfg-leader-preview-entry {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #eef2ff;
}
.ntcfg-leader-preview-change {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 34px;
    height: 22px;
    padding: 0 8px;
    border-radius: 999px;
    background: rgba(74, 222, 128, 0.2);
    color: #c7ffd8;
    font-size: 11px;
    font-weight: 700;
}
.ntcfg-music-preview-card {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(10, 12, 20, 0.22);
}
.ntcfg-music-preview-cover {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 8px;
    background: linear-gradient(135deg, rgba(29, 185, 84, 0.8), rgba(28, 153, 244, 0.8));
    color: #fff;
    font-size: 16px;
    font-weight: 700;
    flex-shrink: 0;
}
.ntcfg-music-preview-cover--art {
    background: linear-gradient(135deg, rgba(28, 153, 244, 0.78), rgba(138, 103, 255, 0.78));
}
.ntcfg-music-preview-meta {
    min-width: 0;
}
.ntcfg-music-preview-title {
    color: #eef2ff;
    font-size: 13px;
    font-weight: 700;
    margin-bottom: 3px;
}
.ntcfg-music-preview-subtitle {
    color: #aab2d4;
    font-size: 12px;
    line-height: 1.35;
    word-break: break-word;
}
.ntcfg-music-control-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}
.ntcfg-music-control-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 30px;
    padding: 0 10px;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(0, 0, 0, 0.18);
    color: #eef2ff;
    font-size: 12px;
}
.ntcfg-music-control-pill.is-active {
    border-color: rgba(29, 185, 84, 0.55);
    background: rgba(29, 185, 84, 0.18);
}
@media (min-width: 900px) {
    .ntcfg-theme-preview-grid {
        grid-template-columns: 1fr 1fr;
    }
    .ntcfg-preview-grid--two {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}
.ntmods-toast {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 100002;
    padding: 12px 14px;
    border-radius: 12px;
    background: rgba(10, 16, 26, 0.96);
    color: #eff5ff;
    border: 1px solid rgba(255, 255, 255, 0.12);
    box-shadow: 0 16px 38px rgba(0, 0, 0, 0.38);
    opacity: 0;
    transform: translateY(8px);
    pointer-events: none;
    transition: opacity 0.16s ease, transform 0.16s ease;
    font-size: 12px;
    line-height: 1.35;
}
.ntmods-toast.is-visible {
    opacity: 1;
    transform: translateY(0);
}
.ntmods-eol-modal {
    position: fixed;
    inset: 0;
    z-index: 100003;
    display: grid;
    place-items: center;
    padding: 18px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.16s ease;
}
.ntmods-eol-modal.is-visible {
    opacity: 1;
    pointer-events: auto;
}
.ntmods-eol-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(3, 7, 14, 0.72);
    backdrop-filter: blur(5px);
}
.ntmods-eol-card {
    position: relative;
    width: min(560px, calc(100vw - 32px));
    padding: 26px;
    border-radius: 14px;
    border: 1px solid rgba(113, 168, 255, 0.28);
    background:
        url(/dist/site/images/backgrounds/bg-noise.png) top left repeat,
        linear-gradient(180deg, rgba(37, 43, 61, 0.98), rgba(23, 28, 43, 0.98));
    background-attachment: scroll, scroll;
    box-shadow:
        0 26px 70px rgba(0, 0, 0, 0.52),
        inset 0 1px 0 rgba(255, 255, 255, 0.08);
    color: #f4f7ff;
    transform: translateY(8px) scale(0.985);
    transition: transform 0.16s ease;
}
.ntmods-eol-modal.is-visible .ntmods-eol-card {
    transform: translateY(0) scale(1);
}
.ntmods-eol-x {
    appearance: none;
    position: absolute;
    top: 14px;
    right: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.06);
    color: rgba(235, 241, 255, 0.78);
    cursor: pointer;
}
.ntmods-eol-x:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
}
.ntmods-eol-logo-shell {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 68px;
    height: 68px;
    margin-bottom: 18px;
    border-radius: 16px;
    background: rgba(28, 153, 244, 0.14);
    border: 1px solid rgba(87, 191, 255, 0.28);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.ntmods-eol-logo {
    width: 48px;
    height: 48px;
    object-fit: contain;
}
.ntmods-eol-kicker {
    margin: 0 0 6px;
    color: #62d6ff;
    font: 700 12px/1.2 "Montserrat", sans-serif;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.ntmods-eol-copy h2 {
    margin: 0 0 10px;
    color: #fff;
    font: 800 28px/1.08 "Montserrat", sans-serif;
}
.ntmods-eol-copy p:not(.ntmods-eol-kicker) {
    margin: 0;
    color: #b9c6e6;
    font-size: 14px;
    line-height: 1.55;
}
.ntmods-eol-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 22px;
}
.ntmods-eol-primary,
.ntmods-eol-secondary {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 42px;
    padding: 10px 18px;
    border-radius: 8px;
    font: 700 13px/1.2 "Montserrat", sans-serif;
    text-decoration: none;
    cursor: pointer;
}
.ntmods-eol-primary {
    border: 1px solid rgba(105, 205, 255, 0.55);
    background: linear-gradient(135deg, #1c99f4 0%, #176fc9 100%);
    color: #fff;
    box-shadow: 0 12px 28px rgba(28, 153, 244, 0.24);
}
.ntmods-eol-primary:hover {
    background: linear-gradient(135deg, #35aaff 0%, #1c99f4 100%);
    color: #fff;
    text-decoration: none;
}
.ntmods-eol-secondary {
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(255, 255, 255, 0.07);
    color: rgba(238, 244, 255, 0.78);
}
.ntmods-eol-secondary:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
}
@media (max-width: 1100px) {
    .ntmods-cap {
        flex-direction: column;
    }
    .ntmods-cap-stats {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        min-width: 0;
    }
    .ntmods-module-card {
        min-height: 820px;
    }
}
@media (max-width: 980px) {
    .ntmods-layout {
        flex-direction: column;
    }
    .ntmods-sidebar {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        width: auto;
        max-width: none;
        gap: 8px;
    }
    .ntmods-stage-intro {
        flex-direction: column;
    }
    .ntmods-grid {
        grid-template-columns: 1fr;
    }
    .ntmods-nav-btn,
    .ntmods-nav-btn:first-child,
    .ntmods-nav-btn:last-child {
        border-radius: 8px;
    }
}
@media (max-width: 720px) {
    .ntmods-cap {
        padding: 18px;
    }
    .ntmods-cap-stats {
        grid-template-columns: 1fr;
    }
    .ntmods-module-head {
        flex-direction: column;
        align-items: stretch;
    }
    .ntmods-module-actions {
        justify-content: stretch;
        gap: 8px;
    }
    .ntmods-module-actions .ntmods-rescan-btn {
        flex: 1 1 160px;
    }
    .ntmods-sidebar {
        grid-template-columns: 1fr;
    }
    .ntmods-module-card {
        min-height: 700px;
    }
    .ntmods-top-tab {
        min-width: 0;
        padding: 12px 5px 11px;
    }
    .ntmods-top-tab .bucket-content {
        font-size: 11px;
    }
    .ntmods-top-tab .bucket {
        gap: 5px !important;
    }
    .ntmods-top-tab-glyph {
        width: 16px;
        height: 16px;
        flex-basis: 16px;
    }
    .ntmods-mod-icon--tab {
        width: 14px;
        height: 14px;
        flex-basis: 14px;
    }
}

/* Reorder mode */
.ntmods-cap-actions {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    flex-wrap: wrap;
}
.ntmods-header-btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.08);
    color: rgba(255, 255, 255, 0.8);
    padding: 7px 12px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.12s ease, border-color 0.12s ease;
}
.ntmods-header-btn:hover {
    background: rgba(255, 255, 255, 0.14);
    border-color: rgba(255, 255, 255, 0.3);
    color: #fff;
    text-decoration: none;
}
.ntmods-header-btn.is-active {
    background: rgba(28, 153, 244, 0.25);
    border-color: rgba(28, 153, 244, 0.5);
    color: #fff;
}
.ntmods-search {
    position: relative;
    display: inline-flex;
}
.ntmods-search-toggle {
    width: 36px;
    justify-content: center;
    padding-left: 10px;
    padding-right: 10px;
}
.ntmods-search-panel {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 30;
    width: min(460px, calc(100vw - 32px));
    padding: 10px;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.18);
    background: url(/dist/site/images/backgrounds/bg-noise.png) top left repeat, linear-gradient(180deg, rgba(37, 43, 61, 0.98), rgba(27, 31, 45, 0.98));
    background-attachment: scroll, scroll;
    box-shadow: 0 18px 42px rgba(0, 0, 0, 0.38);
}
.ntmods-search-input-shell {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    padding: 8px 10px;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(6, 10, 18, 0.28);
    color: rgba(210, 220, 245, 0.72);
}
.ntmods-search-input {
    appearance: none;
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    outline: 0;
    background: transparent;
    color: #fff;
    font-size: 13px;
}
.ntmods-search-input::placeholder {
    color: rgba(210, 220, 245, 0.52);
}
.ntmods-search-clear {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: rgba(210, 220, 245, 0.68);
    cursor: pointer;
}
.ntmods-search-clear:hover {
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
}
.ntmods-search-results {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: min(420px, 56vh);
    margin-top: 8px;
    overflow: auto;
}
.ntmods-search-result {
    appearance: none;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 3px;
    width: 100%;
    min-width: 0;
    padding: 10px 11px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.045);
    color: #e9efff;
    text-align: left;
    cursor: pointer;
}
.ntmods-search-result:hover,
.ntmods-search-result:focus-visible {
    outline: none;
    border-color: rgba(28, 153, 244, 0.48);
    background: rgba(28, 153, 244, 0.14);
}
.ntmods-search-result-path {
    color: rgba(166, 181, 207, 0.82);
    font-size: 11px;
    line-height: 1.2;
}
.ntmods-search-result-title {
    color: #fff;
    font-size: 13px;
    font-weight: 700;
    line-height: 1.25;
}
.ntmods-search-result-hint {
    color: rgba(210, 220, 245, 0.72);
    font-size: 12px;
    line-height: 1.35;
}
.ntmods-search-empty {
    padding: 12px 10px 4px;
    color: rgba(210, 220, 245, 0.66);
    font-size: 12px;
    text-align: center;
}
.ntmods-search-hit-focus {
    outline: 2px solid rgba(28, 153, 244, 0.9) !important;
    outline-offset: 3px;
    box-shadow: 0 0 0 6px rgba(28, 153, 244, 0.16), 0 12px 28px rgba(0, 0, 0, 0.22) !important;
}
@media (max-width: 720px) {
    .ntmods-cap-actions {
        width: 100%;
        justify-content: flex-start;
    }
    .ntmods-search-panel {
        left: 0;
        right: auto;
        width: min(420px, calc(100vw - 36px));
    }
}
.ntmods-self-update-btn {
    border-color: rgba(87, 191, 255, 0.45);
    background: rgba(28, 153, 244, 0.22);
    color: #fff;
}
.ntmods-self-update-btn:hover {
    border-color: rgba(87, 191, 255, 0.65);
    background: rgba(28, 153, 244, 0.34);
}
.ntmods-self-update-version {
    padding-left: 2px;
    color: rgba(255, 255, 255, 0.72);
    font-size: 11px;
}
.ntmods-top-tabs.is-reordering {
    outline: 2px dashed rgba(28, 153, 244, 0.3);
    outline-offset: 2px;
    border-radius: 10px;
}
.ntmods-top-tabs.is-reordering .ntmods-top-tab {
    cursor: grab;
}
.ntmods-top-tabs.is-reordering .ntmods-top-tab:active {
    cursor: grabbing;
}
.ntmods-tab-dragging {
    opacity: 0.4;
    outline: 2px dashed rgba(255,255,255,0.2);
}
.ntmods-tab-dragover {
    background: rgba(28, 153, 244, 0.15) !important;
    border-color: rgba(28, 153, 244, 0.4) !important;
}

/* Not Installed card */
.ntmods-not-installed {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 60px 30px;
    gap: 12px;
    min-height: 300px;
}
.ntmods-not-installed-icon {
    opacity: 0.3;
    margin-bottom: 8px;
}
.ntmods-not-installed-icon svg {
    width: 64px;
    height: 64px;
}
.ntmods-not-installed-title {
    font-family: "Montserrat", sans-serif;
    font-size: 22px;
    font-weight: 700;
    color: rgba(174, 180, 199, 0.8);
    margin: 0;
}
.ntmods-not-installed-copy {
    font-size: 14px;
    color: rgba(174, 180, 199, 0.6);
    max-width: 420px;
    line-height: 1.5;
    margin: 0;
}
.ntmods-not-installed-copy strong {
    color: rgba(220, 225, 240, 0.8);
}
.ntmods-install-link {
    display: inline-block;
    margin-top: 12px;
    padding: 10px 24px;
    font-family: "Montserrat", sans-serif;
    font-size: 13px;
    font-weight: 600;
    color: #fff;
    background: linear-gradient(135deg, #1c99f4 0%, #1a7ed4 100%);
    border-radius: 6px;
    text-decoration: none;
    transition: background 0.15s ease, transform 0.1s ease;
}
.ntmods-install-link:hover {
    background: linear-gradient(135deg, #2ba6ff 0%, #1c99f4 100%);
    transform: translateY(-1px);
}

/* Preview tab styling (not installed — blue outline) */
.ntmods-top-tab.is-preview {
    opacity: 0.6;
    border-color: rgba(100, 160, 255, 0.3);
}
.ntmods-top-tab.is-preview:hover {
    opacity: 0.8;
}


/* Inactive tab styling (installed before, but not active this page session) */
.ntmods-top-tab.is-outdated {
    opacity: 0.6;
    border-color: rgba(87, 191, 255, 0.24);
}
.ntmods-top-tab.is-outdated:hover {
    opacity: 0.8;
}
.ntmods-outdated-card .ntmods-not-installed-icon {
    opacity: 0.5;
}
.ntmods-outdated-title {
    color: rgba(98, 214, 255, 0.92) !important;
}
.ntmods-update-link {
    background: linear-gradient(135deg, #1c99f4 0%, #176fc9 100%) !important;
}
.ntmods-update-link:hover {
    background: linear-gradient(135deg, #35aaff 0%, #1c99f4 100%) !important;
}


/* Dismiss stale data button */
.ntmods-dismiss-stale-btn {
    appearance: none;
    margin-top: 8px;
    padding: 8px 18px;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.06);
    color: rgba(255, 255, 255, 0.6);
    font-size: 12px;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}
.ntmods-dismiss-stale-btn:hover {
    background: rgba(214, 47, 58, 0.2);
    border-color: rgba(214, 47, 58, 0.4);
    color: #ff9999;
}

/* Update dot on tab label */
.ntmods-update-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: #ff6b2b;
    margin-left: 5px;
    vertical-align: middle;
    box-shadow: 0 0 6px rgba(255, 107, 43, 0.5);
}

/* Update banner inside module card */
.ntmods-update-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 12px 20px;
    background: linear-gradient(135deg, rgba(255, 107, 43, 0.14), rgba(255, 176, 32, 0.10));
    border-bottom: 1px solid rgba(255, 146, 43, 0.2);
    font-size: 13px;
    color: #ffe0b6;
}
.ntmods-update-banner-text {
    min-width: 0;
    line-height: 1.45;
}
.ntmods-update-banner-text strong {
    color: #fff;
}
.ntmods-update-banner-link {
    flex: 0 0 auto;
    padding: 7px 14px;
    border-radius: 6px;
    background: rgba(255, 107, 43, 0.25);
    border: 1px solid rgba(255, 146, 43, 0.35);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.12s ease;
}
.ntmods-update-banner-link:hover {
    background: rgba(255, 107, 43, 0.4);
}
.ntmods-version-arrow {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 6px;
    font-family: "Montserrat", sans-serif;
    font-weight: 600;
}
.ntmods-version-current {
    color: rgba(255, 255, 255, 0.6);
}
.ntmods-version-arrow svg {
    color: rgba(255, 255, 255, 0.4);
    flex-shrink: 0;
}
.ntmods-version-latest {
    color: #fff;
}
        `;
        if (shouldAppend) {
            (document.head || document.documentElement).appendChild(style);
        }
    }

    function setActiveDropdownItem() {
        document.querySelectorAll(`.${DROPDOWN_ITEM_CLASS}`).forEach((item) => {
            item.classList.toggle('is-current', isModMenuRoute());
        });
    }

    function setPageTitle() {
        if (isModMenuRoute()) {
            document.title = 'Mods | Nitro Type';
        }
    }

    function ensureEntryStyles() {
        if (document.getElementById('ntmods-entry-style')) return;
        const style = document.createElement('style');
        style.id = 'ntmods-entry-style';
        style.textContent = `
.ntmods-dropdown-item.is-current .dropdown-link {
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
}
.ntmods-dropdown-item .dropdown-link .icon {
    opacity: 0.92;
}
.ntmods-dropdown-item .dropdown-link .ntmods-inline-icon {
    opacity: 1;
    color: #9fb2c6 !important;
    fill: currentColor !important;
}
.ntmods-dropdown-item .dropdown-link:hover .ntmods-inline-icon,
.ntmods-dropdown-item.is-current .dropdown-link .ntmods-inline-icon {
    color: #c8d5e2 !important;
    fill: currentColor !important;
}
.ntmods-inline-icon {
    width: 16px;
    height: 16px;
    min-width: 16px;
    min-height: 16px;
    display: inline-block;
    vertical-align: -3px;
    flex: 0 0 16px;
    shape-rendering: geometricPrecision;
}
.ntmods-dropdown-item .dropdown-link {
    position: relative;
}
.ntmods-dropdown-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-left: 8px;
    padding: 2px 8px;
    border-radius: 10px;
    border: 1px solid rgba(255, 107, 43, 0.55);
    background: rgba(255, 107, 43, 0.1);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.3px;
    color: #ff9e5e;
    line-height: 1.4;
    vertical-align: middle;
    white-space: nowrap;
}
.ntmods-dropdown-pill-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #ff6b2b;
    box-shadow: 0 0 5px rgba(255, 107, 43, 0.5);
    flex-shrink: 0;
}
        `;
        (document.head || document.documentElement).appendChild(style);
    }

    function getModsMenuIconMarkup() {
        return `
            <svg class="ntmods-inline-icon mrxs" viewBox="0 0 177.5 178" aria-hidden="true">
                <g
                    transform="translate(-12,189) scale(0.1,-0.1)"
                    fill="currentColor"
                    stroke="currentColor"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                >
                    <path
                        stroke-width="102"
                        d="M867 1870 c-18 -15 -28 -37 -38 -86 -8 -42 -19 -68 -29 -71 -8 -3
                        -49 -20 -91 -37 l-76 -32 -34 24 c-59 41 -95 53 -126 41 -15 -5 -61 -42 -100
                        -82 -94 -93 -100 -116 -48 -194 35 -53 37 -59 25 -88 -7 -16 -22 -57 -35 -90
                        l-23 -60 -65 -15 c-103 -24 -107 -30 -107 -177 0 -155 4 -160 133 -187 97 -21
                        111 -17 105 30 -3 27 -8 30 -68 44 -36 8 -71 19 -77 23 -9 5 -13 35 -13 87 0
                        52 4 82 13 87 6 4 41 15 77 23 l64 15 26 75 c14 41 35 92 48 113 30 51 28 68
                        -13 133 -19 30 -35 58 -35 62 0 4 27 34 59 66 l60 59 38 -25 c90 -59 96 -60
                        150 -31 26 14 82 38 123 53 l75 26 16 75 15 74 86 3 c99 3 97 5 117 -90 14
                        -64 19 -70 81 -87 25 -7 75 -29 113 -47 75 -38 72 -38 156 19 l43 30 64 -64
                        64 -64 -42 -63 -41 -63 41 -87 c23 -48 44 -100 48 -117 10 -45 20 -53 82 -66
                        92 -18 92 -18 92 -109 0 -91 0 -91 -92 -109 -65 -14 -72 -20 -88 -81 -6 -25
                        -27 -74 -46 -110 -19 -35 -34 -69 -34 -75 0 -5 18 -37 40 -70 l40 -60 -62 -62
                        c-34 -34 -64 -61 -67 -59 -3 2 -34 20 -69 41 l-64 39 -56 -31 c-31 -17 -87
                        -41 -123 -53 l-66 -21 -19 -77 -19 -77 -78 -3 c-46 -2 -84 2 -92 8 -7 6 -18
                        38 -25 73 -13 69 -24 87 -50 87 -35 0 -44 -26 -32 -93 29 -155 32 -157 196
                        -157 140 0 150 6 172 103 13 57 20 70 42 79 15 6 55 23 89 39 l61 27 54 -36
                        c84 -57 102 -52 203 48 102 101 107 123 51 206 l-35 52 36 83 c19 46 42 89 50
                        96 8 6 37 14 64 18 27 4 59 15 72 25 21 17 22 26 22 150 0 154 -3 159 -105
                        179 -51 10 -63 16 -72 39 -6 15 -24 55 -39 90 l-29 63 35 52 c55 81 50 111
                        -30 196 -72 75 -123 106 -158 95 -12 -4 -44 -22 -70 -39 l-49 -33 -61 27 c-34
                        16 -74 33 -89 39 -22 9 -29 22 -42 79 -22 98 -31 103 -176 103 -102 0 -121 -3
                        -143 -20z"
                    />
                </g>
                <svg x="0" y="0" width="156" height="156" viewBox="0 0 256 256">
                    <g transform="translate(20,276) scale(0.1,-0.1)" stroke-linejoin="round">
                        <path fill="currentColor" stroke="none" d="M1162 1810 c-45 -11 -105 -31 -133 -46 -78 -43 -154 -125 -197 -212
                            l-37 -77 0 -175 0 -176 -230 -209 c-376 -342 -471 -439 -495 -502 -69 -186 63
                            -377 261 -376 118 0 170 41 469 368 258 283 366 389 387 380 32 -13 157 -24
                            208 -19 61 6 147 31 197 55 71 36 162 133 204 216 l39 78 0 135 c-1 119 -4
                            143 -25 200 -30 78 -61 100 -108 76 -16 -8 -82 -66 -147 -128 -128 -123 -160
                            -148 -189 -148 -27 0 -85 47 -97 78 -13 33 1 52 146 206 134 143 150 167 135
                            195 -16 31 -80 60 -178 81 -104 23 -105 23 -210 0z"/>
                    </g>
                </svg>
            </svg>
        `;
    }

    function insertModsDropdownItem() {
        ensureEntryStyles();
        const dropdownLists = document.querySelectorAll('.dropdown--account .dropdown-items');
        if (!dropdownLists.length) return;
        const hasUpdate = checkAnyModuleHasUpdate();
        const pillHtml = hasUpdate
            ? '<span class="ntmods-dropdown-pill"><span class="ntmods-dropdown-pill-dot"></span>Update Available</span>'
            : '';

        dropdownLists.forEach((list) => {
            const existing = list.querySelector(`.${DROPDOWN_ITEM_CLASS}`);

            // If item exists, sync the Bot Hunter update indicator and move on.
            if (existing) {
                const link = existing.querySelector('.dropdown-link');
                if (link) {
                    const pill = link.querySelector('.ntmods-dropdown-pill');
                    if (hasUpdate && !pill) {
                        link.insertAdjacentHTML('beforeend', '<span class="ntmods-dropdown-pill"><span class="ntmods-dropdown-pill-dot"></span>Update Available</span>');
                    } else if (!hasUpdate && pill) {
                        pill.remove();
                    }
                }
                return;
            }

            const li = document.createElement('li');
            li.className = `list-item dropdown-item ${DROPDOWN_ITEM_CLASS}`;
            li.innerHTML = `
                <a class="dropdown-link" href="${MOD_MENU_PATH}">
                    ${getModsMenuIconMarkup()}
                    Mods
                    ${pillHtml}
                </a>
            `;

            const statsItem = Array.from(list.children).find((item) => {
                const link = item.querySelector('a[href="/stats"]');
                return !!link || item.textContent.trim().includes('My Stats');
            });

            if (statsItem) {
                statsItem.after(li);
            } else {
                list.appendChild(li);
            }
        });

        setActiveDropdownItem();
    }

    // Sync the update pill on all existing "Mods" dropdown items.
    function updateDropdownDot() {
        const hasUpdate = checkAnyModuleHasUpdate();
        document.querySelectorAll(`.${DROPDOWN_ITEM_CLASS}`).forEach((li) => {
            const link = li.querySelector('.dropdown-link');
            if (!link) return;
            const existing = link.querySelector('.ntmods-dropdown-pill');
            if (hasUpdate && !existing) {
                link.insertAdjacentHTML('beforeend', '<span class="ntmods-dropdown-pill"><span class="ntmods-dropdown-pill-dot"></span>Update Available</span>');
            } else if (!hasUpdate && existing) {
                existing.remove();
            }
        });
    }

    // Check if any registered module has an available update.
    // Always reads directly from localStorage + version cache — works on every page,
    // no dependency on _cachedModules or the mods page being loaded.
    function checkAnyModuleHasUpdate() {
        return getBotHunterUpdateState().hasUpdate;
    }

    function updateRouteStatus() {
        const main = document.querySelector('main.structure-content');
        if (isModMenuRoute()) {
            document.documentElement.classList.add('is-mods-route');
        } else {
            document.documentElement.classList.remove('is-mods-route');
            if (main) main.classList.remove('custom-loaded');
        }
    }

    function getModuleById(modules, moduleId) {
        return modules.find((module) => module.id === moduleId) || modules[0] || null;
    }

    // Synchronous — reads the Bot Hunter cache only. All sunset modules remain excluded.
    function applyVersionUpdates(modules) {
        const botHunter = modules.find((module) => module.id === BOT_HUNTER_MODULE_ID);
        if (botHunter) {
            delete botHunter._hasUpdate;
            delete botHunter._latestVersion;
            const state = getBotHunterUpdateState(botHunter);
            if (state.hasUpdate) {
                botHunter._hasUpdate = true;
                botHunter._latestVersion = state.latestVersion;
            }
        }
        updateDropdownDot();
    }

    function dismissStaleModule(moduleId) {
        // Remove the stale manifest and alive key
        try {
            localStorage.removeItem(MANIFEST_PREFIX + moduleId);
            localStorage.removeItem(ALIVE_PREFIX + moduleId);
        } catch { /* ignore */ }
        // Remove all ntcfg values for this module
        try {
            const prefix = `ntcfg:${moduleId}:`;
            Object.keys(localStorage).forEach((key) => {
                if (key.startsWith(prefix)) localStorage.removeItem(key);
            });
        } catch { /* ignore */ }
        showToast(`Cleared data for ${moduleId}.`);
        invalidateModuleCache();
        renderPage('', '', true);
    }

    let _renderDebounceTimer = null;
    let _versionCheckDone = false;
    let _cachedModules = null;
    let _suppressObserver = false;

    // Invalidate the module cache so the next render rebuilds from localStorage.
    function invalidateModuleCache() {
        _cachedModules = null;
        invalidateFieldCache();
        clearSectionDomCache();
        clearCardDomCache();
    }

    function renderPage(requestedModuleId = '', requestedSectionId = '', forceRebuild) {
        if (renderInProgress || !isModMenuRoute()) return;
        const mainContent = document.querySelector('main.structure-content');
        if (!mainContent) return;

        // Cancel any pending debounced render
        if (_renderDebounceTimer) {
            clearTimeout(_renderDebounceTimer);
            _renderDebounceTimer = null;
        }

        renderInProgress = true;
        _suppressObserver = true;
        try {
            ensureStyles();

            // Reuse cached module list for tab switches; rebuild on first load,
            // rescan, or when statuses change (forceRebuild).
            if (forceRebuild || !_cachedModules) {
                _cachedModules = buildVisibleModules();
            }
            const modules = _cachedModules;
            applyVersionUpdates(modules);
            const selection = requestedModuleId
                ? {
                    activeModuleId: requestedModuleId,
                    activeSectionId: requestedSectionId
                }
                : getInitialSelection(modules);

            const activeModule = getModuleById(modules, selection.activeModuleId);
            const activeModuleSections = getRenderableSections(activeModule);
            const activeSection = activeModule
                ? activeModuleSections.find((section) => section.id === selection.activeSectionId) || activeModuleSections[0]
                : null;

            const nextUiState = {
                moduleId: activeModule ? activeModule.id : '',
                sectionId: activeSection ? activeSection.id : ''
            };

            writeUiState(nextUiState);

            // Batch-read all field values for the active module in one pass
            if (activeModule) preloadFieldValues(activeModule.id, activeModule.source);

            mainContent.innerHTML = buildPageHtml(modules, nextUiState.moduleId, nextUiState.sectionId);
            mainContent.classList.add('custom-loaded');
            requestAnimationFrame(() => {
                mainContent.classList.add('custom-loaded');
            });

            bindPageEvents(modules);
            setActiveDropdownItem();
            setPageTitle();
            updateDropdownDot();
            queueScriptEolNotice();

            _versionCheckDone = true;
        } catch (err) {
            console.error('[renderPage] ERROR', err);
            mainContent.innerHTML = buildRecoveryHtml(err);
            mainContent.classList.add('custom-loaded');
            bindRecoveryEvents();
        } finally {
            renderInProgress = false;
            _suppressObserver = false;
        }
    }

    // ── Card-level DOM cache ──
    // Caches the fully-rendered .ntmods-module-card content (header + section + all events)
    // so tab switches are a single DOM node swap with zero rebuilding.
    const _cardDomCache = new Map();
    let _activeCardCacheKey = '';

    function clearCardDomCache() {
        _cardDomCache.clear();
        _activeCardCacheKey = '';
    }

    // Save the current card's live children back into their cache entry
    // so they can be re-attached later.
    function stashCurrentCard(card) {
        if (!_activeCardCacheKey || !card.firstChild) return;
        let container = _cardDomCache.get(_activeCardCacheKey);
        if (!container) {
            container = document.createElement('div');
            _cardDomCache.set(_activeCardCacheKey, container);
        }
        while (card.firstChild) container.appendChild(card.firstChild);
    }

    // Fast tab switch — swaps cached card DOM nodes. Zero HTML parsing, zero event re-binding.
    let _switchTabSeq = 0;
    function switchTab(moduleId, sectionId) {
        if (_renderDebounceTimer) clearTimeout(_renderDebounceTimer);
        const seq = ++_switchTabSeq;

        // Immediately update tab highlight + show loading state (before debounce)
        const tabBar = document.querySelector('.ntmods-top-tabs');
        if (tabBar) {
            tabBar.querySelectorAll('.ntmods-top-tab').forEach((tab) => {
                tab.classList.toggle('is-active', tab.getAttribute('data-module-id') === moduleId);
            });
        }
        const card = document.querySelector('.ntmods-module-card');
        if (card) {
            // Show loading placeholder if the card swap takes time
            const loadingTimer = setTimeout(() => {
                if (_switchTabSeq === seq) {
                    card.innerHTML = '<div style="padding:40px;text-align:center;color:rgba(255,255,255,0.4);font-size:14px;">Loading\u2026</div>';
                }
            }, 80);
            card._loadingTimer = loadingTimer;
        }

        _renderDebounceTimer = setTimeout(() => {
            _renderDebounceTimer = null;
            if (card && card._loadingTimer) { clearTimeout(card._loadingTimer); card._loadingTimer = null; }

            try {
                const modules = _cachedModules;
                if (!card || !modules) {
                    renderPage(moduleId, sectionId);
                    return;
                }

                const activeModule = getModuleById(modules, moduleId);
                if (!activeModule) {
                    renderPage(moduleId, sectionId);
                    return;
                }
                const activeModuleSections = getRenderableSections(activeModule);
                const activeSection = activeModuleSections.find((s) => s.id === sectionId) || activeModuleSections[0] || null;
                const cardCacheKey = `${moduleId}::${sectionId || (activeSection ? activeSection.id : '')}`;

                // Don't re-render if we're already on this tab
                if (cardCacheKey === _activeCardCacheKey) {
                    focusPendingSearchResult(activeModule.id, activeSection ? activeSection.id : '');
                    return;
                }

                // Update UI state
                writeUiState({
                    moduleId: activeModule.id,
                    sectionId: activeSection ? activeSection.id : ''
                });

                // Tab highlight already applied immediately (before debounce)

                // Suppress MutationObserver during DOM swap to prevent cascading handlers
                _suppressObserver = true;
                try {
                    // Stash current card's DOM nodes back into cache before swapping
                    stashCurrentCard(card);

                    // Check card DOM cache — if we have this module's card, just swap the node
                    const cachedCard = _cardDomCache.get(cardCacheKey);
                    if (cachedCard && cachedCard.firstChild) {
                        while (cachedCard.firstChild) card.appendChild(cachedCard.firstChild);
                        _activeCardCacheKey = cardCacheKey;
                        setPageTitle();
                        focusPendingSearchResult(activeModule.id, activeSection ? activeSection.id : '');
                        return;
                    }

                    // No cache — build from scratch
                    _activeCardCacheKey = cardCacheKey;
                    preloadFieldValues(activeModule.id, activeModule.source);
                    card.innerHTML = buildModuleCardInner(activeModule, activeSection);
                    bindContentEvents(modules, activeModule, activeSection);
                    setPageTitle();
                } finally {
                    _suppressObserver = false;
                }
            } catch (err) {
                console.error('[switchTab] ERROR', err);
            }
        }, 30);
    }

    function bindSearchEvents(modules, { exitReorderMode = null } = {}) {
        if (typeof cleanupActiveSearchOutsideClick === 'function') {
            cleanupActiveSearchOutsideClick();
            cleanupActiveSearchOutsideClick = null;
        }
        const root = document.querySelector('[data-ntmods-search]');
        if (!root) return;
        const toggle = root.querySelector('[data-action-key="toggle-search"]');
        const panel = root.querySelector('[data-ntmods-search-panel]');
        const input = root.querySelector('[data-ntmods-search-input]');
        const clear = root.querySelector('[data-ntmods-search-clear]');
        const results = root.querySelector('[data-ntmods-search-results]');
        if (!toggle || !panel || !input || !clear || !results) return;

        const updateResults = () => {
            results.innerHTML = buildSearchResultsHtml(modules, input.value);
            clear.hidden = !String(input.value || '').trim();
        };

        const isEventInsideSearch = (event) => {
            const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
            return path.includes(root) || root.contains(event.target);
        };

        const removeOutsideSearchListener = () => {
            if (typeof cleanupActiveSearchOutsideClick === 'function') {
                cleanupActiveSearchOutsideClick();
                cleanupActiveSearchOutsideClick = null;
            }
        };

        const installOutsideSearchListener = () => {
            removeOutsideSearchListener();
            const handleOutsidePointerDown = (event) => {
                if (!root.classList.contains('is-open')) return;
                if (isEventInsideSearch(event)) return;
                setOpen(false);
            };
            document.addEventListener('pointerdown', handleOutsidePointerDown, true);
            cleanupActiveSearchOutsideClick = () => {
                document.removeEventListener('pointerdown', handleOutsidePointerDown, true);
            };
        };

        const setOpen = (open) => {
            root.classList.toggle('is-open', open);
            panel.hidden = !open;
            toggle.classList.toggle('is-active', open);
            toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
            if (open) {
                if (typeof exitReorderMode === 'function') exitReorderMode();
                updateResults();
                requestAnimationFrame(() => input.focus());
                installOutsideSearchListener();
            } else {
                removeOutsideSearchListener();
            }
        };

        toggle.addEventListener('click', (event) => {
            event.stopPropagation();
            setOpen(!root.classList.contains('is-open'));
        });

        input.addEventListener('input', updateResults);
        input.addEventListener('keydown', (event) => {
            if (event.key === 'Escape') {
                event.preventDefault();
                setOpen(false);
                toggle.focus();
                return;
            }
            if (event.key === 'Enter') {
                const firstResult = results.querySelector('.ntmods-search-result');
                if (firstResult) {
                    event.preventDefault();
                    firstResult.click();
                }
            }
        });

        clear.addEventListener('click', (event) => {
            event.preventDefault();
            input.value = '';
            updateResults();
            input.focus();
        });

        results.addEventListener('click', (event) => {
            const resultButton = event.target.closest('.ntmods-search-result');
            if (!resultButton) return;
            const moduleId = resultButton.getAttribute('data-search-module-id') || '';
            const sectionId = resultButton.getAttribute('data-search-section-id') || '';
            const fieldKey = resultButton.getAttribute('data-search-field-key') || '';
            if (!moduleId || !sectionId || !fieldKey) return;

            pendingSearchFocus = { moduleId, sectionId, fieldKey };
            setOpen(false);
            switchTab(moduleId, sectionId);
        });
    }

    function bindPageEvents(modules) {
        document.querySelectorAll('[data-module-id]').forEach((button) => {
            button.addEventListener('click', () => {
                // Don't switch tabs while reorder mode is active
                const tabBar = button.closest('.ntmods-top-tabs');
                if (tabBar && tabBar.classList.contains('is-reordering')) return;
                const moduleId = button.getAttribute('data-module-id') || '';
                const module = getModuleById(modules, moduleId);
                const moduleSections = getRenderableSections(module);
                const sectionId = moduleSections[0] ? moduleSections[0].id : '';
                switchTab(moduleId, sectionId);
            });
        });

        // ── Reorder mode toggle ──
        const tabBar = document.querySelector('.ntmods-top-tabs');
        const reorderBtn = document.querySelector('[data-action-key="toggle-reorder"]');

        function enterReorderMode() {
            if (!tabBar) return;
            tabBar.classList.add('is-reordering');
            if (reorderBtn) {
                reorderBtn.classList.add('is-active');
                reorderBtn.innerHTML = `
                    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>
                    Done
                `;
            }
            let dragSrc = null;

            tabBar.querySelectorAll('[data-module-id]').forEach((tab) => {
                tab.draggable = true;

                tab._dragstart = (e) => {
                    dragSrc = tab;
                    tab.classList.add('ntmods-tab-dragging');
                    e.dataTransfer.effectAllowed = 'move';
                    e.dataTransfer.setData('text/plain', tab.getAttribute('data-module-id'));
                };
                tab._dragend = () => {
                    if (dragSrc) dragSrc.classList.remove('ntmods-tab-dragging');
                    tabBar.querySelectorAll('.ntmods-tab-dragover').forEach((el) => el.classList.remove('ntmods-tab-dragover'));
                    dragSrc = null;
                };
                tab._dragover = (e) => {
                    e.preventDefault();
                    e.dataTransfer.dropEffect = 'move';
                    if (tab === dragSrc) return;
                    tab.classList.add('ntmods-tab-dragover');
                };
                tab._dragleave = () => {
                    tab.classList.remove('ntmods-tab-dragover');
                };
                tab._drop = (e) => {
                    e.preventDefault();
                    tab.classList.remove('ntmods-tab-dragover');
                    if (!dragSrc || dragSrc === tab) return;
                    const allTabs = [...tabBar.querySelectorAll('[data-module-id]')];
                    const fromIdx = allTabs.indexOf(dragSrc);
                    const toIdx = allTabs.indexOf(tab);
                    if (fromIdx < toIdx) {
                        tab.parentNode.insertBefore(dragSrc, tab.nextSibling);
                    } else {
                        tab.parentNode.insertBefore(dragSrc, tab);
                    }
                    const newOrder = [...tabBar.querySelectorAll('[data-module-id]')].map(
                        (t) => t.getAttribute('data-module-id')
                    );
                    writePersistentJson(TAB_ORDER_KEY, newOrder);
                    showToast('Tab order saved.');
                };

                tab.addEventListener('dragstart', tab._dragstart);
                tab.addEventListener('dragend', tab._dragend);
                tab.addEventListener('dragover', tab._dragover);
                tab.addEventListener('dragleave', tab._dragleave);
                tab.addEventListener('drop', tab._drop);
            });
        }

        function exitReorderMode() {
            if (!tabBar) return;
            tabBar.classList.remove('is-reordering');
            if (reorderBtn) {
                reorderBtn.classList.remove('is-active');
                reorderBtn.innerHTML = `
                    <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 13 12 18 17 13"></polyline><polyline points="7 6 12 11 17 6"></polyline></svg>
                    Reorder Tabs
                `;
            }
            tabBar.querySelectorAll('[data-module-id]').forEach((tab) => {
                tab.draggable = false;
                if (tab._dragstart) tab.removeEventListener('dragstart', tab._dragstart);
                if (tab._dragend) tab.removeEventListener('dragend', tab._dragend);
                if (tab._dragover) tab.removeEventListener('dragover', tab._dragover);
                if (tab._dragleave) tab.removeEventListener('dragleave', tab._dragleave);
                if (tab._drop) tab.removeEventListener('drop', tab._drop);
            });
        }

        if (reorderBtn) {
            reorderBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                if (tabBar && tabBar.classList.contains('is-reordering')) {
                    exitReorderMode();
                } else {
                    enterReorderMode();
                }
            });
        }

        bindSearchEvents(modules, { exitReorderMode });

        const uiState = readUiState();
        const activeModule = getModuleById(modules, uiState.moduleId);
        const activeModuleSections = getRenderableSections(activeModule);
        const activeSection = activeModule
            ? activeModuleSections.find((section) => section.id === uiState.sectionId) || activeModuleSections[0]
            : null;

        bindContentEvents(modules, activeModule, activeSection);
    }

    // Bind events inside .ntmods-module-card only (sections, rescan, dismiss, settings fields).
    // Called by both full renderPage and fast switchTab.
    function bindContentEvents(modules, activeModule, activeSection) {
        const card = document.querySelector('.ntmods-module-card');
        if (!card) return;

        card.querySelectorAll('[data-section-id]').forEach((button) => {
            button.addEventListener('click', () => {
                const uiState = readUiState();
                switchTab(uiState.moduleId || '', button.getAttribute('data-section-id') || '');
            });
        });

        card.querySelectorAll('[data-action-key]').forEach((button) => {
            button.addEventListener('click', () => {
                const actionKey = button.getAttribute('data-action-key') || '';
                if (actionKey === 'check-bot-hunter-updates') {
                    if (_rescanInProgress) return;
                    _rescanInProgress = true;
                    button.disabled = true;
                    showToast('Checking Bot Hunter for updates...');
                    (async () => {
                        try {
                            const result = await refreshBotHunterVersion({
                                forceRefresh: true,
                                stampGlobalCheck: true
                            });
                            _versionCheckDone = true;
                            invalidateModuleCache();
                            renderPage('', '', true);
                            if (result.error === 'not-installed') {
                                showToast('Bot Hunter did not report an installed version. Reload Nitro Type and try again.');
                            } else if (result.error) {
                                showToast('Could not reach GreasyFork. Please try again shortly.');
                            } else if (result.hasUpdate) {
                                showToast(`Bot Hunter v${result.latestVersion} is available.`);
                            } else {
                                showToast(`Bot Hunter is up to date at v${result.installedVersion}.`);
                            }
                        } catch {
                            showToast('The Bot Hunter update check could not be completed.');
                        } finally {
                            _rescanInProgress = false;
                            if (button.isConnected) button.disabled = false;
                        }
                    })();
                    return;
                }
                if (actionKey === 'rescan') {
                    window.open(MOD_SUITE_EXTENSION_URL, '_blank', 'noopener,noreferrer');
                    return;
                }
                if (actionKey === 'clear-settings') {
                    void showNtcfgConfirmationDialog({
                        title: 'Clear Mod Suite data?',
                        message: 'Clear settings and caches for the installed public Nitro Type scripts?',
                        note: 'This keeps the scripts installed, but resets their preferences and cached data.',
                        confirmLabel: 'Clear Data',
                        cancelLabel: 'Keep Data',
                        tone: 'danger'
                    }).then((confirmed) => {
                        if (confirmed) beginGlobalReset(modules);
                    });
                    return;
                }
                // Dispatch action event for external scripts
                const moduleId = activeModule?.id || '';
                document.dispatchEvent(new CustomEvent('ntcfg:action', {
                    detail: { script: moduleId, key: actionKey }
                }));
            });
        });

        // Dismiss stale module data
        card.querySelectorAll('[data-dismiss-stale]').forEach((btn) => {
            btn.addEventListener('click', () => {
                const moduleId = btn.getAttribute('data-dismiss-stale') || '';
                if (moduleId) dismissStaleModule(moduleId);
            });
        });

        if (activeModule?.source === 'manifest' && activeSection) {
            mountGenericSection(activeModule, activeSection);
            focusPendingSearchResult(activeModule.id, activeSection.id);
            return;
        }

        const fieldMap = new Map((activeSection?.items || []).filter((item) => item.key).map((item) => [item.key, item]));

        card.querySelectorAll('[data-field-key]').forEach((input) => {
            const fieldKey = input.getAttribute('data-field-key') || '';
            const item = fieldMap.get(fieldKey);
            if (!activeModule || !item) return;

            const inputType = input.getAttribute('data-field-type') || item.type;
            const eventName = item.type === 'toggle' || inputType === 'select' || inputType === 'multi-select' ? 'change' : 'input';

            input.addEventListener(eventName, () => {
                if (inputType === 'color-text') {
                    const colorValue = String(input.value || '').trim();
                    const colorPicker = card.querySelector(`[data-field-key="${CSS.escape(fieldKey)}"][data-field-type="color"]`);
                    if (/^#[0-9A-Fa-f]{6}$/.test(colorValue)) {
                        setFieldValue(activeModule, item, colorValue.toUpperCase());
                        if (colorPicker) colorPicker.value = colorValue.toUpperCase();
                    }
                    return;
                }

                const nextValue = coerceInputValue(input, item);
                setFieldValue(activeModule, item, nextValue);

                if (inputType === 'color') {
                    const colorText = card.querySelector(`[data-field-key="${CSS.escape(fieldKey)}"][data-field-type="color-text"]`);
                    if (colorText) colorText.value = String(nextValue).toUpperCase();
                }
            });

            if (inputType === 'text' || inputType === 'number' || inputType === 'select') {
                input.addEventListener('change', () => {
                    const nextValue = coerceInputValue(input, item);
                    setFieldValue(activeModule, item, nextValue);
                });
            }
        });

        if (activeModule && activeSection) {
            focusPendingSearchResult(activeModule.id, activeSection.id);
        }

    }

    function ensureModMenuPageRendered() {
        if (!isModMenuRoute()) return;

        const main = document.querySelector('main.structure-content');
        if (!main) {
            ensureMainWaitObserver();
            return;
        }

        const app = document.getElementById('ntmods-app');
        if (app) {
            stopMainWaitObserver();
            if (!main.classList.contains('custom-loaded')) {
                main.classList.add('custom-loaded');
            }
            setActiveDropdownItem();
            setPageTitle();
            return;
        }

        stopMainWaitObserver();
        renderPage();
    }

    function handlePage() {
        insertModsDropdownItem();
        if (!isModMenuRoute()) {
            scriptEolNoticeClosedThisVisit = false;
            clearModMenuPageSyncTimer();
            pendingModMenuRouteSync = false;
            stopModMenuRouteGuard();
            stopMainWaitObserver();
            setActiveDropdownItem();
            updateDropdownDot();
            return;
        }

        startModMenuRouteGuard();
        ensureModMenuPageRendered();
    }

    function clearModMenuPageSyncTimer() {
        if (!modMenuPageSyncTimer) return;
        clearTimeout(modMenuPageSyncTimer);
        modMenuPageSyncTimer = null;
    }

    function scheduleModMenuPageSync({ syncRoute = false, delay = 0 } = {}) {
        pendingModMenuRouteSync = pendingModMenuRouteSync || !!syncRoute;
        clearModMenuPageSyncTimer();
        modMenuPageSyncTimer = setTimeout(() => {
            modMenuPageSyncTimer = null;
            const shouldSyncRoute = pendingModMenuRouteSync;
            pendingModMenuRouteSync = false;
            if (shouldSyncRoute) updateRouteStatus();
            handlePage();
        }, Math.max(0, Number(delay) || 0));
    }

    // Build a snapshot string of all module alive statuses for dirty-checking.
    function buildAliveSnapshot() {
        const parts = [];
        try {
            Object.keys(localStorage).forEach((key) => {
                if (key.startsWith(MANIFEST_PREFIX)) {
                    const manifest = readJson(key, null);
                    if (manifest?.id) parts.push(manifest.id + ':' + getScriptStatus(manifest.id));
                }
            });
        } catch { /* ignore */ }
        parts.sort();
        return parts.join('|');
    }

    function refreshActiveModMenuPage() {
        if (!isModMenuRoute()) return;
        if (document.getElementById('ntmods-app')) {
            invalidateModuleCache();
            renderPage('', '', true);
            _lastAliveSnapshot = buildAliveSnapshot();
            return;
        }
        ensureMainWaitObserver();
        scheduleModMenuPageSync({ syncRoute: false, delay: 0 });
    }

    function refreshActiveModMenuFieldViews() {
        if (!isModMenuRoute()) return;
        if (!document.getElementById('ntmods-app')) return;
        invalidateFieldCache();
        clearSectionDomCache();
        const uiState = readUiState();
        renderPage(uiState.moduleId || '', uiState.sectionId || '', false);
    }

    let modMenuStorageSyncRefreshTimer = null;
    let modMenuStorageSyncNeedsPage = false;
    let modMenuStorageSyncNeedsFields = false;

    function scheduleActiveModMenuStorageRefresh(kind) {
        if (!isModMenuRoute()) return;
        if (kind === 'page') {
            modMenuStorageSyncNeedsPage = true;
        } else {
            modMenuStorageSyncNeedsFields = true;
        }
        if (modMenuStorageSyncRefreshTimer) return;
        modMenuStorageSyncRefreshTimer = setTimeout(() => {
            modMenuStorageSyncRefreshTimer = null;
            const needsPage = modMenuStorageSyncNeedsPage;
            const needsFields = modMenuStorageSyncNeedsFields;
            modMenuStorageSyncNeedsPage = false;
            modMenuStorageSyncNeedsFields = false;
            if (needsPage) {
                refreshActiveModMenuPage();
                return;
            }
            if (needsFields) refreshActiveModMenuFieldViews();
        }, 100);
    }

    // Like refreshActiveModMenuPage, but only re-renders if module statuses changed.
    // Prevents unnecessary DOM rebuilds during heartbeat polling.
    function refreshIfStatusChanged() {
        if (!isModMenuRoute()) return;
        if (!document.getElementById('ntmods-app')) return;
        const snap = buildAliveSnapshot();
        if (snap === _lastAliveSnapshot) return;
        _lastAliveSnapshot = snap;
        invalidateModuleCache();
        renderPage('', '', true);
    }

    // No alive key clearing needed. getScriptStatus simply checks if the
    // alive key exists. Scripts write once on load, so any running script
    // has a key from the current page session.

    // Auto-cleanup removed: manifests are only cleaned via the Rescan button
    // (user-initiated). This prevents running scripts from losing their
    // manifests due to heartbeat timing races on page load. Scripts without
    // heartbeats correctly show as "Not Active" (outdated) until the user
    // rescans or the script heartbeats in.

    // Session-based alive detection: PAGE_SESSION_START (defined at top) records
    // when this page session started. getScriptStatus checks if alive keys are
    // from this session. No clearing needed — avoids race conditions with child
    // scripts that may run before or after the mod menu.

    let _mainWaitObserver = null;
    let _mainWaitTimer = null;
    const MAIN_WAIT_TIMEOUT_MS = 15000;

    function stopMainWaitObserver() {
        if (_mainWaitObserver) {
            try { _mainWaitObserver.disconnect(); } catch { /* ignore */ }
            _mainWaitObserver = null;
        }
        if (_mainWaitTimer) {
            clearTimeout(_mainWaitTimer);
            _mainWaitTimer = null;
        }
    }

    function failOpenModMenuRoute(reason = '') {
        if (!isModMenuRoute()) return;
        const main = document.querySelector('main.structure-content');
        if (!main) {
            document.documentElement.classList.remove('is-mods-route');
            return;
        }

        main.classList.add('custom-loaded');
        if (document.getElementById('ntmods-app')) return;

        main.innerHTML = buildRecoveryHtml(new Error(reason || 'Timed out waiting for Nitro Type to finish rendering the settings page.'));
        bindRecoveryEvents();
    }

    function stopModMenuRouteGuard() {
        if (modMenuRouteGuardObserver) {
            try { modMenuRouteGuardObserver.disconnect(); } catch { /* ignore */ }
            modMenuRouteGuardObserver = null;
        }
        if (modMenuRouteGuardTimer) {
            clearTimeout(modMenuRouteGuardTimer);
            modMenuRouteGuardTimer = null;
        }
    }

    function scheduleModMenuRouteGuardCheck(delay = 80) {
        if (!isModMenuRoute()) {
            stopModMenuRouteGuard();
            return;
        }

        if (modMenuRouteGuardTimer) clearTimeout(modMenuRouteGuardTimer);
        modMenuRouteGuardTimer = setTimeout(() => {
            modMenuRouteGuardTimer = null;
            if (!isModMenuRoute()) {
                stopModMenuRouteGuard();
                return;
            }

            updateRouteStatus();

            const main = document.querySelector('main.structure-content');
            if (!main) {
                ensureMainWaitObserver();
                return;
            }

            const app = document.getElementById('ntmods-app');
            if (app) {
                if (!main.classList.contains('custom-loaded')) {
                    main.classList.add('custom-loaded');
                }
                return;
            }

            if (renderInProgress || _suppressObserver) {
                scheduleModMenuRouteGuardCheck(150);
                return;
            }

            scheduleModMenuPageSync({ syncRoute: false, delay: 0 });
        }, Math.max(0, Number(delay) || 0));
    }

    function startModMenuRouteGuard() {
        if (!isModMenuRoute()) {
            stopModMenuRouteGuard();
            return;
        }

        if (!modMenuRouteGuardObserver) {
            modMenuRouteGuardObserver = new MutationObserver(() => {
                if (!isModMenuRoute()) {
                    stopModMenuRouteGuard();
                    return;
                }
                scheduleModMenuRouteGuardCheck(80);
            });
            modMenuRouteGuardObserver.observe(document.documentElement, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['class']
            });
        }

        scheduleModMenuRouteGuardCheck(0);
    }

    function ensureMainWaitObserver() {
        if (!isModMenuRoute()) {
            stopMainWaitObserver();
            return;
        }
        if (!_mainWaitObserver) {
            _mainWaitObserver = new MutationObserver(() => {
                if (!isModMenuRoute()) {
                    stopMainWaitObserver();
                    return;
                }

                const observedMain = document.querySelector('main.structure-content');
                if (!observedMain) return;

                const app = document.getElementById('ntmods-app');
                const isLoaded = observedMain.classList.contains('custom-loaded');
                if (!app || !isLoaded) {
                    scheduleModMenuPageSync({ syncRoute: false, delay: 0 });
                }
            });

            _mainWaitObserver.observe(document.documentElement, { childList: true, subtree: true });
        }

        const main = document.querySelector('main.structure-content');
        if (main) {
            const app = document.getElementById('ntmods-app');
            const isLoaded = main.classList.contains('custom-loaded');
            if (!app || !isLoaded) {
                scheduleModMenuPageSync({ syncRoute: false, delay: 0 });
            }
        }

        if (_mainWaitTimer) clearTimeout(_mainWaitTimer);
        _mainWaitTimer = setTimeout(() => {
            if (isModMenuRoute() && !document.getElementById('ntmods-app')) {
                failOpenModMenuRoute('Timed out waiting for Nitro Type to finish rendering the settings page.');
            }
            stopMainWaitObserver();
        }, MAIN_WAIT_TIMEOUT_MS);
    }

    function fastInject() {
        insertModsDropdownItem();
        if (isModMenuRoute()) {
            startModMenuRouteGuard();
            ensureMainWaitObserver();
        }
    }

    function onSpaNavigation() {
        scheduleModMenuPageSync({ syncRoute: true, delay: 0 });
        ensureDropdownAfterNav();
    }

    // Retry dropdown injection over a few seconds to survive React re-renders.
    // React may destroy and recreate the nav bar during SPA navigations,
    // so we keep checking even after successful injection.
    let _dropdownRetryTimer = null;
    function retryDropdownInjection(remaining) {
        if (remaining === undefined) remaining = 8; // ~4s of retries
        if (remaining <= 0) {
            updateDropdownDot(); // once after all retries, not on every retry
            return;
        }
        insertModsDropdownItem();
        _dropdownRetryTimer = setTimeout(() => retryDropdownInjection(remaining - 1), 500);
    }
    retryDropdownInjection();

    // Re-inject dropdown item after SPA navigations settle.
    // React may re-render the nav bar, removing our injected item.
    function ensureDropdownAfterNav() {
        if (_dropdownRetryTimer) clearTimeout(_dropdownRetryTimer);
        retryDropdownInjection();
    }

    // No delayed re-renders needed. Each child script writes its alive key
    // BEFORE dispatching ntcfg:manifest-updated, so the mod menu's re-render
    // (triggered by that event) already sees the correct alive status.

    function startModMenuRouteShell() {
        const ntRouteHelper = initNTRouteHelper(window);
        ntRouteHelper.subscribe(() => {
            onSpaNavigation();
        }, { immediate: false });

        document.addEventListener('ntcfg:manifest-updated', refreshActiveModMenuPage);
        document.addEventListener(RUNTIME_MARKER_EVENT, () => scheduleActiveModMenuStorageRefresh('page'));
        window.addEventListener(RUNTIME_MARKER_EVENT, () => scheduleActiveModMenuStorageRefresh('page'));
        window.addEventListener('storage', (event) => {
            const key = String(event?.key || '');
            if (!key) return;
            if (key === EXTENSION_RUNTIME_MARKER_KEY || key.startsWith(STANDALONE_RUNTIME_PREFIX)) {
                scheduleActiveModMenuStorageRefresh('page');
                return;
            }
            // Alive heartbeats are rewritten on every page load in other tabs;
            // a full field rebuild for those wipes hover/focus state. Status
            // badges only need the cheap diff check.
            if (key.startsWith(ALIVE_PREFIX)) {
                refreshIfStatusChanged();
                return;
            }
            if (key.startsWith(MANIFEST_PREFIX)) {
                refreshActiveModMenuPage();
                return;
            }
            if (key.startsWith('ntcfg:') || key.startsWith(PREVIEW_VALUE_PREFIX)) {
                refreshActiveModMenuFieldViews();
            }
        });

        fastInject();
        scheduleModMenuPageSync({ syncRoute: true, delay: 0 });
    }

    function startModMenuDropdownLifecycle() {
        retryDropdownInjection();
    }

    function startModMenuVersionCheckLifecycle() {
        // Bot Hunter remains maintained as the sole external companion script.
        // All sunset modules stay excluded from GreasyFork polling.
        updateDropdownDot();
        window.setTimeout(() => {
            try {
                if (!isModMenuRoute()) return;
                const state = getBotHunterUpdateState();
                if (!state.installed || !state.installedVersion) return;
                const lastCheck = Number(readPersistentJson(BOT_HUNTER_VERSION_CHECK_TIMESTAMP_KEY, 0) || 0);
                if (Date.now() - lastCheck < VERSION_CACHE_TTL_MS) {
                    updateDropdownDot();
                    return;
                }
                void refreshBotHunterVersion({ forceRefresh: true, stampGlobalCheck: true }).then(() => {
                    if (!isModMenuRoute()) return;
                    _versionCheckDone = true;
                    invalidateModuleCache();
                    renderPage('', '', true);
                });
            } catch {
                // Version checks are best-effort; manual checking remains available.
            }
        }, 3000);
    }

    startModMenuRouteShell();
    startModMenuDropdownLifecycle();
    startModMenuVersionCheckLifecycle();
})();