Raw Source
TheNorthman / TNT Collection

// ==UserScript==
// @name         TNT Collection
// @version      2.1.0
// @namespace    tnt.collection
// @author       Kingfisher
// @description  TNT Collection Tools for Ikariam
// @license      MIT
// @include      http*s*.ikariam.*/*
// @exclude      http*support*.ikariam.*/*
// @require      https://code.jquery.com/jquery-1.12.4.min.js
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_log
// @grant        GM_xmlhttpRequest
// @downloadURL  https://raw.githubusercontent.com/TheNorthman/tnt.collection/main/dist/tnt.collection.user.js
// @updateURL    https://raw.githubusercontent.com/TheNorthman/tnt.collection/main/dist/tnt.collection.user.js
// ==/UserScript==

// Ikariam scaling fix
//ikariam.worldview_scale_city = 1;
//ikariam.worldview_scale_island = 1;
//ikariam.worldview_scale_max = 1;
//ikariam.worldview_scale_min = 0.90;
//ikariam.worldview_scale_worldmap = 1;
ikariam.worldview_scroll_left_city = 240;
//ikariam.worldview_scroll_left_island = 265;
//ikariam.worldview_scroll_top_city = 120;
//ikariam.worldview_scroll_top_island = 190;
Object.defineProperty(ikariam, "worldview_scale_min", {
  set: v => Reflect.set(ikariam, "_worldview_scale_min", Math.max(0.94, v)),
  get: () => ikariam._worldview_scale_min ?? 0.94,
  configurable: true
});

ikariam.worldview_scale_city = 0.94;

// Initialize the tntConsole
const tntConsole = Object.assign({}, window.console);

// Move large data blocks to separate internal modules for better organization
const TNT_BUILDING_DEFINITIONS = Object.freeze([
    // Government
    { key: 'townHall', name: 'Town Hall', viewName: 'townHall', icon: '/cdn/all/both/img/city/townhall_l.png', buildingId: 0, helpId: 1, maxedLvl: 32, category: 'government' },
    { key: 'palace', name: 'Palace', viewName: 'palace', icon: '/cdn/all/both/img/city/palace_l.png', buildingId: 11, helpId: 1, maxedLvl: 12, category: 'government' },
    { key: 'palaceColony', name: 'Governor\'s Residence', viewName: 'palaceColony', icon: '/cdn/all/both/img/city/palaceColony_l.png', buildingId: 17, helpId: 1, maxedLvl: 12, category: 'government' },
    { key: 'embassy', name: 'Embassy', viewName: 'embassy', icon: '/cdn/all/both/img/city/embassy_l.png', buildingId: 12, helpId: 1, category: 'government' },
    { key: 'chronosForge', name: 'Chronos\' Forge', viewName: 'chronosForge', icon: '/cdn/all/both/img/city/chronosForge_l.png', buildingId: 35, helpId: 1, maxedLvl: 4, category: 'government' },

    // Resource storage
    { key: 'warehouse', name: 'Warehouse', viewName: 'warehouse', icon: '/cdn/all/both/img/city/warehouse_l.png', buildingId: 7, helpId: 1, maxedLvl: 24, category: 'trade' },
    { key: 'dump', name: 'Depot', viewName: 'dump', icon: '/cdn/all/both/img/city/dump_l.png', buildingId: 29, helpId: 1, maxedLvl: 24, category: 'trade' },

    // Trade & Diplomacy
    { key: 'port', name: 'Trading Port', viewName: 'port', icon: '/cdn/all/both/img/city/port_l.png', buildingId: 3, helpId: 1, maxedLvl: 24, category: 'trade' },
    { key: 'dockyard', name: 'Dockyard', viewName: 'dockyard', icon: '/cdn/all/both/img/city/dockyard_l.png', buildingId: 33, helpId: 1, maxedLvl: 3, category: 'trade' },
    { key: 'marineChartArchive', name: 'Sea Chart Archive', viewName: 'marineChartArchive', icon: '/cdn/all/both/img/city/marinechartarchive_l.png', buildingId: 32, helpId: 1, maxedLvl: 18, category: 'trade' },
    { key: 'branchOffice', name: 'Trading Post', viewName: 'tradingPost', icon: '/cdn/all/both/img/city/branchoffice_l.png', buildingId: 13, helpId: 1, maxedLvl: 20, category: 'trade' },

    // Culture & Research
    { key: 'academy', name: 'Academy', viewName: 'academy', icon: '/cdn/all/both/img/city/academy_l.png', buildingId: 4, helpId: 1, maxedLvl: 24, category: 'culture' },
    { key: 'museum', name: 'Museum', viewName: 'museum', icon: '/cdn/all/both/img/city/museum_l.png', buildingId: 10, helpId: 1, maxedLvl: 21, category: 'culture' },
    { key: 'tavern', name: 'Tavern', viewName: 'tavern', icon: '/cdn/all/both/img/city/taverne_l.png', buildingId: 9, helpId: 1, maxedLvl: 32, category: 'culture' },
    { key: 'temple', name: 'Temple', viewName: 'temple', icon: '/cdn/all/both/img/city/temple_l.png', buildingId: 28, helpId: 1, maxedLvl: 24, category: 'culture' },
    { key: 'shrineOfOlympus', name: 'Gods\' Shrine', viewName: 'shrineOfOlympus', icon: '/cdn/all/both/img/city/shrineOfOlympus_l.png', buildingId: 34, helpId: 1, maxedLvl: 20, category: 'culture' },

    // Resource reducers
    { key: 'carpentering', name: 'Carpenter', viewName: 'carpentering', icon: '/cdn/all/both/img/city/carpentering_l.png', buildingId: 23, helpId: 1, maxedLvl: 50, category: 'resourceReducer' },
    { key: 'architect', name: 'Architect\'s Office', viewName: 'architect', icon: '/cdn/all/both/img/city/architect_l.png', buildingId: 24, helpId: 1, maxedLvl: 50, category: 'resourceReducer' },
    { key: 'vineyard', name: 'Wine Press', viewName: 'vineyard', icon: '/cdn/all/both/img/city/vineyard_l.png', buildingId: 26, helpId: 1, maxedLvl: 50, category: 'resourceReducer' },
    { key: 'optician', name: 'Optician', viewName: 'optician', icon: '/cdn/all/both/img/city/optician_l.png', buildingId: 25, helpId: 1, maxedLvl: 50, category: 'resourceReducer' },
    { key: 'fireworker', name: 'Firework Test Area', viewName: 'fireworker', icon: '/cdn/all/both/img/city/fireworker_l.png', buildingId: 27, helpId: 1, maxedLvl: 50, category: 'resourceReducer' },

    // Resource enhancers
    { key: 'forester', name: 'Forester\'s House', viewName: 'forester', icon: '/cdn/all/both/img/city/forester_l.png', buildingId: 18, helpId: 1, maxedLvl: 30, category: 'resourceEnhancer' },
    { key: 'stonemason', name: 'Stonemason', viewName: 'stonemason', icon: '/cdn/all/both/img/city/stonemason_l.png', buildingId: 19, helpId: 1, maxedLvl: 30, category: 'resourceEnhancer' },
    { key: 'winegrower', name: 'Winegrower', viewName: 'winegrower', icon: '/cdn/all/both/img/city/winegrower_l.png', buildingId: 21, helpId: 1, maxedLvl: 30, category: 'resourceEnhancer' },
    { key: 'glassblowing', name: 'Glassblower', viewName: 'glassblowing', icon: '/cdn/all/both/img/city/glassblowing_l.png', buildingId: 20, helpId: 1, maxedLvl: 30, category: 'resourceEnhancer' },
    { key: 'alchemist', name: 'Alchemist\'s Tower', viewName: 'alchemist', icon: '/cdn/all/both/img/city/alchemist_l.png', buildingId: 22, helpId: 1, maxedLvl: 30, category: 'resourceEnhancer' },

    // Military
    { key: 'wall', name: 'Wall', viewName: 'wall', icon: '/cdn/all/both/img/city/wall.png', buildingId: 8, helpId: 1, maxedLvl: 32, category: 'military' },
    { key: 'barracks', name: 'Barracks', viewName: 'barracks', icon: '/cdn/all/both/img/city/barracks_l.png', buildingId: 6, helpId: 1, maxedLvl: 32, category: 'military' },
    { key: 'safehouse', name: 'Hideout', viewName: 'safehouse', icon: '/cdn/all/both/img/city/safehouse_l.png', buildingId: 16, helpId: 1, maxedLvl: 42, category: 'military' },
    { key: 'workshop', name: 'Workshop', viewName: 'workshop', icon: '/cdn/all/both/img/city/workshop_l.png', buildingId: 15, helpId: 1, maxedLvl: 32, category: 'military' },
    { key: 'shipyard', name: 'Shipyard', viewName: 'shipyard', icon: '/cdn/all/both/img/city/shipyard_l.png', buildingId: 5, helpId: 1, maxedLvl: 32, category: 'military' },

    // Special buildings
    { key: 'pirateFortress', name: 'Pirate Fortress', viewName: 'pirateFortress', icon: '/cdn/all/both/img/city/pirateFortress_l.png', buildingId: 30, helpId: 1, category: 'special' },
    { key: 'blackMarket', name: 'Black Market', viewName: 'blackMarket', icon: '/cdn/all/both/img/city/blackmarket_l.png', buildingId: 31, helpId: 1, category: 'special' }
]);

// validBuildingTypes is always in sync with TNT_BUILDING_DEFINITIONS
const validBuildingTypes = Object.freeze(TNT_BUILDING_DEFINITIONS.map(b => b.key));

const TNT_TOOLTIP_TEMPLATES = {
    resource: {
        header: {
            wood: {
                title: 'Wood',
                body: 'Production:<br><span class="tnt_tooltip_indent">1h: {1hwood}</span><br><span class="tnt_tooltip_indent">24h: {24hwood}</span><br>'
            },
            wine: {
                title: 'Wine',
                body: 'Production:<br><span class="tnt_tooltip_indent">1h: {1hwine}</span><br><span class="tnt_tooltip_indent">24h: {24hwine}</span><br>Luxury good consumed in Taverns to keep citizens happy.<br>Produced by Winegrowers.'
            },
            marble: {
                title: 'Marble',
                body: 'Production:<br><span class="tnt_tooltip_indent">1h: {1hmarble}</span><br><span class="tnt_tooltip_indent">24h: {24hmarble}</span><br>Used for structural buildings and town upgrades.<br>Supplied by Stonemasons.'
            },
            crystal: {
                title: 'Crystal Glass',
                body: 'Production:<br><span class="tnt_tooltip_indent">1h: {1hcrystal}</span><br><span class="tnt_tooltip_indent">24h: {24hcrystal}</span><br>Essential for research and scientific progress.<br>Refined by Opticians.'
            },
            sulfur: {
                title: 'Sulfur',
                body: 'Production:<br><span class="tnt_tooltip_indent">1h: {1hsulfur}</span><br><span class="tnt_tooltip_indent">24h: {24hsulfur}</span><br>Powerful military resource used to create weapons and explosives.<br>Extracted by Fireworkers.'
            },
            population: {
                title: 'Population',
                body: 'Total inhabitants of your city.<br>Affects growth, tax income, and workforce availability.'
            },
            citizens: {
                title: 'Citizens',
                body: 'Free population available for jobs,<br>research, or military service.'
            }
        },
        cell: {
            wood: {
                title: 'Wood',
                body: '{cityName} wood: {value}'
            },
            wine: {
                title: 'Wine',
                body: '{cityName} wine: {value}'
            },
            marble: {
                title: 'Marble',
                body: '{cityName} marble: {value}'
            },
            crystal: {
                title: 'Crystal',
                body: '{cityName} crystal: {value}'
            },
            sulfur: {
                title: 'Sulfur',
                body: '{cityName} sulfur: {value}'
            },
            population: {
                title: 'Population',
                body: '{cityName} population: {value}'
            },
            citizens: {
                title: 'Citizens',
                body: '{cityName} citizens: {value}'
            }
        }
    },
    building: {
        header: {
            default: {
                title: '{buildingName}',
                body: 'Max Level: {maxedLvl}'
            }
        },
        cell: {
            default: {
                title: '{buildingName} - {cityName}',
                body: 'Status {statusText}'
            }
        }
    }
};

const template = Object.freeze({
    resources: `
        <div id="tnt_info_resources">
            <div id="tnt_info_resources_content"></div>
            <div id="tnt_info_buildings_content" style="display:none;"></div>
        </div>
    `
});

const TNT_STYLES = `
`;

const tnt = {

    version: GM_info.script.version,

    template, // Add template to tnt object

    delay: (time) => new Promise(resolve => setTimeout(resolve, time)),

    // Settings module - manage user settings
    settings: {
        // Get setting with default value from new storage structure
        get(key, defaultValue = null) {
            return tnt.data.storage.settings?.[key] ?? defaultValue;
        },

        // Set setting value in new storage structure
        set(key, value) {
            if (!tnt.data.storage.settings) {
                tnt.data.storage.settings = {};
            }
            tnt.data.storage.settings[key] = value;
            tnt.core.storage.save();
        },

        // Toggle boolean setting
        toggle(key) {
            const current = this.get(key, false);
            this.set(key, !current);
            return !current;
        },

        // Get persistent per-building max-level (editable by user)
        getMaxedLvl(buildingType) {
            if (!tnt.data.storage.settings) return 0;
            const maxed = tnt.data.storage.settings.maxedLvl || {};
            if (buildingType === 'palaceOrColony') {
                if (maxed && typeof maxed[buildingType] !== 'undefined' && maxed[buildingType] !== null) {
                    const parsed = parseInt(maxed[buildingType], 10);
                    if (!isNaN(parsed) && parsed >= 0) return parsed;
                }
                const p = this.getMaxedLvl('palace');
                const c = this.getMaxedLvl('palaceColony');
                return Math.max(p, c);
            }

            if (maxed && typeof maxed[buildingType] !== 'undefined' && maxed[buildingType] !== null) {
                const parsed = parseInt(maxed[buildingType], 10);
                if (!isNaN(parsed) && parsed >= 0) return parsed;
            }

            const def = TNT_BUILDING_DEFINITIONS.find(b => b.key === buildingType);
            return def && def.maxedLvl ? def.maxedLvl : 0;
        },

        resetMaxedLvl(buildingType) {
            if (!tnt.data.storage.settings || !tnt.data.storage.settings.maxedLvl) return;
            const maxed = tnt.data.storage.settings.maxedLvl;
            if (buildingType === 'palaceOrColony') {
                delete maxed.palace;
                delete maxed.palaceColony;
                delete maxed.palaceOrColony;
            } else {
                delete maxed[buildingType];
            }
            tnt.core.storage.save();
        },

        setMaxedLvl(buildingType, value) {
            if (!tnt.data.storage.settings) {
                tnt.data.storage.settings = {};
            }
            if (!tnt.data.storage.settings.maxedLvl) {
                tnt.data.storage.settings.maxedLvl = {};
            }

            // empty means reset to default
            if (value === '' || value === null || typeof value === 'undefined') {
                this.resetMaxedLvl(buildingType);
                return;
            }

            const parsed = parseInt(value, 10);
            if (isNaN(parsed) || parsed < 0) {
                this.resetMaxedLvl(buildingType);
            } else {
                // remove override if equal final default (reduces stored state)
                const def = this.getMaxedLvl(buildingType);
                if (parsed === def) {
                    this.resetMaxedLvl(buildingType);
                } else {
                    tnt.data.storage.settings.maxedLvl[buildingType] = parsed;
                }
            }
            tnt.core.storage.save();
        },

        // Get layout preferences
        getLayoutPrefs() {
            return this.get("layoutPrefs", {
                maintainLayout: false,
                url: "",
                layout: null
            });
        },

        // Set layout preferences
        setLayoutPrefs(prefs) {
            this.set("layoutPrefs", prefs);
        },

        // Clear layout preferences
        clearLayoutPrefs() {
            this.set("layoutPrefs", {
                maintainLayout: false,
                url: "",
                layout: null
            });
        },

        // Parse Ikariam URL and extract layout parameters
        parseLayoutFromUrl(url) {
            try {
                const urlObj = new URL(url);
                const params = urlObj.searchParams;

                // Extract layout parameters
                const layout = {
                    citymap: {},
                    mainbox: {},
                    sidebar: {}
                };

                // City map (offsets and zoom)
                const cityTop = params.get('cityTop');
                const cityLeft = params.get('cityLeft');
                const cityWorldviewScale = params.get('cityWorldviewScale');
                if (cityTop) layout.citymap.top = parseInt(cityTop.replace('px', ''));
                if (cityLeft) layout.citymap.left = parseInt(cityLeft.replace('px', ''));
                if (cityWorldviewScale) layout.citymap.zoom = parseFloat(cityWorldviewScale);

                // Mainbox parameters
                const mainboxX = params.get('mainbox_x');
                const mainboxY = params.get('mainbox_y');
                const mainboxZ = params.get('mainbox_z');
                if (mainboxX) layout.mainbox.x = parseInt(mainboxX);
                if (mainboxY) layout.mainbox.y = parseInt(mainboxY);
                if (mainboxZ) layout.mainbox.z = parseInt(mainboxZ);

                // Sidebar parameters
                const sidebarX = params.get('sidebar_x');
                const sidebarY = params.get('sidebar_y');
                const sidebarZ = params.get('sidebar_z');
                if (sidebarX) layout.sidebar.x = parseInt(sidebarX);
                if (sidebarY) layout.sidebar.y = parseInt(sidebarY);
                if (sidebarZ) layout.sidebar.z = parseInt(sidebarZ);

                return layout;
            } catch (e) {
                tnt.core.debug.warn('TNT: Failed to parse layout URL: ' + e.message, 3);
                return null;
            }
        },

        // Get all resource display settings
        getResourceDisplaySettings() {
            return {
                showResources: this.get("cityShowResources", true),
                showPopulation: this.get("cityShowResourcesPorpulation", true),
                showCitizens: this.get("cityShowResourcesCitizens", true),
                showWood: this.get("cityShowResourcesWoods", true),
                showWine: this.get("cityShowResourcesWine", true),
                showMarble: this.get("cityShowResourcesMarble", true),
                showCrystal: this.get("cityShowResourcesCrystal", true),
                showSulfur: this.get("cityShowResourcesSulfur", true)
            };
        },

        // Get all feature settings
        getFeatureSettings() {
            return {
                removePremiumOffers: this.get("allRemovePremiumOffers", true),
                removeFooterNavigation: this.get("allRemoveFooterNavigation", true),
                changeNavigationCoord: this.get("allChangeNavigationCoord", true),
                showCityLvl: this.get("islandShowCityLvl", true),
                removeFlyingShop: this.get("cityRemoveFlyingShop", true),
                notificationAdvisors: this.get("notificationAdvisors", true),
                notificationSound: this.get("notificationSound", true)
            };
        },

        // Validate if URL is a valid Ikariam URL
        isValidIkariamUrl(url) {
            try {
                const urlObj = new URL(url);
                return urlObj.hostname.includes('ikariam') &&
                    urlObj.hostname.includes('gameforge.com');
            } catch (e) {
                return false;
            }
        },

        // Initialize default settings - simplified without migration
        initDefaults() {
            const defaults = {
                "allRemovePremiumOffers": true,
                "allRemoveFooterNavigation": true,
                "allChangeNavigationCoord": true,
                "islandShowCityLvl": true,
                "cityRemoveFlyingShop": true,
                "cityShowResources": true,
                "cityShowResourcesPorpulation": true,
                "cityShowResourcesCitizens": true,
                "cityShowResourcesWoods": true,
                "cityShowResourcesWine": true,
                "cityShowResourcesMarble": true,
                "cityShowResourcesCrystal": true,
                "cityShowResourcesSulfur": true,
                "notificationAdvisors": true,
                "notificationSound": true,
                "citySwitcherActive": false,
                "citySwitcherStartCity": null,
                "citySwitcherVisited": [],
                "debugEnabled": true,
                "layoutPrefs": {
                    maintainLayout: false,
                    url: "",
                    layout: null
                }
            };

            // Initialize defaults for any missing settings
            Object.entries(defaults).forEach(([key, defaultValue]) => {
                if (this.get(key) === undefined) {
                    this.set(key, defaultValue);
                }
            });

            // Ensure maxedLvl mapping exists
            if (!this.get("maxedLvl")) {
                this.set("maxedLvl", {});
            }

            this.set("version", tnt.version);
        }
    },

    // Main data structure to hold all data
    data: {
        ikariam: {
            subDomain: location.hostname.split('.')[0],
            url: {
                notification: (() => {
                    const sub = location.hostname.split('.')[0];
                    const base = `https://${sub}.ikariam.gameforge.com/cdn/all/both/layout/advisors/`;
                    return {
                        defaultPicture: base + "mayor_premium.png",
                        mayor: base + "mayor.png",
                        mayor_premium: base + "mayor_premium.png",
                        general: base + "general.png",
                        general_premium: base + "general_premium.png",
                        general_alert: base + "general_premium_alert.png",
                        scientist: base + "scientist.png",
                        scientist_premium: base + "scientist_premium.png",
                        diplomat: base + "diplomat.png",
                        diplomat_premium: base + "diplomat_premium.png"
                    };
                })()
            }
        },
        storage: {
            // NEW STRUCTURE: Own cities (existing data)
            city: {},

            // NEW STRUCTURE: Foreign cities
            foreign: {},

            // NEW STRUCTURE: Cities with spies (subset of foreign)
            spy: {},

            // NEW STRUCTURE: Avatar/player data
            avatar: {
                ambrosia: 0,
                gold: 0
            },

            // NEW STRUCTURE: TNT settings (includes notification settings)
            settings: {
                notification: {
                    city: false,
                    military: false,
                    militaryAlert: false,
                    scientist: false,
                    diplomat: false
                }
            }
        }
    },

    // IMPORTANT: Common functionality that runs on all pages
    all() {
        // Common functionality that runs on all pages
        const settings = this.settings.getFeatureSettings();

        // Apply global UI modifications
        if (settings.removePremiumOffers) {
            $('.premiumOfferBox').hide();
        }
    },

    // IMPORTANT: City-specific functionality
    city() {
        // Apply city-specific modifications
        tnt.ui.applyUIModifications();

        // Apply layout after DOM is rendered. This set mainbox to user defined position, if enabled, so it has effect before dialogs are opened
        tnt.utils.applyLayoutDirectly();
    },

    // IMPORTANT: Island-specific functionality
    island() {
        // Island-specific functionality
        tnt.core.debug.log('[TNT] Island view loaded');

        // Show city levels if setting is enabled
        if (tnt.settings.get("islandShowCityLvl", true)) {
            tnt.utils.displayCityLevels();
        }
    },

    // IMPORTANT: World-specific functionality
    world() {
        // World map specific functionality
        tnt.core.debug.log('[TNT] World map loaded');

        // Apply UI modifications for world map - Found in Ikariam Map Enhancer
        $('.cities').each(function () {
            if (this.innerText === "0") {
                $(this).parent().css('opacity', 0.5);
            } else {
                $(this).parent().css('opacity', 1);
            }
        });
        $('.own, .ally').css('filter', 'drop-shadow(0px 10px 4px #000)');
        $('.piracyInRange').css('opacity', 0.75);
    },

    // Initialize the core module
    core: {
        init() {
            // We need to init the storage before anything else, so tnt.core.debug has its settings available
            tnt.core.storage.init();

            // Log the initialization
            tnt.core.debug.log(`TNT Collection v${tnt.version} - Init...`, 1);

            // We run events.init() first to overwrite the default Ikariam events as early as possible
            tnt.core.events.init();

            // Initialize all core components
            tnt.core.storage.init();
            tnt.core.notification.init();
            tnt.core.options.init();

            // Collect city data
            tnt.dataCollector.update();
            tnt.dataCollector.show();

            // Apply UI modifications
            tnt.ui.applyUIModifications();

            // Apply global styles
            tnt.all();

            // Check if city switcher is active, and continue if so.
            tnt.citySwitcher.checkAndContinue();

            switch ($("body").attr("id")) {
                case "island": tnt.island(); break;
                case "city": tnt.city(); break;
                case "worldmap_iso": tnt.world(); break;
            }
        },

        // AJAX helper - Not used at the moment, but can be used for future AJAX requests
        ajax: {
            send(data, url = tnt.url.update, callback = null) {
                // Remove noisy debug logging
                tnt.core.debug.log('[TNT] Ajax call data length: ' + JSON.stringify(data).length, 3);
                GM_xmlhttpRequest({
                    url, method: 'POST',
                    data: "data=" + encodeURIComponent(JSON.stringify(data)),
                    headers: { "Content-Type": "application/x-www-form-urlencoded" },
                    onload: resp => {
                        if (callback) callback();
                    },
                    onerror: (error) => {
                        // Keep error logging but make it cleaner
                        tnt.core.debug.error("[TNT] AJAX Error: " + error.message, 1);
                    }
                });
            }
        },

        debug: {
            enable: 1,
            level: 5,

            // Log messages with level control
            log(val, level = 2) {
                const debug = tnt.settings.get('debug', { enable: true, level: 2 });
                if (debug.enable && level <= debug.level) {
                    tntConsole.log(val);
                }
            },

            // Log objects with level control
            dir(val, level = 2) {
                const debug = tnt.settings.get('debug', { enable: true, level: 1 });
                if (debug.enable && level <= debug.level) {
                    tntConsole.dir(val);
                }
            },

            // Log warnings with level control
            warn(val, level = 3) {
                const debug = tnt.settings.get('debug', { enable: true, level: 1 });
                if (debug.enable && level <= debug.level) {
                    tntConsole.warn(val);
                }
            },

            // Log errors with level control
            error(val, level = 1) {
                const debug = tnt.settings.get('debug', { enable: true, level: 1 });
                if (debug.enable && level <= debug.level) {
                    tntConsole.error(val);
                }
            }
        },

        storage: {
            init() {
                const scriptStartTime = performance.now();

                try {
                    const storedData = localStorage.getItem("tnt_storage");

                    if (storedData) {
                        const parsedData = JSON.parse(storedData);
                        const storedVersion = parsedData.version;

                        // Enhanced version check - detect structure compatibility
                        if (storedVersion === tnt.version) {
                            // Same version - use existing data
                            tnt.data.storage = $.extend(true, {}, tnt.data.storage, parsedData);
                        } else {
                            // Check if stored data has new structure (city, foreign, spy, settings)
                            const hasNewStructure = parsedData.city &&
                                parsedData.settings &&
                                typeof parsedData.settings === 'object';

                            if (hasNewStructure) {
                                // New structure exists - just update version, no reset needed
                                tnt.data.storage = $.extend(true, {}, tnt.data.storage, parsedData);
                                tnt.data.storage.version = tnt.version;
                                tnt.core.storage.save();

                                // Log timing information
                                tnt.core.debug.log(`[TNT Timing] Script start: ${scriptStartTime.toFixed(2)}ms`, 2);
                                tnt.core.debug.log(`[TNT Timing] Storage parsed: ${(performance.now() - scriptStartTime).toFixed(2)}ms`, 2);
                            } else {
                                // Reset to clean defaults with current version
                                tnt.data.storage.version = tnt.version;
                                tnt.core.storage.save();

                                // Smart auto-start data collection with 200ms delay
                                setTimeout(() => {
                                    const cityList = tnt.get.player.list.cities();
                                    const cityCount = Object.keys(cityList).length;

                                    if (cityCount > 1) {
                                        // Multiple cities - start city switcher
                                        tnt.citySwitcher.start();
                                    } else if (cityCount === 1) {
                                        // Single city - just collect current city data
                                        tnt.dataCollector.update();
                                    }
                                }, 200);
                            }
                        }
                    } else {
                        // No existing storage - new user
                        tnt.data.storage.version = tnt.version;
                        tnt.core.storage.save();

                        // Smart auto-start for new users with 200ms delay
                        setTimeout(() => {
                            const cityList = tnt.get.player.list.cities();
                            const cityCount = Object.keys(cityList).length;

                            if (cityCount > 1) {
                                // Multiple cities - start city switcher
                                tnt.citySwitcher.start();
                            } else if (cityCount === 1) {
                                // Single city - just collect current city data
                                tnt.dataCollector.update();
                            }
                        }, 200);
                    }

                    // Check when city list becomes available
                    const cityList = tnt.get.player.list.cities();

                } catch (e) {
                    tnt.core.debug.log("Error parsing tnt_storage: " + e.message, 1);

                    // On parse error, treat as new user
                    tnt.data.storage.version = tnt.version;
                    tnt.core.storage.save();
                }
            },

            // Get setting value from storage
            get(group, name) {
                if (!tnt.data.storage || !tnt.data.storage[group]) return undefined;
                return tnt.data.storage[group][name];
            },

            // Set setting value in storage
            set(group, name, value) {
                if (!tnt.data.storage) tnt.data.storage = {};
                if (!tnt.data.storage[group]) tnt.data.storage[group] = {};
                tnt.data.storage[group][name] = value;
                tnt.core.storage.save();
            },
            // Save data to storage
            save() {
                try {
                    localStorage.setItem("tnt_storage", JSON.stringify(tnt.data.storage));
                } catch (e) {
                    tnt.core.debug.log("Error saving to localStorage: " + e.message, 1);
                }
            }
        },

        notification: {
            init() { if (Notification && Notification.permission !== "granted") Notification.requestPermission(); },
            notifyMe(title, message, picture) {
                // Disabled for now
                return;
            },
            check() {
                // Disable notifications for now
                return;
            }
        },

        events: {
            init() {
                // Check if ajax and ajax.Responder exist before overriding
                if (typeof ajax !== 'undefined' && ajax.Responder) {
                    tnt.core.debug.log('[TNT] Ajax responder available, applying override', 2);
                    tnt.core.events.ikariam.override();
                } else {
                    tnt.core.debug.log('[TNT] Ajax responder not available, skipping override', 2);
                }
            },
            ikariam: {
                override() {
                    // updateGlobalData = Move this into its own function
                    ajax.Responder.tntUpdateGlobalData = ajax.Responder.updateGlobalData;
                    ajax.Responder.updateGlobalData = function (response) {

                        var view = $('body').attr('id');
                        tnt.core.debug.warn("[TNT] updateGlobalData (View: " + view + ")", 4);

                        // Let Ikariam do its stuff
                        ajax.Responder.tntUpdateGlobalData(response);

                        // Check notifications
                        tnt.core.notification.check();

                        // Collect data
                        tnt.dataCollector.update();
                        tnt.dataCollector.show();

                        // Run tnt.all() to handle all common tasks
                        tnt.all();
                    }

                    // updateBackgroundData = Move this into its own function
                    ajax.Responder.tntUpdateBackgroundData = ajax.Responder.updateBackgroundData;
                    ajax.Responder.updateBackgroundData = function (response) {
                        var view = $('body').attr('id');
                        tnt.core.debug.log("updateBackgroundData (View: " + view + ")", 3);

                        // Let Ikariam do its stuff
                        ajax.Responder.tntUpdateBackgroundData(response);

                        // Check notifications
                        tnt.core.notification.check();

                        // Apply removeFlyingShop/sidebar slots removal, during background updates
                        if (view === "city") {
                            tnt.ui.applyUIModifications();
                        }

                        switch (view) {
                            case "worldmap_iso":
                                tnt.core.debug.log($('worldmap_iso: div.islandTile div.cities'), 3);
                                break;
                            case "city":
                                break;
                            case "plunder":
                            case "deploymentFleet":
                            case "deployment":
                            case "plunderFleet":
                                // Select all units when pillaging
                                setTimeout(() => {
                                    // Set all units to max
                                    $('#selectArmy .setMax').trigger("click");
                                    $('#fleetDeploymentForm .setMax').trigger("click");

                                    // Set extra transporters to available count
                                    const freeTransporters = parseInt($("#js_GlobalMenu_freeTransporters").text()) || 0;
                                    $('#extraTransporter').val(freeTransporters);
                                }, 1500);
                                break;
                            case 'tradeAdvisor':
                                tnt.core.debug.log("tradeAdvisor", 3);
                                break;
                        }
                    }

                    // changeView = Move this into its own function
                    ajax.Responder.tntChangeView = ajax.Responder.changeView;
                    ajax.Responder.changeView = function (response) {
                        tnt.core.debug.log("I'm here!");
                        var view = $('body').attr('id');

                        // Set early Ikariam properties before rendering
                        try {
                            if (ikariam.templateView && ikariam.templateView.id === "city") {
                                const layoutPrefs = tnt.data.storage.settings.layoutPrefs;
                                if (layoutPrefs && layoutPrefs.maintainLayout && layoutPrefs.layout) {
                                    const layout = layoutPrefs.layout;
                                    // Defensive null checks
                                    // if (layout.mainbox) {
                                    //     if (typeof layout.mainbox.x === 'number') ikariam.mainbox_x = layout.mainbox.x;
                                    //     if (typeof layout.mainbox.y === 'number') ikariam.mainbox_y = layout.mainbox.y;
                                    //     if (typeof layout.mainbox.z === 'number') ikariam.mainbox_z = layout.mainbox.z;
                                    //     tnt.core.debug.log("Setting mainbox position to: " + ikariam.mainbox_x + ", " + ikariam.mainbox_y + ", " + ikariam.mainbox_z, 3);
                                    // }
                                    // if (layout.sidebar) {
                                    //     if (typeof layout.sidebar.x === 'number') ikariam.sidebar_x = layout.sidebar.x;
                                    //     if (typeof layout.sidebar.y === 'number') ikariam.sidebar_y = layout.sidebar.y;
                                    //     if (typeof layout.sidebar.z === 'number') ikariam.sidebar_z = layout.sidebar.z;
                                    //     tnt.core.debug.log("Setting sidebar position to: " + ikariam.sidebar_x + ", " + ikariam.sidebar_y + ", " + ikariam.sidebar_z, 3);
                                    // }
                                    // if (layout.citymap && typeof layout.citymap.zoom === 'number') {
                                    //     localStorage.setItem('cityWorldviewScale', layout.citymap.zoom.toString());
                                    // }
                                }
                            }
                        } catch (e) {
                            // Defensive: ignore errors
                        }

                        tnt.core.debug.log("changeView (View: " + view + ")", 3);

                        // Let Ikariam do its stuff
                        ajax.Responder.tntChangeView(response);

                        // Apply layout with inline styles after rendering
                        try {
                            if (ikariam.templateView && ikariam.templateView.id === "city") {
                                tnt.utils.applyLayoutDirectly();
                            }
                        } catch (e) { }

                        // Check notifications
                        tnt.core.notification.check();

                        tnt.core.debug.log("ikariam.templateView.id: '" + ikariam.templateView.id + "'", 3);
                        switch (ikariam.templateView.id) {
                            case "townHall":
                                if (!ikariam.backgroundView.screen.data.isCapital && $('#sidebarWidget .indicator').length > 1) {
                                    $('#sidebarWidget .indicator').last().trigger("click");
                                }
                                break;
                            case "tradeAdvisor":
                                $("#tradeAdvisor").children('div.contentBox01h').eq(1).hide();
                                break;
                            case "militaryAdvisor":
                                $("#militaryAdvisor").find('div.contentBox01h').eq(0).hide();
                                break;
                            case "researchAdvisor":
                                $("#researchAdvisor").find('div.contentBox01h').eq(1).hide();
                                break;
                            case "diplomacyAdvisor":
                                $("#tab_diplomacyAdvisor").find('div.contentBox01h').eq(2).hide();
                                break;
                            case "transport":
                                $('#setPremiumJetPropulsion').hide().prev().hide();
                                break;
                            case "resource":
                                $('#sidebarWidget .indicator').eq(1).trigger("click");
                                break;
                            case "merchantNavy":
                                setTimeout(() => {
                                    $('.pulldown .btn').trigger('click');
                                    pulldownAll();
                                    tnt.core.debug.log('btn');
                                }, 5000);
                                break;
                            case "plunder":
                            case "deployment":
                            case "plunderFleet":
                                // Wait for dialog to be ready
                                setTimeout(() => {
                                    // Select all units
                                    $('#selectArmy .assignUnits .setMax').trigger("click");
                                    $('#fleetDeploymentForm .setMax').trigger("click");

                                    // Set initial transporter count
                                    const freeTransporters = tnt.get.military.transporters.free();
                                    $('#extraTransporter').val(freeTransporters);

                                    // Prevent 0 transporters when min is clicked
                                    $('#selectArmy .assignUnits .setMin').on('click', function () {
                                        if (parseInt($('#extraTransporter').val()) === 0) {
                                            $('#extraTransporter').val(freeTransporters);
                                        }
                                    });
                                }, 1200);
                                break;
                        }

                        // Run tnt.all() to handle all common tasks
                        tnt.all();
                    }
                }
            }
        },

        options: {
            init() {
                if (tnt.settings.get("version") !== tnt.version) {
                    tnt.settings.initDefaults();
                }
                tnt.ui.showOptionsDialog();
            }
        }
    },


    // BEGIN: DO NOT MODIFY - Fixed logic
    // Legacy compatibility - Here all the communication with Ikariam is handled
    // Should only be changed by the core team
    // These has to work for the rest of the code to work properly. We keep them here so we only have to change them in one place.
    get: {
        // Player data
        player: {
            id: () => tnt.utils.safeGet(() => parseInt(ikariam.model.avatarId), 0),
            alliance: {
                id: () => tnt.utils.safeGet(() => parseInt(ikariam.model.avatarAllyId), 0),
                hasAlly: () => tnt.utils.safeGet(() => ikariam.model.hasAlly, false)
            },
            economy: {
                gold: () => tnt.utils.safeGet(() => parseInt(ikariam.model.gold), 0),
                ambrosia: () => tnt.utils.safeGet(() => ikariam.model.ambrosia, 0),
                income: () => tnt.utils.safeGet(() => ikariam.model.income, 0),
                upkeep: () => tnt.utils.safeGet(() => ikariam.model.upkeep, 0),
                scientistsUpkeep: () => tnt.utils.safeGet(() => ikariam.model.sciencetistsUpkeep, 0),
                godGoldResult: () => tnt.utils.safeGet(() => ikariam.model.godGoldResult, 0)
            },
            list: {
                cities: () => tnt.utils.safeGet(() => {
                    const cityList = {};
                    for (const key in ikariam.model.relatedCityData) {
                        if (key.startsWith("city_")) {
                            const cityId = key.replace("city_", "");
                            cityList[cityId] = {
                                name: ikariam.model.relatedCityData[key].name,
                                coordinates: ikariam.model.relatedCityData[key].coords
                            };
                        }
                    }
                    return cityList;
                }, {})
            }
        },

        // City data
        city: {
            id: () => {
                const urlParams = new URLSearchParams(window.location.search);
                const urlCityId = urlParams.get('cityId') || urlParams.get('currentCityId');
                if (urlCityId && urlCityId !== 'undefined') return urlCityId;

                try {
                    const modelCityId = ikariam.model.relatedCityData.selectedCity.replace(/[^\d-]+/g, "");
                    if (modelCityId && modelCityId !== 'undefined') return modelCityId;
                } catch (e) { }

                const menuCityId = $('#js_GlobalMenu_citySelect').attr('name');
                if (menuCityId && menuCityId !== 'undefined') return menuCityId;

                const cities = tnt.get.player.list.cities();
                const cityIds = Object.keys(cities);
                if (cityIds.length > 0) return cityIds[0];

                tnt.core.debug.warn('TNT: No valid city ID found', 3);
                return null;
            },
            name: (id) => tnt.utils.safeGet(() => {
                if (id) return ikariam.model.relatedCityData["city_" + id].name;
                return $("#citySelect option:selected").text().split("] ")[1];
            }, "Unknown City"),

            coords: () => $("#js_islandBreadCoords").text(),

            tradegood: () => tnt.utils.safeGet(() => ikariam.model.producedTradegood, 0),

            level: () => $("#js_CityPosition0Level").text(),

            resources: {
                wood: () => tnt.utils.safeGet(() => ikariam.model.currentResources.resource, 0),
                wine: () => tnt.utils.safeGet(() => ikariam.model.currentResources[1], 0),
                marble: () => tnt.utils.safeGet(() => ikariam.model.currentResources[2], 0),
                crystal: () => tnt.utils.safeGet(() => ikariam.model.currentResources[3], 0),
                sulfur: () => tnt.utils.safeGet(() => ikariam.model.currentResources[4], 0),
                population: () => tnt.utils.safeGet(() => ikariam.model.currentResources.population, 0),
                citizens: () => tnt.utils.safeGet(() => ikariam.model.currentResources.citizens, 0),
                max: () => tnt.utils.safeGet(() => ikariam.model.maxResources.resource, 0),
                wineSpending: () => tnt.utils.safeGet(() => ikariam.model.wineSpending, 0)
            },

            production: {
                resource: () => tnt.utils.safeGet(() => ikariam.model.resourceProduction, 0),
                tradegood: () => tnt.utils.safeGet(() => ikariam.model.tradegoodProduction, 0)
            }
        },

        // Military data
        military: {
            transporters: {
                free: () => tnt.utils.safeGet(() => ikariam.model.freeTransporters, 0),
                max: () => tnt.utils.safeGet(() => ikariam.model.maxTransporters, 0)
            }
        }
    },

    // is functions - Used to check various states
    is: {
        // Check if the current city is the player's own city
        ownCity: () => tnt.utils.safeGet(() => ikariam.model.isOwnCity, false)
    },

    // has functions - Used to check if certain features are available
    has: {
        construction: () => tnt.utils.hasConstruction()
    }

    // END: DO NOT MODIFY - Fixed logic
};

// Plugin system - Allows for modular extensions to TNT
tnt.plugins = [];
// Register a plugin with TNT
tnt.registerPlugin = function (plugin) {
    if (plugin?.name) {
        tnt.plugins.push(plugin);
        tnt.core.debug.log(`[TNT] Plugin registered: ${plugin.name}`, 2);
    } else {
        tnt.core.debug.warn('[TNT] Attempted to register unnamed plugin', 1);
    }
};

// UI module - handle all DOM manipulation and event binding
tnt.ui = {
    // Create and show the options dialog
    showOptionsDialog() {
        const optionsHtml = this.buildOptionsHtml();

        if ($('#tntOptions').length === 0) {
            $('li.serverTime').before(`
                <li>
                    <a id="tntOptionsLink" href="javascript:void(0);">TNT Options v${tnt.version}</a>
                    <div id="tntOptions" class="tntBox" style="display:none;">
                        ${optionsHtml}
                    </div>
                </li>
            `);
            tnt.events.attachOptionsEventHandlers();
        }
    },

    buildOptionsHtml() {
        const settings = tnt.settings.getFeatureSettings();
        const resourceSettings = tnt.settings.getResourceDisplaySettings();
        const layoutPrefs = tnt.settings.getLayoutPrefs();

        // Prepare extracted layout data display
        let layoutDataHtml = '';
        if (layoutPrefs.layout) {
            // Helper to flatten and format an object as key1:val1, key2:val2
            function fmt(obj) {
                if (!obj || typeof obj !== 'object') return '';
                return Object.entries(obj)
                    .map(([k, v]) => `${k}:${v}`)
                    .join(', ');
            }
            const citymap = fmt(layoutPrefs.layout.citymap);
            const mainbox = fmt(layoutPrefs.layout.mainbox);
            const sidebar = fmt(layoutPrefs.layout.sidebar);
            layoutDataHtml = `<div id="tntLayoutCurrentData" style="margin-top:5px;font-size:10px;color:#666;word-break:break-all;line-height:1.4;">
                <span><b>citymap</b>: ${citymap || '-'}</span><br/>
                <span><b>mainbox</b>: ${mainbox || '-'}</span><br/>
                <span><b>sidebar</b>: ${sidebar || '-'}</span>
            </div>`;
        }

        return `
            <div id="tntUpdateLine" align="center" style="padding-bottom:5px;">
                <a id="tntColUpgradeLink" href="" style="display:none;color:blue;font-size:12px;">
                    Version <span id="tntColVersion"></span> is available. Click here to update now!
                </a>
            </div>
            <div>
                <div class="tnt_left" style="float:left;width:50%;">
                    <legend>All:</legend>
                    ${this.createCheckbox('tntAllRemovePremiumOffers', 'Remove Premium Offers', settings.removePremiumOffers)}
                    ${this.createCheckbox('tntAllRemoveFooterNavigation', 'Remove footer navigation', settings.removeFooterNavigation)}
                    ${this.createCheckbox('tntAllChangeNavigationCoord', 'Make footer navigation coord input a number', settings.changeNavigationCoord)}
                </div>
                <div class="tnt_left" style="float:left;width:50%;">
                    <legend>Debug:</legend>
                    ${this.createCheckbox('tntDebugEnable', 'Enable debug logging', tnt.settings.get('debug')?.enable ?? true)}
                    <label for="tntDebugLevel" style="font-size:11px;">Log level:</label>
                    <select id="tntDebugLevel" style="font-size:11px;">
                        <option value="1"${tnt.settings.get('debug')?.level === 1 ? ' selected' : ''}>1 - Errors only</option>
                        <option value="2"${tnt.settings.get('debug')?.level === 2 ? ' selected' : ''}>2 - Important</option>
                        <option value="3"${tnt.settings.get('debug')?.level === 3 ? ' selected' : ''}>3 - Warnings</option>
                        <option value="4"${tnt.settings.get('debug')?.level === 4 ? ' selected' : ''}>4 - Verbose</option>
                    </select>
                </div>
                <div class="tnt_left" style="float:left;width:50%;">
                    <legend>Notifications:</legend>
                    ${this.createCheckbox('tntNotificationAdvisors', 'Show notifications from Advisors', settings.notificationAdvisors)}
                    ${this.createCheckbox('tntNotificationSound', 'Play sound with notifications from Advisors', settings.notificationSound)}
                </div>
                <div class="tnt_left" style="float:left;width:50%;">
                    <legend>Islands:</legend>
                    ${this.createCheckbox('tntIslandShowCityLvl', 'Show Town Levels on Islands', settings.showCityLvl)}
                </div>
                <div class="tnt_left" style="float:left;width:50%;">
                    <legend>City:</legend>
                    ${this.createCheckbox('tntCityRemoveFlyingShop', 'Remove flying shop', settings.removeFlyingShop)}
                    ${this.createCheckbox('tntCityShowResources', 'Show resources', resourceSettings.showResources)}
                    <div class="tnt_left" style="padding-left:20px;">
                        ${this.createCheckbox('tntCityShowResourcesPorpulation', 'Show population', resourceSettings.showPopulation)}
                        ${this.createCheckbox('tntCityShowResourcesCitizens', 'Show citizens', resourceSettings.showCitizens)}
                        ${this.createCheckbox('tntCityShowResourcesWoods', 'Show wood', resourceSettings.showWood)}
                        ${this.createCheckbox('tntCityShowResourcesWine', 'Show Wine', resourceSettings.showWine)}
                        ${this.createCheckbox('tntCityShowResourcesMarble', 'Show Marble', resourceSettings.showMarble)}
                        ${this.createCheckbox('tntCityShowResourcesCrystal', 'Show Crystal', resourceSettings.showCrystal)}
                        ${this.createCheckbox('tntCityShowResourcesSulfur', 'Show Sulfur', resourceSettings.showSulfur)}
                    </div>
                </div>
                <div class="tnt_left" style="float:left;width:50%;">
                    <legend>World Map:</legend>
                </div>
                <div class="tnt_left" style="float:left;width:50%;">
                    <legend>Layout:</legend>
                    ${this.createCheckbox('tntLayoutMaintain', 'Maintain layout from URL', layoutPrefs.maintainLayout)}
                    <div id="tntLayoutUrlSection" style="padding-left:20px;${layoutPrefs.maintainLayout ? '' : 'display:none;'}">
                        <label for="tntLayoutUrl" style="display:block;margin-top:5px;font-size:11px;">Paste Ikariam layout URL:</label>
                        <input id="tntLayoutUrl" type="text" style="width:90%;margin-top:2px;font-size:11px;" placeholder="https://s##-us.ikariam.gameforge.com/?view=city&..." />
                        ${layoutDataHtml}
                    </div>
                </div>
            </div>
            <div align="center" style="clear:both;">
                <input id="tntOptionsClose" type="button" class="button" value="Close and refresh" />
            </div>
        `;
    },

    createCheckbox(id, label, checked) {
        return `<input id="${id}" type="checkbox"${checked ? ' checked="checked"' : ''} /> ${label}<br/>`;
    },

    // Apply UI modifications based on settings
    applyUIModifications() {
        const settings = tnt.settings.getFeatureSettings();

        // Need delay to ensure elements are ready
        setTimeout(() => {
            if (settings.removeFooterNavigation) {
                $('div#footer').hide();
            }

            if (settings.removeFlyingShop && $("body").attr("id") === "city") {
                $('.premiumOfferBox').hide();
                $('#leftMenu .expandable.resourceShop, #leftMenu .expandable.slot1, #leftMenu .expandable.slot2').remove();
                $('#js_viewCityMenu').css({
                    'top': '195px'
                });
            }
        }, 200);
    }
};

// Utilities module
tnt.utils = {
    // Safe getter with error handling
    safeGet(getter, defaultValue = null) {
        try {
            return getter();
        } catch (e) {
            tnt.core.debug.log(`Error in safeGet: ${e.message}`);
            return defaultValue;
        }
    },

    // Returns true if any building in the city is currently under construction.
    hasConstruction() {
        return $('.constructionSite').length > 0;
    },

    // Calculates resource and tradegood production for a city over a given number of hours.
    // Returns an object with formatted string values for each resource.
    calculateProduction(cityID, hours) {
        const city = tnt.data.storage.city[cityID]; // Use new storage structure
        if (city && city.hasOwnProperty('resourceProduction') && city.hasOwnProperty('tradegoodProduction')) {
            return {
                wood: parseInt((city.resourceProduction * hours * 3600)).toLocaleString(),
                wine: city.producedTradegood == 1 ? (parseInt(city.tradegoodProduction * hours * 3600)).toLocaleString() : "0",
                marble: city.producedTradegood == 2 ? (parseInt(city.tradegoodProduction * hours * 3600)).toLocaleString() : "0",
                crystal: city.producedTradegood == 3 ? (parseInt(city.tradegoodProduction * hours * 3600)).toLocaleString() : "0",
                sulfur: city.producedTradegood == 4 ? (parseInt(city.tradegoodProduction * hours * 3600)).toLocaleString() : "0"
            };
        }

        if (!city) {
            tnt.core.debug.log(`City ID ${cityID} not found in storage`);
        } else {
            tnt.core.debug.log(`City ID ${cityID} missing production data (resourceProduction: ${city.resourceProduction}, tradegoodProduction: ${city.tradegoodProduction})`);
        }
        return { wood: "0", wine: "0", marble: "0", crystal: "0", sulfur: "0" };
    },

    // Extracts the building level from the element's CSS class (e.g., "level12").
    // Returns the level as a string or '?' if not found.
    extractLevelFromElement($element) {
        const classes = $element.attr('class') || '';
        const levelMatch = classes.match(/level(\d+)/);
        return levelMatch ? levelMatch[1] : '?';
    },

    // Creates a DOM element to visually display the city level.
    createLevelIndicator(level) {
        return $('<div class="tntLvl">' + level + '</div>');
    },

    // Check if current page is island view
    isIslandView() {
        return $("body").attr("id") === "island";
    },

    // Validate city element for level display
    validateCityElement($element) {
        // Check if element exists
        if ($element.length === 0) return false;

        // Check if already has level indicator
        if ($element.find('.tntLvl').length > 0) return false;

        // Check if it's actually a player city
        if (!$element.hasClass('city')) return false;

        return true;
    },

    // Iterate through city positions with callback
    iterateCityPositions(callback) {
        for (let i = 0; i <= 16; i++) {
            const $cityLocation = $(`#cityLocation${i}`);
            callback($cityLocation, i);
        }
    },

    // Displays level indicators for all player cities on the island view.
    // Skips non-city elements and avoids duplicate indicators.
    displayCityLevels() {
        // Only run on island view
        if (!this.isIslandView()) return;

        // Iterate through all city positions
        this.iterateCityPositions(($cityLocation, position) => {
            // Validate the city element
            if (!this.validateCityElement($cityLocation)) return;

            // Extract level from element
            const level = this.extractLevelFromElement($cityLocation);

            // Create and append level indicator
            const $levelIndicator = this.createLevelIndicator(level);
            $cityLocation.append($levelIndicator);
        });
    },

    // Building Detection Utilities

    // Extract position number from element ID
    extractPositionFromElement($element) {
        const posId = $element.attr('id');
        if (!posId) return null;
        const match = posId.match(/\d+$/);
        return match ? match[0] : null;
    },

    // Detect building type from CSS classes
    detectBuildingType($element) {
        const classes = ($element.attr('class') || '').split(/\s+/);
        return classes.find(c => validBuildingTypes.includes(c)) || null;
    },

    // Check if building is under construction
    isUnderConstruction($element) {
        return $element.hasClass('constructionSite');
    },

    // Extracts the current level, under construction, and upgradable state for a building element.
    // Handles multiple DOM patterns and fallback cases for robustness.
    extractBuildingLevel($element) {
        let level = 0;
        let position = $element.data('position');

        if (typeof position === 'undefined') {
            position = $element.data('id');
            if (typeof position === 'undefined') {
                const idAttr = $element.attr('id');
                const match = idAttr && idAttr.match(/(\d+)$/);
                if (match) position = match[1];
            }
        }

        const underConstruction = $element.hasClass('constructionSite');

        // Try direct level via #js_CityPositionXLevel
        let usedDirectLevel = false;
        if (typeof position !== 'undefined') {
            const $levelSpan = $("#js_CityPosition" + position + "Level");
            if ($levelSpan.length) {
                const txt = $levelSpan.text().trim();
                if (/^\d+$/.test(txt)) {
                    level = parseInt(txt, 10);
                    usedDirectLevel = true;
                }
            }
        }

        // If not found, try from .level span or class fallback
        if (!usedDirectLevel) {
            const $level = $element.find('.level');
            if ($level.length > 0) {
                const match = $level.text().match(/\d+/);
                if (match) level = parseInt(match[0], 10);
            } else {
                const classes = ($element.attr('class') || '').split(/\s+/);
                const levelClass = classes.find(c => c.startsWith('level'));
                if (levelClass) {
                    const match = levelClass.match(/\d+$/);
                    if (match) level = parseInt(match[0], 10);
                }
            }
        }

        // NEW: fallback if level is still 0 and it's under construction
        if (underConstruction && level <= 0 && typeof position !== 'undefined') {
            const $link = $("#js_CityPosition" + position + "Link");
            if ($link.length) {
                const m = $link.attr("title") && $link.attr("title").match(/\((\d+)\)/);
                if (m) level = parseInt(m[1], 10);
            }
        }

        // Check upgradable (scrollName green)
        let upgradable = false;
        if (typeof position !== 'undefined') {
            const $scrollName = $("#js_CityPosition" + position + "ScrollName");
            if ($scrollName.length && $scrollName.hasClass("green")) {
                upgradable = true;
            }
        }
        if (!upgradable && $element.find('.green').length > 0) {
            upgradable = true;
        }

        return {
            level,
            underConstruction,
            upgradable
        };
    },

    // Create building data object
    createBuildingData(position, buildingType, levelInfo) {
        return {
            position,
            level: levelInfo.level,
            name: buildingType,
            underConstruction: levelInfo.underConstruction,
            upgradable: levelInfo.upgradable // Store upgradable state
        };
    },

    // Adds or updates a building entry in the provided collection by building type and position.
    addBuildingToCollection(collection, buildingData) {
        const buildingType = buildingData.name;
        collection[buildingType] = collection[buildingType] || [];

        const existingIndex = collection[buildingType].findIndex(b => b.position === buildingData.position);
        if (existingIndex >= 0) {
            collection[buildingType][existingIndex] = buildingData;
        } else {
            collection[buildingType].push(buildingData);
        }
    },

    // Scans all building positions in the current city and returns a collection of detected buildings.
    // Ensures under-construction buildings are always included, even if level is 0.
    // Guarantees every building type is present in the result, even if not found.
    scanAllBuildings() {
        const $positions = $('div[id^="position"].building, div[id^="js_CityPosition"].building');
        if (!$positions.length) return { buildings: {}, hasConstruction: false };

        const foundBuildings = {};
        const hasAnyConstruction = this.hasConstruction();

        $positions.each((index, element) => {
            const $pos = $(element);
            const position = this.extractPositionFromElement($pos);
            if (!position) return;

            // Only allow Town Hall at position 0
            if (position == 0) {
                let level = 0;
                let underConstruction = $pos.hasClass('constructionSite');
                let upgradable = false;
                if (underConstruction) {
                    // Under construction: get level from the link's title
                    const $link = $("#js_CityPosition0Link");
                    if ($link.length) {
                        const m = $link.attr("title") && $link.attr("title").match(/\((\d+)\)/);
                        if (m) level = parseInt(m[1], 10);
                    }
                } else {
                    // Not under construction: get level from the visible span
                    const $levelSpan = $("#js_CityPosition0Level");
                    if ($levelSpan.length) {
                        const txt = $levelSpan.text().trim();
                        if (/^\d+$/.test(txt)) level = parseInt(txt, 10);
                    }
                    // Upgradable: check if the scroll name is green
                    const $scrollName = $("#js_CityPosition0ScrollName");
                    if ($scrollName.length && $scrollName.hasClass("green")) upgradable = true;
                }
                // Always save Town Hall if level > 0 or under construction
                if (level > 0 || underConstruction) {
                    const buildingData = {
                        position: 0,
                        level: level,
                        name: 'townHall',
                        underConstruction: underConstruction,
                        upgradable: upgradable
                    };
                    this.addBuildingToCollection(foundBuildings, buildingData);
                }
                // Do not allow any other building at position 0
                return;
            }

            // Default logic for all other buildings (never allow townHall at any other position)
            let buildingType = this.detectBuildingType($pos);
            if (buildingType === 'townHall') return;
            // Enhanced detection for construction sites
            const isUnderConstruction = $pos.hasClass('constructionSite');
            if (!buildingType && isUnderConstruction) {
                const $a = $pos.find('a[href*="view="]');
                if ($a.length > 0) {
                    const href = $a.attr('href');
                    const match = href && href.match(/view=([a-zA-Z]+)/);
                    if (match && match[1]) {
                        const viewName = match[1];
                        const def = (typeof TNT_BUILDING_DEFINITIONS !== 'undefined' ? TNT_BUILDING_DEFINITIONS : (window.TNT_BUILDING_DEFINITIONS || []))
                            .find(b => b.viewName === viewName);
                        buildingType = def ? def.key : null;
                    }
                }
            }
            if (!buildingType) return;

            const levelInfo = this.extractBuildingLevel($pos);

            // BUGFIX: Always include buildings under construction, regardless of level
            if (isUnderConstruction || levelInfo.level > 0) {
                // For buildings under construction with level 0, set level to 0 but mark as under construction
                if (isUnderConstruction && levelInfo.level <= 0) {
                    levelInfo.level = 0;
                }

                const buildingData = this.createBuildingData(position, buildingType, levelInfo);
                buildingData.underConstruction = isUnderConstruction; // Ensure this flag is always correct
                this.addBuildingToCollection(foundBuildings, buildingData);
            }
            // Skip buildings with level 0 that aren't under construction
            else if (levelInfo.level <= 0 && !isUnderConstruction) {
                return;
            }
        });

        // Ensure every building type is present in the collection, even if empty
        const buildingDefs = TNT_BUILDING_DEFINITIONS || [];
        buildingDefs.forEach(def => {
            if (!foundBuildings.hasOwnProperty(def.key)) {
                foundBuildings[def.key] = [];
            }
        });

        return {
            buildings: foundBuildings,
            hasConstruction: hasAnyConstruction
        };
    },

    // Attempts to switch to the specified city using several fallback methods.
    // Tries AJAX, dropdown, and direct URL navigation for maximum compatibility.
    switchToCity(cityId) {
        // tntConsole.log('[TNT] Utils switching to city:', cityId);

        // Try multiple methods to switch cities
        let switchSuccess = false;

        // Method 1: Direct ajaxHandlerCall (most reliable)
        try {
            if (typeof ajaxHandlerCall === 'function') {
                // console.log('[TNT] Utils using ajaxHandlerCall method');
                ajaxHandlerCall(`?view=city&cityId=${cityId}`);
                switchSuccess = true;
                return true;
            }
        } catch (e) {
            // console.log('[TNT] Utils ajaxHandlerCall failed:', e.message);
        }

        // Method 2: Try to find and trigger the city select dropdown change
        try {
            const $citySelect = $('#js_GlobalMenu_citySelect');
            if ($citySelect.length > 0) {
                // console.log('[TNT] Utils using city select dropdown method');
                $citySelect.val(cityId).trigger('change');
                switchSuccess = true;
                return true;
            }
        } catch (e) {
            // console.log('[TNT] Utils city select dropdown failed:', e.message);
        }

        // Method 3: Try the dropdown li click with more specific targeting
        try {
            const $cityOption = $(`#dropDown_js_citySelectContainer li[selectValue="${cityId}"]`);
            if ($cityOption.length > 0) {
                // console.log('[TNT] Utils using improved dropdown click method');

                // Get the select element that the dropdown controls
                const $select = $('#js_GlobalMenu_citySelect, #citySelect');
                if ($select.length > 0) {
                    // Update the select value first
                    $select.val(cityId);

                    // Then trigger the change event
                    $select.trigger('change');

                    // Also trigger a click on the option for good measure
                    $cityOption.trigger('click');

                    switchSuccess = true;
                    return true;
                }
            }
        } catch (e) {
            // console.log('[TNT] Utils improved dropdown method failed:', e.message);
        }

        // Method 4: Direct URL navigation (fallback)
        if (!switchSuccess) {
            // console.log('[TNT] Utils using URL navigation fallback');
            const currentUrl = new URL(window.location.href);
            currentUrl.searchParams.set('cityId', cityId);
            currentUrl.searchParams.set('currentCityId', cityId);
            window.location.href = currentUrl.toString();
            return true;
        }

        return false;
    },

    // Applies user-defined layout preferences to the city view using inline styles.
    // Only applies if layout maintenance is enabled and layout data is available.
    applyLayoutDirectly() {
        const layoutPrefs = tnt.settings.getLayoutPrefs();
        const layout = layoutPrefs.layout;

        // If the maintainLayout is not enabled or we don't have a layout, we can't apply it
        if (!layoutPrefs || !layoutPrefs.maintainLayout || !layout) return;

        // IMPORTANT: Enforce citymap position if enabled in settings. Do NOT modify or remove this! IT WORKS!
        if (layout.citymap) {
            const citymap = layout.citymap;
            if (citymap) {
                $('#worldmap').css({
                    top: citymap.top + 'px',
                    left: citymap.left + 'px',
                    transform: `scale(${citymap.zoom || 1})` // Apply zoom if available
                });
            }
        }

        // IMPORTANT: Enforce mainbox position if enabled in settings. Do NOT modify or remove this! IT WORKS!
        if (layout.mainbox) {
            const mainbox = layout.mainbox;
            if (ikariam && layout.maintainLayout && mainbox) {
                // Apply specific adjustments for Ikariam
                if (ikariam.mainbox_x !== mainbox.x) {
                    ikariam.mainbox_x = mainbox.x;
                }
                if (ikariam.mainbox_z !== mainbox.z) {
                    ikariam.mainbox_z = mainbox.z;
                }
            }
        }

        // IMPORTANT: Enforce sidebar position if enabled in settings. Do NOT modify or remove this! IT WORKS!
        if (layout.sidebar) {
            const sidebar = layout.sidebar;
            if (layout.maintainLayout && sidebar) {
                // Apply specific adjustments for Ikariam
                if (ikariam.sidebar_x !== sidebar.x) {
                    ikariam.sidebar_x = sidebar.x;
                }
                if (ikariam.sidebar_z !== sidebar.z) {
                    ikariam.sidebar_z = sidebar.z;
                }
            }
        }
    },

    buildingExistsInAnyCity(buildingKey, cities) {
        return Object.values(cities).some(city =>
            city.buildings && Array.isArray(city.buildings[buildingKey]) && city.buildings[buildingKey].length > 0
        );
    }
};

// Event module - handles all event bindings and interactions
tnt.events = {
    attachButtonEvents() {
        // Attach event handlers for minimize/maximize
        $('.tnt_panel_minimize_btn').off('click').on('click', function () {
            const $panel = $('#tnt_info_resources');
            const $btn = $(this);

            if ($panel.hasClass('minimized')) {
                $panel.removeClass('minimized');
                $btn.removeClass('tnt_foreward').addClass('tnt_back');
            } else {
                $panel.addClass('minimized');
                $btn.removeClass('tnt_back').addClass('tnt_foreward');
            }
        });

        // Attach event handlers for toggle between resources and buildings
        $('.tnt_table_toggle_btn').off('click').on('click', function () {
            const $resourceContent = $('#tnt_info_resources_content');
            const $buildingContent = $('#tnt_info_buildings_content');

            if ($resourceContent.is(':visible')) {
                $resourceContent.hide();
                $buildingContent.show();
                $(this).addClass('active');
            } else {
                $buildingContent.hide();
                $resourceContent.show();
                $(this).removeClass('active');
            }
        });

        // Attach event handlers for refresh button. citySwitcher.start() will handle the refresh logic
        $('.tnt_refresh_btn').off('click').on('click', function () {
            tnt.citySwitcher.start();
        });
    },

    // Attach event handlers for options dialog
    attachOptionsEventHandlers() {
        // Open/close dialog
        $("#tntOptionsLink").on("click", () => $("#tntOptions").slideToggle());
        $("#tntOptionsClose").on("click", () => {
            $("#tntOptions").slideToggle();
            location.reload();
        });

        // Setting change handlers
        const settingHandlers = {
            'tntAllRemovePremiumOffers': 'allRemovePremiumOffers',
            'tntAllRemoveFooterNavigation': 'allRemoveFooterNavigation',
            'tntAllChangeNavigationCoord': 'allChangeNavigationCoord',
            'tntIslandShowCityLvl': 'islandShowCityLvl',
            'tntCityRemoveFlyingShop': 'cityRemoveFlyingShop',
            'tntCityShowResources': 'cityShowResources',
            'tntCityShowResourcesPorpulation': 'cityShowResourcesPorpulation',
            'tntCityShowResourcesCitizens': 'cityShowResourcesCitizens',
            'tntCityShowResourcesWoods': 'cityShowResourcesWoods',
            'tntCityShowResourcesWine': 'cityShowResourcesWine',
            'tntCityShowResourcesMarble': 'cityShowResourcesMarble',
            'tntCityShowResourcesCrystal': 'cityShowResourcesCrystal',
            'tntCityShowResourcesSulfur': 'cityShowResourcesSulfur',
            'tntNotificationAdvisors': 'notificationAdvisors'
        };

        Object.entries(settingHandlers).forEach(([elementId, settingKey]) => {
            $(`#${elementId}`).on("change", () => tnt.settings.toggle(settingKey));
        });

        // Special handler for notification sound (different toggle logic)
        $("#tntNotificationSound").on("change", () => {
            tnt.settings.set("notificationSound", !tnt.settings.get("notificationSound"));
        });

        // Layout maintenance checkbox handler
        $("#tntLayoutMaintain").on("change", () => {
            const isChecked = $("#tntLayoutMaintain").is(':checked');
            const layoutPrefs = tnt.settings.getLayoutPrefs();

            if (isChecked) {
                layoutPrefs.maintainLayout = true;
                $("#tntLayoutUrlSection").show();
            } else {
                // Clear layout preferences when unchecked
                tnt.settings.clearLayoutPrefs();
                $("#tntLayoutUrlSection").hide();
            }

            if (isChecked) {
                tnt.settings.setLayoutPrefs(layoutPrefs);
            }
        });

        // Layout URL input handler
        $("#tntLayoutUrl").on("paste blur keypress", function (e) {
            // Handle paste, blur, or Enter key
            if (e.type === 'keypress' && e.which !== 13) return;

            setTimeout(() => {
                const url = $(this).val().trim();

                if (url && tnt.settings.isValidIkariamUrl(url)) {
                    const layout = tnt.settings.parseLayoutFromUrl(url);

                    if (layout) {
                        const layoutPrefs = {
                            maintainLayout: true,
                            url: url,
                            layout: layout
                        };

                        tnt.settings.setLayoutPrefs(layoutPrefs);

                        // Show extracted layout data in compact format
                        function fmt(obj) {
                            if (!obj || typeof obj !== 'object') return '';
                            return Object.entries(obj)
                                .map(([k, v]) => `${k}:${v}`)
                                .join(', ');
                        }
                        const citymap = fmt(layout.citymap);
                        const mainbox = fmt(layout.mainbox);
                        const sidebar = fmt(layout.sidebar);
                        const layoutDataHtml = `<div id="tntLayoutCurrentData" style="margin-top:5px;font-size:10px;color:#666;word-break:break-all;line-height:1.4;">
                            <span><b>citymap</b>: ${citymap || '-'}</span><br/>
                            <span><b>mainbox</b>: ${mainbox || '-'}</span><br/>
                            <span><b>sidebar</b>: ${sidebar || '-'}</span>
                        </div>`;
                        if ($("#tntLayoutCurrentData").length) {
                            $("#tntLayoutCurrentData").replaceWith(layoutDataHtml);
                        } else {
                            $("#tntLayoutUrlSection").append(layoutDataHtml);
                        }

                        // Clear the input after successful processing
                        $(this).val('');

                        tnt.core.debug.log(`[TNT] Layout preferences saved: ${JSON.stringify(layoutPrefs)}`, 2);
                    } else {
                        alert('Failed to parse layout parameters from URL');
                    }
                } else if (url) {
                    alert('Please enter a valid Ikariam URL');
                }
            }, 10);
        });

        // Debug toggle
        $('#tntDebugEnable').on('change', () => {
            const debug = tnt.settings.get('debug', { enable: true, level: 3 });
            debug.enable = $('#tntDebugEnable').is(':checked');
            tnt.settings.set('debug', debug);
        });

        // Debug level change
        $('#tntDebugLevel').on('change', () => {
            const debug = tnt.settings.get('debug', { enable: true, level: 3 });
            debug.level = parseInt($('#tntDebugLevel').val(), 10);
            tnt.settings.set('debug', debug);
        });
    },
};

// dataCollector = Collects and stores resource data
tnt.dataCollector = {
    update() {
        const currentCityId = tnt.get.city.id();

        // Skip data collection if no valid city ID
        if (!currentCityId || currentCityId === 'undefined') {
            return;
        }

        const isOwnCity = tnt.is.ownCity();

        if (isOwnCity) {
            this.collectOwnCityData(currentCityId);
        } else {
            this.collectForeignCityData(currentCityId);
        }

        // Update visual progress AFTER data collection with proper timing
        if (tnt.citySwitcher.isActive) {
            // console.log(`[TNT] Data collected for ${currentCityId} - scheduling visual update`);
            setTimeout(() => {
                tnt.citySwitcher.updateVisualProgress();
            }, 500);
        }
    },

    collectOwnCityData(currentCityId) {
        const prev = $.extend(true, {}, tnt.data.storage.city[currentCityId] || {});

        const cityData = {
            ...prev,
            name: tnt.get.city.name(currentCityId),
            buildings: {},
            cityIslandCoords: tnt.get.city.coords(),
            producedTradegood: parseInt(tnt.get.city.tradegood()),
            population: tnt.get.city.resources.population(),
            citizens: tnt.get.city.resources.citizens(),
            max: tnt.get.city.resources.max(),
            wood: tnt.get.city.resources.wood(),
            wine: tnt.get.city.resources.wine(),
            marble: tnt.get.city.resources.marble(),
            crystal: tnt.get.city.resources.crystal(),
            sulfur: tnt.get.city.resources.sulfur(),
            hasConstruction: false,
            cityLvl: tnt.get.city.level(),
            resourceProduction: tnt.get.city.production.resource(),
            tradegoodProduction: tnt.get.city.production.tradegood(),
            lastUpdate: Date.now(),
            isOwn: true
        };

        // Only update buildings when in city view
        if ($("body").attr("id") === "city") {
            const buildingData = tnt.utils.scanAllBuildings();
            cityData.buildings = buildingData.buildings;
            cityData.hasConstruction = buildingData.hasConstruction;
        } else {
            cityData.hasConstruction = prev.hasConstruction || false;
        }

        // Store in own city data
        tnt.data.storage.city[currentCityId] = cityData;
        tnt.core.storage.save();
        // tnt.dataCollector.show(); // Moved to tnt.core.init() to avoid double updates. Remove if not needed
    },

    collectForeignCityData(currentCityId) {
        // console.log('[TNT] Collecting foreign city data for:', currentCityId);

        const hasSpyAccess = $('.spy_warning').length > 0 || $('#js_spiesInsideText').length > 0;
        const ownerName = tnt.utils.safeGet(() => ikariam.backgroundView.screen.data.ownerName, 'Unknown');
        const ownerId = tnt.utils.safeGet(() => ikariam.backgroundView.screen.data.ownerId, 0);

        const foreignCityData = {
            cityId: currentCityId,
            name: tnt.utils.safeGet(() => ikariam.backgroundView.screen.data.name, 'Unknown City'),
            ownerName: ownerName,
            ownerId: parseInt(ownerId),
            cityIslandCoords: tnt.get.city.coords(),
            cityLvl: tnt.get.city.level(),
            producedTradegood: parseInt(tnt.get.city.tradegood()),
            hasSpyAccess: hasSpyAccess,
            buildings: {},
            lastUpdate: Date.now(),
            isOwn: false
        };

        // Collect visible building data
        if ($("body").attr("id") === "city") {
            const buildingData = tnt.utils.scanAllBuildings();
            foreignCityData.buildings = buildingData.buildings;
            foreignCityData.hasConstruction = buildingData.hasConstruction;
        }

        // Store in foreign city data
        tnt.data.storage.foreign[currentCityId] = foreignCityData;

        // Also store in spy data if we have spy access
        if (hasSpyAccess) {
            tnt.data.storage.spy[currentCityId] = foreignCityData;
            // console.log('[TNT] Stored spy data for city:', currentCityId);
        }

        tnt.core.storage.save();
        // console.log('[TNT] Foreign city data collected and stored');
    },

    show() {
        // Only show resource tables for own cities
        if (tnt.settings.getResourceDisplaySettings().showResources && $("body").attr("id") == "city" && tnt.is.ownCity()) {

            // Show resource tables
            if ($('#tnt_info_resources').length === 0) {
                $('body').append(tnt.template.resources);
            }

            // $('#tnt_info_resources_content').empty();
            // $('#tnt_info_buildings_content').empty();

            // Build and display the resource table
            const resourceTable = tnt.tableBuilder.buildTable('resources');
            $('#tnt_info_resources_content').html(resourceTable);

            // Build and display the buildings table
            const buildingTable = tnt.tableBuilder.buildTable('buildings');
            $('#tnt_info_buildings_content').html(buildingTable);

            // Create external controls (buttons) and attach event handlers
            this.createExternalControls();
            tnt.tableBuilder.attachEventHandlers(); // Is this needed here?
        }
    },

    createExternalControls() {
        // Only create if they don't exist yet
        if ($('.tnt_external_controls').length === 0) {
            const $externalControls = $('<div class="tnt_external_controls"></div>');

            // Left side buttons (Min/Max)
            const $leftButtons = $('<div class="tnt_left_buttons"></div>');
            $leftButtons.append('<span class="tnt_panel_minimize_btn tnt_back" title="Minimize/Maximize panel"></span>');

            // Right side buttons (Refresh, Toggle)
            const $rightButtons = $('<div class="tnt_right_buttons"></div>');
            $rightButtons.append('<span class="tnt_refresh_btn" title="Refresh all cities"></span>');
            $rightButtons.append('<span class="tnt_table_toggle_btn" title="Show buildings/resources"></span>');

            $externalControls.append($leftButtons);
            $externalControls.append($rightButtons);
            $('#tnt_info_resources').prepend($externalControls);

            // Attach event handlers for the new buttons
            tnt.events.attachButtonEvents();
        }
    },

    // NEW: Calculate totals across all cities
    calculateTotals() {
        let total = {
            population: 0,
            citizens: 0,
            wood: 0,
            wine: 0,
            marble: 0,
            crystal: 0,
            sulfur: 0
        };

        $.each(tnt.data.storage.city, function (cityID, cityData) {
            total.population += parseInt(cityData.population) || 0;
            total.citizens += parseInt(cityData.citizens) || 0;
            total.wood += cityData.wood || 0;
            total.wine += cityData.wine || 0;
            total.marble += cityData.marble || 0;
            total.crystal += cityData.crystal || 0;
            total.sulfur += cityData.sulfur || 0;
        });

        return total;
    },

    getMergedBuildingColumns(buildingColumns) {
        // Determine which building columns are used in any city
        const usedColumns = buildingColumns.filter(function (col) {
            const cities = Object.values(tnt.data.storage.city);
            if (col.key === 'palace' || col.key === 'palaceColony') {
                return cities.some(city =>
                    (city.buildings?.['palace']?.length > 0) ||
                    (city.buildings?.['palaceColony']?.length > 0)
                );
            }
            return cities.some(city => city.buildings?.[col.key]?.length > 0);
        });

        // Merge palace/palaceColony into a single column for display
        const mergedColumns = [];
        let seenPalace = false;
        usedColumns.forEach(function (col) {
            if ((col.key === 'palace' || col.key === 'palaceColony') && !seenPalace) {
                mergedColumns.push({
                    key: 'palaceOrColony',
                    name: 'Palace / Governor\'s Residence',
                    icon: '/cdn/all/both/img/city/palace_l.png',
                    icon2: '/cdn/all/both/img/city/palaceColony_l.png',
                    buildingId: 11,
                    helpId: 1
                });
                seenPalace = true;
            } else if (col.key !== 'palace' && col.key !== 'palaceColony') {
                mergedColumns.push(col);
            }
        });

        return mergedColumns;
    },

    calculateCategorySpans(mergedColumns) {
        // Dynamically generate buildingCategories from TNT_BUILDING_DEFINITIONS
        const buildingCategories = TNT_BUILDING_DEFINITIONS.reduce((acc, b) => {
            if (!acc[b.category]) acc[b.category] = [];
            acc[b.category].push(b.key);
            return acc;
        }, {});

        const categorySpans = {};
        mergedColumns.forEach(col => {
            for (let [category, buildings] of Object.entries(buildingCategories)) {
                if (buildings.includes(col.key) ||
                    (col.key === 'palaceOrColony' && (buildings.includes('palace') || buildings.includes('palaceColony')))) {
                    categorySpans[category] = (categorySpans[category] || 0) + 1;
                }
            }
        });

        return categorySpans;
    },

    // PHASE 1: Add calculateBuildingTotals helper
    calculateBuildingTotals(mergedColumns) {
        const totals = {};

        mergedColumns.forEach(col => {
            let total = 0;

            Object.values(tnt.data.storage.city || {}).forEach(city => {
                if (!city.buildings) return;

                if (col.key === 'palaceOrColony') {
                    const palace = city.buildings.palace || [];
                    const colony = city.buildings.palaceColony || [];
                    total += palace.reduce((sum, b) => sum + (b.level || 0), 0);
                    total += colony.reduce((sum, b) => sum + (b.level || 0), 0);
                } else {
                    const arr = city.buildings[col.key] || [];
                    total += arr.reduce((sum, b) => sum + (b.level || 0), 0);
                }
            });

            totals[col.key] = total;
        });

        return totals;
    },

    sortCities() {
        var list = {};
        var cities = tnt.data.storage.city || {};
        $.each(cities, (cityID, value) => {
            if (value && typeof value.producedTradegood !== 'undefined') {
                list[cityID] = value.producedTradegood;
            }
        });
        var order = { 2: 0, 1: 1, 3: 2, 4: 3 };
        return Object.keys(list).sort((a, b) => order[list[a]] - order[list[b]]);
    },

    checkMinMax(city, resource) {
        if (!tnt.settings.getResourceDisplaySettings().showResources || !city || !city.max) return '';
        var max = city.max, txt = '';
        switch (resource) {
            case 0: if (city.wood > max * .8) txt += ' tnt_storage_danger'; if (city.wood < 100000) txt += ' tnt_storage_min'; break;
            case 1: if (city.wine > max * .8) txt += ' tnt_storage_danger'; if (city.wine < 100000) txt += ' tnt_storage_min'; break;
            case 2: if (city.marble > max * .8) txt += ' tnt_storage_danger'; if (city.marble < 50000) txt += ' tnt_storage_min'; break;
            case 3: if (city.crystal > max * .8) txt += ' tnt_storage_danger'; if (city.crystal < 50000) txt += ' tnt_storage_min'; break;
            case 4: if (city.sulfur > max * .8) txt += ' tnt_storage_danger'; if (city.sulfur < 50000) txt += ' tnt_storage_min'; break;
        }
        return txt;
    },

    getIcon(resource) {
        switch (resource) {
            case 0: return '<img class="tnt_resource_icon" src="/cdn/all/both/resources/icon_wood.png">';
            case 1: return '<img class="tnt_resource_icon" src="/cdn/all/both/resources/icon_wine.png">';
            case 2: return '<img class="tnt_resource_icon" src="/cdn/all/both/resources/icon_marble.png">';
            case 3: return '<img class="tnt_resource_icon" src="/cdn/all/both/resources/icon_crystal.png">';
            case 4: return '<img class="tnt_resource_icon" src="/cdn/all/both/resources/icon_sulfur.png">';
            case 'population': return '<img class="tnt_resource_icon tnt_icon_po" src="//gf3.geo.gfsrv.net/cdn2f/6d077d68d9ae22f9095515f282a112.png" style="width: 10px !important;">';
            case 'citizens': return '<img class="tnt_resource_icon" src="/cdn/all/both/resources/icon_population.png">';
            default: return '';
        }
    }
};

// City switcher module - CLEANER debug version
tnt.citySwitcher = {
    isActive: false,
    startCityId: null,
    visitedCities: [],

    start() {
        this.startCityId = tnt.get.city.id();

        if (!this.startCityId) {
            // console.log('[TNT] Cannot start - no valid city ID detected');
            return;
        }

        this.isActive = true;
        this.visitedCities = [this.startCityId];

        tnt.settings.set("citySwitcherActive", true);
        tnt.settings.set("citySwitcherStartCity", this.startCityId);
        tnt.settings.set("citySwitcherVisited", this.visitedCities);

        // console.log(`[TNT] CitySwitcher STARTED from city: ${this.startCityId}`);

        // Update visual immediately for starting city
        this.updateVisualProgress();

        // Start with 1.5 second delay
        setTimeout(() => {
            this.nextCity();
        }, 1500);
    },

    nextCity() {
        const allCities = Object.keys(tnt.get.player.list.cities());
        // console.log(`[TNT] Looking for next city. Visited: [${this.visitedCities.join(', ')}]`);

        for (const cityId of allCities) {
            if (!this.visitedCities.includes(cityId)) {
                // console.log(`[TNT] Next city: ${cityId}`);
                this.switchToCity(cityId);
                return;
            }
        }

        // console.log('[TNT] All cities visited - ending cycle');
        this.end();
    },

    switchToCity(cityId) {
        // console.log(`[TNT] === SWITCHING TO CITY ${cityId} ===`);

        // Add to visited list BEFORE switching
        if (!this.visitedCities.includes(cityId)) {
            this.visitedCities.push(cityId);
            tnt.settings.set("citySwitcherVisited", this.visitedCities);
            // console.log(`[TNT] Visited list updated: [${this.visitedCities.join(', ')}]`);
        }

        return tnt.utils.switchToCity(cityId);
    },

    // Switch back to the starting city and update the states. Before resuming normal visual state
    end() {
        this.switchToCity(this.startCityId);
        this.isActive = false;
        tnt.settings.set("citySwitcherActive", false);

        // Restore normal state after final switch
        // setTimeout(() => {
        // console.log('[TNT] Restoring normal visual state');
        this.restoreNormalVisualState();
        // }, 2000);
    },

    updateVisualProgress() {
        // 
        if ($('#tnt_info_resources').is(':visible') && $("body").attr("id") === "city") {
            const resourceTable = tnt.tableBuilder.buildTable('resources');
            $('#tnt_info_resources_content').html(resourceTable);

            const buildingTable = tnt.tableBuilder.buildTable('buildings');
            $('#tnt_info_buildings_content').html(buildingTable);

            // Shouldn't need to reattach handlers here. We are going to move to a new city anyway.
            // tnt.tableBuilder.attachEventHandlers();
        }
    },

    restoreNormalVisualState() {
        // Restore normal visual state of the resources/buildings tables
        this.visitedCities = [];

        if ($('#tnt_info_resources').is(':visible') && $("body").attr("id") === "city") {
            const resourceTable = tnt.tableBuilder.buildTable('resources');
            $('#tnt_info_resources_content').html(resourceTable);

            const buildingTable = tnt.tableBuilder.buildTable('buildings');
            $('#tnt_info_buildings_content').html(buildingTable);

            tnt.tableBuilder.attachEventHandlers();
        }
    },

    checkAndContinue() {
        const isActive = tnt.settings.get("citySwitcherActive", false);

        if (isActive) {
            const visitedCities = tnt.settings.get("citySwitcherVisited", []);

            if (visitedCities.length > 1) {
                // console.log('[TNT] Continuing citySwitcher cycle');
                this.isActive = true;
                this.startCityId = tnt.settings.get("citySwitcherStartCity");
                this.visitedCities = visitedCities;

                this.updateVisualProgress();

                // 2 second delay between city switches
                setTimeout(() => {
                    this.nextCity();
                }, 100);
            } else {
                // Direct navigation detected - stopping citySwitcher
                this.isActive = false;
                tnt.settings.set("citySwitcherActive", false);
                this.restoreNormalVisualState();
            }
        }
    }
};

// Tooltip/Bubbletip Testing Module
tnt.tooltip = {
    // Initialize the tooltip system (ensure BubbleTips is ready)
    init() {
        if (typeof BubbleTips === 'undefined' || typeof BubbleTips.bindBubbleTip !== 'function') {
            tnt.core.debug.log('[TNT] BubbleTips system is not available');
            return false;
        }

        if (!BubbleTips.bubbleNode || !BubbleTips.infoNode) {
            BubbleTips.init?.();
        }

        // Ensure hover/info bubbles are non-interactive so they do not steal mouse events and cause flicker
        $(BubbleTips.bubbleNode).css('pointer-events', 'none');
        $(BubbleTips.infoNode).css('pointer-events', 'none');

        // Patch BubbleTips hover tooltip position to auto-flip above cursor when near viewport bottom
        if (!BubbleTips._tntTooltipAutoFlip) {
            const originalBindBubbleTip = BubbleTips.bindBubbleTip.bind(BubbleTips);
            BubbleTips.bindBubbleTip = function (location, type, html, n, target, minSize) {
                const result = originalBindBubbleTip(location, type, html, n, target, minSize);

                if (type === 13 && target) {
                    const $target = $(target);

                    // Remove the original BubbleTips mousemove for this target and use our own logic
                    $target.off('mousemove');
                    $target.off('mousemove.tnt_tooltip_auto_flip');

                    $target.on('mousemove.tnt_tooltip_auto_flip', function (event) {
                        if (!BubbleTips.infotip || !BubbleTips.infoNode) return;

                        const $tip = $(BubbleTips.infotip);
                        const tooltipWidth = $tip.outerWidth();
                        const tooltipHeight = $tip.outerHeight();
                        const scrollLeft = $(document).scrollLeft();
                        const scrollTop = $(window).scrollTop();
                        const winWidth = $(window).width();
                        const winHeight = $(window).height();

                        const pageX = event.pageX || event.clientX + scrollLeft;
                        const pageY = event.pageY || event.clientY + scrollTop;

                        const xOffset = Number(BubbleTips.offsetLeft || 0);
                        const yOffset = Number(BubbleTips.offsetTop || 0) + (window.isIE ? 10 : 0);
                        const aboveGap = 15; // keep a small gap when flipped above cursor

                        let left = pageX + xOffset;
                        let top = pageY + yOffset;

                        if (left + tooltipWidth - 20 > winWidth + scrollLeft) {
                            left = pageX - tooltipWidth + 20;
                        }
                        if (left < scrollLeft + 20) {
                            left = scrollLeft + 20;
                        }

                        if (top + tooltipHeight + 10 > winHeight + scrollTop) {
                            top = pageY - tooltipHeight - aboveGap;
                        }
                        if (top < scrollTop + 10) {
                            top = scrollTop + 10;
                        }

                        $(BubbleTips.infoNode).css({ top: top + 'px', left: left + 'px' });
                    });
                }

                return result;
            };

            BubbleTips._tntTooltipAutoFlip = true;
        }

        tnt.core.debug.log('[TNT] BubbleTips system is available and initialized');
        return true;
    },

    formatTemplateTooltip({ title, body }) {
        const titleHtml = title ? `<div style="font-weight:bold !important;color:#000 !important;font-size:12px;line-height:1.2;">${title}</div><div style="height:0.5px;min-height:0.5px;background:#000;margin:2px 0;line-height:0;overflow:hidden;"></div>` : '';
        const bodyHtml = body ? `<div style="font-size:12px;line-height:1.4;">${body}</div>` : '';
        return `<div>${titleHtml}${bodyHtml}</div>`;
    },

    // Bind a tooltip HTML to an element
    bindToElement($el, html) {
        if (!$el || $el.length === 0 || !html) return;

        $el.off('mouseover.tnt mouseout.tnt');

        const showTooltip = (element) => {
            try {
                BubbleTips.clear?.();
                BubbleTips.init?.();
                $(BubbleTips.infoNode).css({ 'z-index': '100000001', 'display': 'block' });
                BubbleTips.bindBubbleTip(6, 13, html, null, element, false);
            } catch (err) {
                tnt.core.debug.warn('TNT: Tooltip bind failed: ' + err, 2);
            }
        };

        const hideTooltip = () => BubbleTips.clear?.();

        $el.on('mouseover.tnt', function (event) {
            const related = event.relatedTarget;
            if (related && $(related).closest($el).length) return;
            showTooltip(this);
        });

        $el.on('mouseout.tnt', function (event) {
            const related = event.relatedTarget;
            if (related && $(related).closest($el).length) return;
            hideTooltip();
        });
    },

    // Bind a template tooltip to an element, filling in calculated values for resources/buildings
    bindTemplateTooltip($el, section, key, context = 'header') {
        if (!$el || $el.length === 0) return;

        // Helpers
        const replaceAll = (template, replacements) => {
            let out = template;
            Object.entries(replacements).forEach(([k, v]) => {
                out = out.split(`{${k}}`).join(v || '');
            });
            return out;
        };

        // Lookup template
        const template =
            TNT_TOOLTIP_TEMPLATES?.[section]?.[context]?.[key] ||
            TNT_TOOLTIP_TEMPLATES?.[section]?.[context]?.default ||
            TNT_TOOLTIP_TEMPLATES?.[section]?.[key] ||
            TNT_TOOLTIP_TEMPLATES?.[key];

        if (!template) {
            tnt.core.debug.log(`[TNT] No tooltip template found for section="${section}", context="${context}", key="${key}"`, 2);
            return;
        }

        const fillTemplate = (tpl, replacements = {}) => {
            const titleText = tpl.title ? replaceAll(tpl.title, replacements) : '';
            const bodyText = tpl.body ? replaceAll(tpl.body, replacements) : '';
            return { title: titleText, body: bodyText };
        };

        if (section === 'resource') {
            const $row = $el.closest('tr');
            const cityId = $el.data('city-id') || ($row.length ? $row.data('city-id') : null);
            if (!cityId) return;

            const prod1h = tnt.utils.calculateProduction(cityId, 1);
            const prod24h = tnt.utils.calculateProduction(cityId, 24);

            const storeCity = tnt.data.storage.city?.[cityId];
            const storeForeignCity = tnt.data.storage.foreign?.[cityId];
            const allCities = tnt.get.player.list.cities() || {};
            const cityName = storeCity?.name || storeCity?.cityName || storeForeignCity?.name || allCities?.[cityId]?.name || allCities?.[cityId]?.cityName || `City ${cityId}`;
            const cityValue = String(storeCity?.[key] ?? storeForeignCity?.[key] ?? '');

            const replacements = {
                '1hwood': prod1h.wood,
                '24hwood': prod24h.wood,
                '1hwine': prod1h.wine,
                '24hwine': prod24h.wine,
                '1hmarble': prod1h.marble,
                '24hmarble': prod24h.marble,
                '1hcrystal': prod1h.crystal,
                '24hcrystal': prod24h.crystal,
                '1hsulfur': prod1h.sulfur,
                '24hsulfur': prod24h.sulfur,
                'cityName': cityName,
                'value': cityValue
            };

            const display = fillTemplate(template, replacements);
            tnt.tooltip.bindToElement($el, tnt.tooltip.formatTemplateTooltip(display));
            return;
        }

        if (section === 'building') {
            const cityId = $el.data('city-id') || $el.closest('tr').data('city-id');
            const def = TNT_BUILDING_DEFINITIONS.find(d => d.key === key) || { name: key };
            const maxedLvl = tnt.settings.getMaxedLvl(key);
            const defaultMaxedLvl = def.maxedLvl || 0;

            let totalLevel = 0;
            Object.values(tnt.data.storage.city || {}).forEach(city => {
                if (!city.buildings) return;
                if (key === 'palaceOrColony') {
                    const palace = city.buildings.palace || [];
                    const colony = city.buildings.palaceColony || [];
                    totalLevel += palace.reduce((sum, b) => sum + (b.level || 0), 0);
                    totalLevel += colony.reduce((sum, b) => sum + (b.level || 0), 0);
                } else {
                    const arr = city.buildings[key] || [];
                    totalLevel += arr.reduce((sum, b) => sum + (b.level || 0), 0);
                }
            });

            const allCities = tnt.get.player.list.cities() || {};
            const city = tnt.data.storage.city?.[cityId] || tnt.data.storage.foreign?.[cityId] || {};
            const cityName = city.name || city.cityName || allCities?.[cityId]?.name || allCities?.[cityId]?.cityName || `City ${cityId}`;

            let levelSum = 0;
            let statusText = '-';
            if (context === 'cell') {
                let levels = [];
                if (key === 'palaceOrColony') {
                    levels = (city.buildings?.palace || []).concat(city.buildings?.palaceColony || []);
                } else {
                    levels = city.buildings?.[key] || [];
                }
                levelSum = levels.reduce((sum, b) => sum + (b.level || 0), 0);
                statusText = levels.some(b => b.underConstruction) ? '<span class="red">Under construction</span>' : levels.some(b => b.upgradable) ? '<span class="green">Upgradable</span>' : '-';
            }

            const replacements = {
                cityName,
                buildingName: def.name || key,
                levelSum: String(levelSum),
                statusText,
                totalLevel: String(totalLevel),
                maxedLvl: String(maxedLvl),
                defaultMaxedLvl: String(defaultMaxedLvl)
            };

            const display = fillTemplate(template, replacements);
            tnt.tooltip.bindToElement($el, tnt.tooltip.formatTemplateTooltip(display));
            return;
        }

        const display = tnt.tooltip.formatTemplateTooltip(template);
        tnt.tooltip.bindToElement($el, display);
    },

    // Attach tooltips to elements with class 'tnt_tooltip_target'
    attachTooltips() {
        if (!tnt.tooltip.init()) {
            tnt.core.debug.log('TNT: BubbleTips not available', 2);
            return;
        }

        if (BubbleTips.bubbleNode) {
            $(BubbleTips.bubbleNode).css('z-index', '100000001');
        }
        if (BubbleTips.infoNode) {
            $(BubbleTips.infoNode).css('z-index', '100000001');
        }

        const $containers = $('.tnt_tooltip_target');
        tnt.core.debug.log(`[TNT] Adding tooltips to ${$containers.length} elements`, 3);

        $containers.each(function () {
            const $container = $(this);
            const section = $container.data('tooltip-section') || ($container.data('resource') ? 'resource' : ($container.data('building-type') ? 'building' : null));
            const context = $container.data('tooltip-context') || 'header';
            const key = $container.data('resource') || $container.data('building-type');
            if (!section || !key) return;

            // For building header cells, also bind tooltip to inner link/image nodes so hovering them triggers the same tooltip.
            const $bindTargets = $container.add($container.find('a, img'));
            tnt.tooltip.bindTemplateTooltip($bindTargets, section, key, context);
        });
    },

    // NOT USED: Create a simple tooltip on an element - Kept for now
    // Types: 10 = success (green), 11 = info (yellow), 12 = error (red), 13 = hover tooltip
    create(element, text, type = 13) {
        if (!this.init()) {
            tnt.core.debug.warn('TNT: BubbleTips not available, cannot create tooltip', 3);
            return false;
        }

        try {
            // Type 13 = hover tooltip that follows mouse
            // Parameters: location(6=custom element), type(13=tooltip), text, null, element, minSize
            BubbleTips.bindBubbleTip(6, type, text, null, element, false);
            return true;
        } catch (e) {
            tnt.core.debug.error('TNT: Error creating tooltip: ' + e.message, 1);
            return false;
        }
    }
};

// Table builder - complete implementation matching working HTML structure
tnt.tableBuilder = {
    buildTable(type) {
        if (type === 'resources') {
            return this.buildResourceTable();
        } else if (type === 'buildings') {
            return this.buildBuildingTable();
        }
        return '';
    },

    // Build the buildings table
    buildResourceTable() {
        // Ensure we have the necessary data
        const cities = tnt.data.storage.city || {};
        const sortedCityIds = tnt.dataCollector.sortCities();
        const settings = tnt.settings.getResourceDisplaySettings();
        const currentCityId = tnt.get.city.id();

        // If no cities or no resources to display, return empty table
        if (sortedCityIds.length === 0) {
            return '<div>No city data available</div>';
        }

        // Calculate colspan for City and Resources columns, based on enabled settings
        let cityColspan = 1; // We start with 1 for the city name column, which is always shown
        if (settings.showPopulation) cityColspan++;
        if (settings.showCitizens) cityColspan++;

        let resourcesSpan = 0; // We start with 0 for the resources columns, which are all conditionally shown
        if (settings.showWood) resourcesSpan++;
        if (settings.showWine) resourcesSpan++;
        if (settings.showMarble) resourcesSpan++;
        if (settings.showCrystal) resourcesSpan++;
        if (settings.showSulfur) resourcesSpan++;

        // Build the HTML table structure
        let html = '<table id="tnt_resources_table" border="1" style="border-collapse:collapse;font:12px Arial,Helvetica,sans-serif;background-color:#fdf7dd;"><tbody>';

        // Category header row - NO CONTROLS TEXT
        html += '<tr class="tnt_category_header">';
        html += '<th class="tnt_category_header" style="background-color:#DBBE8C;border: 1px solid #000;padding:4px;font-weight:bold;text-align:center;width:60px;"></th>';
        html += `<th colspan="${cityColspan}" class="tnt_category_header" style="background-color:#DBBE8C;border: 1px solid #000;padding:4px;font-weight:bold;text-align:center;">City Info</th>`;
        if (resourcesSpan > 0) {
            html += `<th colspan="${resourcesSpan}" class="tnt_category_header" style="background-color:#DBBE8C;border: 1px solid #000;padding:4px;font-weight:bold;text-align:center;">Resources</th>`;
        }
        html += '</tr>';

        // Subcategory header row - COMPLETELY CLEAN with NO buttons whatsoever
        html += '<tr class="tnt_subcategory_header">';
        html += '<th class="tnt_center tnt_bold" style="position:relative;text-align:center;padding:4px;font-weight:bold;border:1px solid #000;background-color:#faeac6;">';
        html += '<div style="position:relative; min-width:120px; text-align:center;">';
        html += '<span style="display:inline-block; text-align:center; min-width:60px;">City</span>';
        html += '</div></th>';

        // Town Hall header
        html += '<th class="tnt_center tnt_bold" style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">';
        html += `<a href="#" onclick="ajaxHandlerCall('?view=buildingDetail&buildingId=0&helpId=1');return false;" title="Learn more about Town Hall...">`;
        html += '<img class="tnt_resource_icon tnt_building_icon" title="Town Hall" src="/cdn/all/both/img/city/townhall_l.png">';
        html += '</a></th>';

        // Optional columns
        if (settings.showPopulation) {
            html += '<th class="tnt_center" style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">';
            html += '<span class="tnt_tooltip_target" data-resource="population">' + tnt.dataCollector.getIcon('population') + '</span></th>';
        }
        if (settings.showCitizens) {
            html += '<th class="tnt_center" style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">';
            html += '<span class="tnt_tooltip_target" data-resource="citizens">' + tnt.dataCollector.getIcon('citizens') + '</span></th>';
        }
        if (settings.showWood) {
            html += '<th class="tnt_center" style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">';
            html += '<span class="tnt_tooltip_target" data-resource="wood">' + tnt.dataCollector.getIcon(0) + '</span></th>';
        }
        if (settings.showWine) {
            html += '<th class="tnt_center" style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">';
            html += '<span class="tnt_tooltip_target" data-resource="wine">' + tnt.dataCollector.getIcon(1) + '</span></th>';
        }
        if (settings.showMarble) {
            html += '<th class="tnt_center" style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">';
            html += '<span class="tnt_tooltip_target" data-resource="marble">' + tnt.dataCollector.getIcon(2) + '</span></th>';
        }
        if (settings.showCrystal) {
            html += '<th class="tnt_center" style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">';
            html += '<span class="tnt_tooltip_target" data-resource="crystal">' + tnt.dataCollector.getIcon(3) + '</span></th>';
        }
        if (settings.showSulfur) {
            html += '<th class="tnt_center" style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">';
            html += '<span class="tnt_tooltip_target" data-resource="sulfur">' + tnt.dataCollector.getIcon(4) + '</span></th>';
        }
        html += '</tr>';

        // Data rows
        sortedCityIds.forEach(cityId => {
            const city = cities[cityId];
            if (!city) return;

            const isCurrentCity = (cityId == currentCityId);
            const hasConstruction = city.hasConstruction;
            const isVisited = tnt.citySwitcher.isActive && tnt.citySwitcher.visitedCities.includes(cityId);
            const progressClass = this.getProgressClass(cityId, isCurrentCity, hasConstruction, isVisited);
            const rowClass = isCurrentCity ? ' class="tnt_selected"' : '';

            // DEBUG: Simple state logging
            if (tnt.citySwitcher.isActive) {
                // console.log(`[TNT] City ${cityId}: Current=${isCurrentCity}, Visited=${isVisited}, Class="${progressClass}"`);
            }

            html += `<tr data-city-id="${cityId}"${rowClass}>`;

            // City name cell with progress styling
            html += `<td class="tnt_city tnt_left${progressClass}" style="padding:4px;text-align:left;border:1px solid #000;background-color:#fdf7dd;">`;
            html += `<a href="#" class="tnt_city_link" data-city-id="${cityId}">`;
            html += tnt.dataCollector.getIcon(city.producedTradegood) + ' ' + tnt.get.city.name(cityId);
            html += '</a></td>';

            // Town Hall level
            let townHallLevel = '-';
            let townHallGreen = false;
            if (city.buildings && Array.isArray(city.buildings['townHall']) && city.buildings['townHall'].length > 0) {
                const arr = city.buildings['townHall'];
                townHallLevel = arr.reduce((acc, b) => acc + (parseInt(b.level) || 0), 0);
                if (arr.some(b => b.upgradable)) townHallGreen = true;
            }
            html += `<td class="tnt_building_level${townHallGreen ? ' green' : ''}" style="padding:4px;text-align:center;border:1px solid #000;background-color:#fdf7dd;">${townHallLevel}</td>`;

            // Optional data columns
            if (settings.showPopulation) {
                const val = parseInt(Math.round(city.population)).toLocaleString();
                html += `<td class="tnt_population" style="padding:4px;text-align:right;border:1px solid #000;background-color:#fdf7dd;">${val}</td>`;
            }
            if (settings.showCitizens) {
                const val = parseInt(Math.round(city.citizens)).toLocaleString();
                html += `<td class="tnt_citizens" style="padding:4px;text-align:right;border:1px solid #000;background-color:#fdf7dd;">${val}</td>`;
            }
            if (settings.showWood) {
                const cssClass = tnt.dataCollector.checkMinMax(city, 0);
                const production = tnt.utils.calculateProduction(cityId, 24).wood;
                html += `<td class="tnt_wood${cssClass}" style="padding:4px;text-align:right;border:1px solid #000;background-color:#fdf7dd;"><span title="${production}">${city.wood.toLocaleString()}</span></td>`;
            }
            if (settings.showWine) {
                const cssClass = tnt.dataCollector.checkMinMax(city, 1);
                const production = tnt.utils.calculateProduction(cityId, 24).wine;
                const fontWeight = city.producedTradegood == 1 ? 'font-weight:bold;color:black;' : '';
                html += `<td class="tnt_wine${cssClass}" style="padding:4px;text-align:right;border:1px solid #000;background-color:#fdf7dd;${fontWeight}"><span title="${production}">${city.wine.toLocaleString()}</span></td>`;
            }
            if (settings.showMarble) {
                const cssClass = tnt.dataCollector.checkMinMax(city, 2);
                const production = tnt.utils.calculateProduction(cityId, 24).marble;
                const fontWeight = city.producedTradegood == 2 ? 'font-weight:bold;color:black;' : '';
                html += `<td class="tnt_marble${cssClass}" style="padding:4px;text-align:right;border:1px solid #000;background-color:#fdf7dd;${fontWeight}"><span title="${production}">${city.marble.toLocaleString()}</span></td>`;
            }
            if (settings.showCrystal) {
                const cssClass = tnt.dataCollector.checkMinMax(city, 3);
                const production = tnt.utils.calculateProduction(cityId, 24).crystal;
                const fontWeight = city.producedTradegood == 3 ? 'font-weight:bold;color:black;' : '';
                html += `<td class="tnt_crystal${cssClass}" style="padding:4px;text-align:right;border:1px solid #000;background-color:#fdf7dd;${fontWeight}"><span title="${production}">${city.crystal.toLocaleString()}</span></td>`;
            }
            if (settings.showSulfur) {
                const cssClass = tnt.dataCollector.checkMinMax(city, 4);
                const production = tnt.utils.calculateProduction(cityId, 24).sulfur;
                const fontWeight = city.producedTradegood == 4 ? 'font-weight:bold;color:black;' : '';
                html += `<td class="tnt_sulfur${cssClass}" style="padding:4px;text-align:right;border:1px solid #000;background-color:#fdf7dd;${fontWeight}"><span title="${production}">${city.sulfur.toLocaleString()}</span></td>`;
            }

            html += '</tr>';
        });

        // Totals row
        const totals = tnt.dataCollector.calculateTotals();
        html += '<tr>';
        html += '<td class="tnt_total" style="padding:4px;text-align:left;border:1px solid #000;background-color:#faeac6;font-weight:bold;">Total</td>';
        html += '<td style="padding:4px;text-align:center;border:1px solid #000;background-color:#faeac6;"></td>';

        if (settings.showPopulation) {
            html += `<td class="tnt_total" style="padding:4px;text-align:right;border:1px solid #000;background-color:#faeac6;font-weight:bold;">${totals.population.toLocaleString()}</td>`;
        }
        if (settings.showCitizens) {

            html += `<td class="tnt_total" style="padding:4px;text-align:right;border:1px solid #000;background-color:#faeac6;font-weight:bold;">${totals.citizens.toLocaleString()}</td>`;
        }
        if (settings.showWood) {
            html += `<td class="tnt_total" style="padding:4px;text-align:right;border:1px solid #000;background-color:#faeac6;font-weight:bold;">${totals.wood.toLocaleString()}</td>`;
        }
        if (settings.showWine) {
            html += `<td class="tnt_total" style="padding:4px;text-align:right;border:1px solid #000;background-color:#faeac6;font-weight:bold;">${totals.wine.toLocaleString()}</td>`;
        }
        if (settings.showMarble) {
            html += `<td class="tnt_total" style="padding:4px;text-align:right;border:1px solid #000;background-color:#faeac6;font-weight:bold;">${totals.marble.toLocaleString()}</td>`;
        }
        if (settings.showCrystal) {
            html += `<td class="tnt_total" style="padding:4px;text-align:right;border:1px solid #000;background-color:#faeac6;font-weight:bold;">${totals.crystal.toLocaleString()}</td>`;
        }
        if (settings.showSulfur) {
            html += `<td class="tnt_total" style="padding:4px;text-align:right;border:1px solid #000;background-color:#faeac6;font-weight:bold;">${totals.sulfur.toLocaleString()}</td>`;
        }
        html += '</tr>';

        html += '</tbody></table>';
        return html;
    },

    buildBuildingTable() {
        const cities = tnt.data.storage.city || {};
        const sortedCityIds = tnt.dataCollector.sortCities();
        const currentCityId = tnt.get.city.id();
        const buildingDefs = TNT_BUILDING_DEFINITIONS;
        const mergedColumns = tnt.dataCollector.getMergedBuildingColumns(buildingDefs);
        const categorySpans = tnt.dataCollector.calculateCategorySpans(mergedColumns);

        if (sortedCityIds.length === 0) {
            return `<div>No city data available</div>`;
        }

        let html = `<table id="tnt_buildings_table" border="1" style="border-collapse:collapse;font:12px Arial,Helvetica,sans-serif;background-color:#fdf7dd;"><tbody>`;

        // Category header row
        html += `<tr class="tnt_category_header">`;
        html += `<th class="tnt_category_header" style="background-color:#DBBE8C;border: 1px solid #000;padding:4px;font-weight:bold;text-align:center;width:60px;"></th>`;
        // html += `<th class="tnt_category_header" style="background-color:#DBBE8C;border: 1px solid #000;padding:4px;font-weight:bold;text-align:center;">City</th>`;
        Object.entries(categorySpans).forEach(([category, span]) => {
            if (span > 0) {
                let displayName = category.replace(/([A-Z])/g, ' $1')
                    .split(' ')
                    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
                    .join(' ');
                html += `<th colspan="${span}" class="tnt_category_header" style="background-color:#DBBE8C;border: 1px solid #000;padding:4px;font-weight:bold;text-align:center;">${displayName}</th>`;
            }
        });
        html += `</tr>`;

        // Subcategory header row
        html += `<tr class="tnt_subcategory_header">`;
        html += `<th class="tnt_center tnt_bold" style="position:relative;text-align:center;padding:4px;font-weight:bold;border:1px solid #000;background-color:#faeac6;">`;
        html += `<div style="position:relative; min-width:120px; text-align:center;">`;
        html += `<span style="display:inline-block; text-align:center; min-width:60px;">City</span>`;
        html += `</div></th>`;

        // Building column headers
        mergedColumns.forEach(building => {
            const thAttrs = `class="tnt_center tnt_bold tnt_tooltip_target" data-tooltip-section="building" data-tooltip-context="header" data-building-type="${building.key}"`;
            if (building.key === 'palaceOrColony') {
                html += `<th ${thAttrs} style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">`;
                html += `<a href="#" onclick="ajaxHandlerCall('?view=buildingDetail&buildingId=11&helpId=1');return false;">`;
                html += `<img class="tnt_resource_icon tnt_building_icon tnt_tooltip_target" src="${building.icon}" alt="Palace" data-tooltip-section="building" data-tooltip-context="header" data-building-type="${building.key}">`;
                html += `</a>`;
                html += `<a href="#" onclick="ajaxHandlerCall('?view=buildingDetail&buildingId=17&helpId=1');return false;">`;
                html += `<img class="tnt_resource_icon tnt_building_icon tnt_tooltip_target" src="${building.icon2}" alt="Governor's Residence" data-tooltip-section="building" data-tooltip-context="header" data-building-type="${building.key}">`;
                html += `</a></th>`;
            } else {
                html += `<th ${thAttrs} style="padding:4px;text-align:center;font-weight:bold;border:1px solid #000;background-color:#faeac6;">`;
                html += `<a href="#" onclick="ajaxHandlerCall('?view=buildingDetail&buildingId=${building.buildingId}&helpId=${building.helpId}');return false;">`;
                html += `<img class="tnt_resource_icon tnt_building_icon tnt_tooltip_target" src="${building.icon}" alt="${building.name}" data-tooltip-section="building" data-tooltip-context="header" data-building-type="${building.key}">`;
                html += `</a></th>`;
            }
        });
        html += `</tr>`;

        // Data rows
        sortedCityIds.forEach(cityId => {
            const city = cities[cityId];
            if (!city) return;

            // Determine city state
            const isCurrentCity = (cityId == currentCityId);
            const hasConstruction = city.hasConstruction;
            const isVisited = tnt.citySwitcher.isActive && tnt.citySwitcher.visitedCities.includes(cityId);
            const progressClass = this.getProgressClass(cityId, isCurrentCity, hasConstruction, isVisited);
            const rowClass = isCurrentCity ? ' class="tnt_selected"' : '';

            html += `<tr data-city-id="${cityId}"${rowClass}>`;

            // City name cell with progress styling
            html += `<td class="tnt_city tnt_left${progressClass}" style="padding:4px;text-align:left;border:1px solid #000;background-color:#fdf7dd;">`;
            html += `<a href="#" class="tnt_city_link" data-city-id="${cityId}">`;
            html += tnt.dataCollector.getIcon(city.producedTradegood) + ' ' + tnt.get.city.name(cityId);
            html += '</a></td>';

            // Add building level cells for each merged column
            mergedColumns.forEach(building => {
                const buildingArray = city.buildings?.[building.key] || [];

                // Special merge handling for palace + palaceColony
                if (building.key === 'palaceOrColony') {
                    const palace = city.buildings?.palace || [];
                    const colony = city.buildings?.palaceColony || [];
                    const merged = palace.concat(colony);
                    html += tnt.tableBuilder.renderBuildingLevelCell(merged, building.key, cityId);
                } else {
                    html += tnt.tableBuilder.renderBuildingLevelCell(buildingArray, building.key, cityId);
                }
            });

            html += '</tr>';
        });

        // Total row with building level totals
        html += '<tr>';
        html += '<td class="tnt_total" style="padding:4px;text-align:left;border:1px solid #000;background-color:#faeac6;font-weight:bold;">Total</td>';
        const buildingTotals = tnt.dataCollector.calculateBuildingTotals(mergedColumns);
        mergedColumns.forEach(col => {
            const total = buildingTotals[col.key] || '';
            html += `<td class="tnt_building_level" style="padding:4px;text-align:center;border:1px solid #000;background-color:#faeac6;font-weight:bold;">${total}</td>`;
        });
        html += '</tr>';

        html += '</tbody></table>';
        return html;
    },

    renderBuildingLevelCell(buildingArray, buildingType, cityId) {
        let tdClass = "tnt_building_level";
        let bgColor = "#fdf7dd";
        let tooltip = "";
        let levelSum = 0;
        let hasConstruction = false;
        let upgradable = false;

        if (!Array.isArray(buildingArray) || buildingArray.length === 0) {
            return `<td class="${tdClass} tnt_tooltip_target" data-tooltip-section="building" data-tooltip-context="cell" data-building-type="${buildingType}" data-city-id="${cityId}" style="padding:4px;text-align:center;border:1px solid #000;background-color:${bgColor};"></td>`;
        }

        const buildingDef = TNT_BUILDING_DEFINITIONS.find(def => def.key === buildingType);

        buildingArray.forEach(b => {
            const lvl = typeof b.level === 'number' ? b.level : 0;
            levelSum += lvl;
            if (b.underConstruction) hasConstruction = true;
            if (b.upgradable) upgradable = true;
            const upgradeNote = b.underConstruction ? ` (Upgrading to ${lvl + 1})` : "";
            tooltip += `Pos ${b.position}: lvl ${lvl}${upgradeNote}\n`;
        });

        const maxedLvl = tnt.settings.getMaxedLvl(buildingType);
        if (maxedLvl && levelSum >= maxedLvl * buildingArray.length) {
            tdClass += " tnt_building_maxed";
        }
        if (upgradable) tdClass += " green";
        if (hasConstruction) bgColor = "#80404050";

        return `<td class="${tdClass} tnt_tooltip_target" data-tooltip-section="building" data-tooltip-context="cell" data-building-type="${buildingType}" data-city-id="${cityId}" style="padding:4px;text-align:center;border:1px solid #000;background-color:${bgColor};" title="${tooltip.trim().replace(/"/g, '&quot;')}">${levelSum > 0 ? levelSum : '0'}</td>`;
    },

    // Visual progress class determination
    getProgressClass(cityId, isCurrentCity, hasConstruction, isVisited) {
        if (!tnt.citySwitcher.isActive) {
            return hasConstruction ? ' tnt_construction' : '';
        }

        if (isCurrentCity) {
            return hasConstruction ? ' tnt_construction' : '';
        } else if (isVisited) {
            return ' tnt_progress_visited';
        } else {
            return hasConstruction ? ' tnt_construction' : '';
        }
    },

    attachEventHandlers() {
        // City switching event handlers using proper Ikariam method
        $(document).off('click', '.tnt_city_link').on('click', '.tnt_city_link', function (event) {
            event.preventDefault();
            event.stopPropagation();

            // console.log('[TNT] City link clicked!');

            const cityId = $(this).data('city-id');
            // console.log('[TNT] Switching to city:', cityId);

            // Try multiple methods to switch cities
            let switchSuccess = false;

            // Method 1: Direct ajaxHandlerCall (most reliable)
            try {
                if (typeof ajaxHandlerCall === 'function') {
                    // console.log('[TNT] Using ajaxHandlerCall method');
                    ajaxHandlerCall(`?view=city&cityId=${cityId}`);
                    switchSuccess = true;
                    return false;
                }
            } catch (e) {
                // console.log('[TNT] ajaxHandlerCall failed:', e.message);
            }

            // Method 2: Try to find and trigger the city select dropdown change
            try {
                const $citySelect = $('#js_GlobalMenu_citySelect');
                if ($citySelect.length > 0) {
                    // console.log('[TNT] Using city select dropdown method');
                    $citySelect.val(cityId).trigger('change');
                    switchSuccess = true;
                    return false;
                }
            } catch (e) {
                // console.log('[TNT] City select dropdown failed:', e.message);
            }

            // Method 3: Try the dropdown li click with more specific targeting
            try {
                const $cityOption = $(`#dropDown_js_citySelectContainer li[selectValue="${cityId}"]`);
                if ($cityOption.length > 0) {
                    // console.log('[TNT] Using improved dropdown click method');

                    // Get the select element that the dropdown controls
                    const $select = $('#js_GlobalMenu_citySelect, #citySelect');
                    if ($select.length > 0) {
                        // Update the select value first
                        $select.val(cityId);

                        // Then trigger the change event
                        $select.trigger('change');

                        // Also trigger a click on the option for good measure
                        $cityOption.trigger('click');

                        switchSuccess = true;
                        return false;
                    }
                }
            } catch (e) {
                // console.log('[TNT] Improved dropdown method failed:', e.message);
            }

            // Method 4: Direct URL navigation (fallback)
            if (!switchSuccess) {
                // console.log('[TNT] Using URL navigation fallback');
                const currentUrl = new URL(window.location.href);
                currentUrl.searchParams.set('cityId', cityId);
                currentUrl.searchParams.set('currentCityId', cityId);
                window.location.href = currentUrl.toString();
            }

            return false;
        });

        // Also add direct click handlers to newly created elements
        $('.tnt_city_link').off('click').on('click', function (event) {
            event.preventDefault();
            event.stopPropagation();

            // console.log('[TNT] Direct city link clicked!');

            const cityId = $(this).data('city-id');
            // console.log('[TNT] Direct switching to city:', cityId);

            // Use the same improved switching logic
            let switchSuccess = false;

            // Method 1: Direct ajaxHandlerCall
            try {
                if (typeof ajaxHandlerCall === 'function') {
                    // console.log('[TNT] Direct using ajaxHandlerCall method');
                    ajaxHandlerCall(`?view=city&cityId=${cityId}`);
                    switchSuccess = true;
                    return false;
                }
            } catch (e) {
                // console.log('[TNT] Direct ajaxHandlerCall failed:', e.message);
            }

            // Method 2: City select dropdown
            try {
                const $citySelect = $('#js_GlobalMenu_citySelect');
                if ($citySelect.length > 0) {
                    // console.log('[TNT] Direct using city select dropdown method');
                    $citySelect.val(cityId).trigger('change');
                    switchSuccess = true;
                    return false;
                }
            } catch (e) {
                // console.log('[TNT] Direct city select dropdown failed:', e.message);
            }

            // Method 3: URL navigation fallback
            if (!switchSuccess) {
                // console.log('[TNT] Direct using URL navigation fallback');
                const currentUrl = new URL(window.location.href);
                currentUrl.searchParams.set('cityId', cityId);
                currentUrl.searchParams.set('currentCityId', cityId);
                window.location.href = currentUrl.toString();
            }

            return false;
        });

        // Double-click to edit global maxed level per building type in the table
        $(document).off('dblclick', '#tnt_buildings_table td.tnt_building_level').on('dblclick', '#tnt_buildings_table td.tnt_building_level', function () {
            const $cell = $(this);
            if ($cell.find('input').length > 0) return;

            const buildingType = $cell.data('building-type');
            if (!buildingType) return;

            const originalValue = $cell.text().trim();
            const initialValue = (buildingType === 'palaceOrColony') ? tnt.settings.getMaxedLvl('palaceOrColony') : originalValue;
            const input = $('<input type="text" class="tnt_maxedlvl_input" />').val(initialValue).css({
                width: '30px',
                boxSizing: 'border-box',
                margin: 0,
                padding: '0 2px',
                border: '1px solid #999',
                lineHeight: '1.2em',
                fontSize: '11px',
                textAlign: 'center'
            });
            $cell.empty().css({overflow: 'hidden', padding: '0 2px'}).append(input);
            input.focus().select();

            let saved = false;

            const finish = () => {
                if (!saved) {
                    $cell.text(originalValue);
                }
            };

            input.on('keydown', (e) => {
                if (e.key === 'Enter') {
                    const entered = input.val().trim();
                    if (entered === '') {
                        if (buildingType === 'palaceOrColony') {
                            tnt.settings.resetMaxedLvl('palace');
                            tnt.settings.resetMaxedLvl('palaceColony');
                            tnt.settings.resetMaxedLvl('palaceOrColony');
                        } else {
                            tnt.settings.resetMaxedLvl(buildingType);
                        }
                        saved = true;
                        tnt.dataCollector.show();
                        return;
                    }

                    const newValue = parseInt(entered, 10);
                    if (isNaN(newValue) || newValue < 0) {
                        alert('Please enter a positive integer for maxed level.');
                        input.focus().select();
                        return;
                    }
                    if (buildingType === 'palaceOrColony') {
                        tnt.settings.setMaxedLvl('palace', newValue);
                        tnt.settings.setMaxedLvl('palaceColony', newValue);
                        tnt.settings.setMaxedLvl('palaceOrColony', newValue);
                    } else {
                        tnt.settings.setMaxedLvl(buildingType, newValue);
                    }
                    saved = true;
                    tnt.dataCollector.show();
                } else if (e.key === 'Escape') {
                    saved = false;
                    finish();
                }
            });

            input.on('blur', finish);
        });

        // Add tooltips to resource icons
        tnt.tooltip.attachTooltips(); // Not sure where to move this. One run once after tables has been build!
    }
};

// Initialize the TNT core
$(document).ready(() => tnt.core.init());

// Apply styles at the end
GM_addStyle(`
    /* Show level styles - using table background color */
    .tntLvl{
        position: absolute !important;
        top: 32px !important;
        left: 44px !important;
        color: #000 !important;
        line-height: 16px !important;
        background-color: #DBBE8C !important;
        font-size: 9px !important;
        font-weight: bold !important;
        text-align: center !important;
        vertical-align: middle !important;
        height: 16px !important;
        width: 16px !important;
        border-radius: 50% !important;
        border: 1px solid #000 !important;
        display: inline-block !important;
        box-shadow: 0 1px 2px rgba(0,0,0,0.3) !important;
        z-index: 1000 !important;
        pointer-events: none !important;
    }
    .tntLvl:hover {
        background-color: #faeac6 !important;
        transform: scale(1.05) !important;
        transition: all 0.2s ease !important;
    }
    /* TNT table styles with higher specificity - override Ikariam's .table01 styles */
    body #tnt_info_resources #tnt_resources_table,
    body #tnt_info_buildings_content #tnt_buildings_table{
        border-collapse: collapse !important;
        font: 12px Arial, Helvetica, sans-serif !important;
        background-color: #fdf7dd !important;
        table-layout: fixed !important;
    }
    
    /* Category header cells - CLEAN and SIMPLE with no internal elements */
    body #tnt_info_resources #tnt_resources_table th.tnt_category_header,
    body #tnt_info_buildings_content #tnt_buildings_table th.tnt_category_header {
        height: 25px !important;
        max-height: 25px !important;
        min-height: 25px !important;
        background-color: #DBBE8C !important;
        border: 1px solid #000 !important;
        padding: 4px !important;
        font-weight: bold !important;
        text-align: center !important;
        box-sizing: border-box !important;
        line-height: 17px !important;
        font-size: 12px !important;
        vertical-align: middle !important;
    }
    
    /* External control buttons container - positioned OUTSIDE table, overlaying */
    .tnt_external_controls {
        position: absolute !important;
        top: 2px !important;
        left: 2px !important;
        width: 116px !important;
        height: 18px !important;
        z-index: 1000 !important;
        pointer-events: none !important;
        display: flex !important;
        justify-content: space-between !important;
        align-items: center !important;
        padding: 0 !important;
        box-sizing: border-box !important;
    }
    
    /* Left buttons container (Min/Max) */
    .tnt_left_buttons {
        display: flex !important;
        align-items: center !important;
        gap: 0px !important;
        pointer-events: none !important;
        flex-shrink: 0 !important;
    }
    
    /* Right buttons container (Refresh, Toggle) */
    .tnt_right_buttons {
        margin-top: 5px !important;
        display: flex !important;
        align-items: center !important;
        gap: 2px !important;
        pointer-events: none !important;
        flex-shrink: 0 !important;
        height: 18px !important;
    }
    
    /* Individual control buttons - perfectly sized and aligned */
    .tnt_left_buttons span,
    .tnt_right_buttons span {
        display: inline-flex !important;
        align-items: center !important;
        justify-content: center !important;
        height: 18px !important;
        width: 18px !important;
        min-height: 18px !important;
        min-width: 18px !important;
        max-height: 18px !important;
        max-width: 18px !important;
        border: 1px solid #8B4513 !important;
        background: linear-gradient(135deg, #E6D3A3 0%, #D2B48C 50%, #C4A47C 100%) !important;
        border-radius: 3px !important;
        cursor: pointer !important;
        flex-shrink: 0 !important;
        pointer-events: auto !important;
        position: relative !important;
        box-shadow: 0 1px 3px rgba(0,0,0,0.3) !important;
        transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
        font-size: 0 !important;
        line-height: 1 !important;
        padding: 0 !important;
        margin: 0 !important;
        box-sizing: border-box !important;
    }
    
    .tnt_left_buttons span:hover,
    .tnt_right_buttons span:hover {
        background: linear-gradient(135deg, #F0E4B6 0%, #E6D3A3 50%, #D2B48C 100%) !important;
        transform: translateY(-1px) scale(1.05) !important;
        box-shadow: 0 3px 6px rgba(0,0,0,0.4) !important;
        border-color: #654321 !important;
    }
    
    .tnt_left_buttons span:active,
    .tnt_right_buttons span:active {
        transform: translateY(0px) scale(1.02) !important;
        box-shadow: 0 1px 3px rgba(0,0,0,0.3) !important;
    }
    
    /* Minimize button icons - properly centered triangles */
    .tnt_left_buttons .tnt_panel_minimize_btn.tnt_back:after { 
        content: "";
        display: block;
        width: 0;
        height: 0;
        border: 3px solid transparent;
        border-right: 5px solid #333;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    
    .tnt_left_buttons .tnt_panel_minimize_btn.tnt_back:hover:after { 
        border-right-color: #000;
    }
    
    .tnt_left_buttons .tnt_panel_minimize_btn.tnt_foreward:after { 
        content: "";
        display: block;
        width: 0;
        height: 0;
        border: 3px solid transparent;
        border-left: 5px solid #333;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    
    .tnt_left_buttons .tnt_panel_minimize_btn.tnt_foreward:hover:after { 
        border-left-color: #000;
    }
    
    /* Toggle button icon - three centered lines */
    .tnt_right_buttons .tnt_table_toggle_btn:after {
        content: "";
        display: block;
        width: 8px;
        height: 2px;
        background: #333;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        box-shadow: 
            0 -3px 0 #333,
            0 3px 0 #333;
        border-radius: 1px;
    }
    
    .tnt_right_buttons .tnt_table_toggle_btn:hover:after {
        background: #000;
        box-shadow: 
            0 -3px 0 #000,
            0 3px 0 #000;
    }
    
    .tnt_right_buttons .tnt_table_toggle_btn.active:after {
        background: #006600;
        box-shadow: 
            0 -3px 0 #006600,
            0 3px 0 #006600;
    }
    
    /* Refresh button icon - perfectly centered */
    .tnt_right_buttons .tnt_refresh_btn:before {
        content: "⟳";
        color: #333;
        font-size: 13px;
        font-weight: bold;
        text-shadow: 0 1px 2px rgba(255,255,255,0.7) !important;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        line-height: 1;
        width: 13px;
        height: 13px;
        display: flex;
        align-items: center;
        justify-content: center;
    }
    
    .tnt_right_buttons .tnt_refresh_btn:hover:before {
        color: #000;
        font-weight: 900;
        text-shadow: 0 1px 3px rgba(255,255,255,0.9) !important;
    }
    
    /* Remove old control button styles that are no longer needed */
    .tnt_control_buttons {
        display: none !important;
    }
    
    /* Minimize button icons */
    .tnt_control_buttons .tnt_panel_minimize_btn.tnt_back:after { 
        content: "";
        display: block;
        width: 0;
        height: 0;
        border: 3px solid transparent;
        border-right: 5px solid #333;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    
    .tnt_control_buttons .tnt_panel_minimize_btn.tnt_back:hover:after { 
        border-right-color: #000;
    }
    
    .tnt_control_buttons .tnt_panel_minimize_btn.tnt_foreward:after { 
        content: "";
        display: block;
        width: 0;
        height: 0;
        border: 3px solid transparent;
        border-left: 5px solid #333;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    
    .tnt_control_buttons .tnt_panel_minimize_btn.tnt_foreward:hover:after { 
        border-left-color: #000;
    }
    
    /* Toggle button icon */
    .tnt_control_buttons .tnt_table_toggle_btn:after {
        content: "";
        display: block;
        width: 6px;
        height: 1px;
        background: #333;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        box-shadow: 
            0 -2px 0 #333,
            0 2px 0 #333;
    }
    
    .tnt_control_buttons .tnt_table_toggle_btn:hover:after {
        background: #000;
        box-shadow: 
            0 -2px 0 #000,
            0 2px 0 #000;
    }
    
    .tnt_control_buttons .tnt_table_toggle_btn.active:after {
        background: #006600;
        box-shadow: 
            0 -2px 0 #006600,
            0 2px 0 #006600;
    }
    
    /* Refresh button icon */
    .tnt_control_buttons .tnt_refresh_btn:before {
        content: "⟳";
        color: #333;
        font-size: 14px;
        font-weight: bold;
        text-shadow: 0 1px 1px rgba(255,255,255,0.5);
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        line-height: 1;
    }
    
    .tnt_control_buttons .tnt_refresh_btn:hover:before {
        color: #000;
        font-weight: 900;
        text-shadow: 0 1px 2px rgba(255,255,255,0.8);
    }
    
    /* Remove all old button styles that conflict with new structure */
    // .tnt_table_toggle_btn:not(.tnt_control_buttons *), // Should always be visible
    #tnt_info_resources .tnt_back,
    #tnt_info_resources .tnt_foreward,
    #tnt_info_updateCities,
    .tnt_panel_minimize_btn:not(.tnt_control_buttons *) {
        display: none !important;
    }
    
    .tnt_city .tnt_panel_minimize_btn {
        display: none !important;
    }
    
    /* Remove old category spacer styles that are no longer needed */
    .tnt_category_spacer {
        display: none !important;
    }
    /* Construction status styling applies to the first cell in any row across all tables */
    // .tnt_construction{
    //     background-color: #80404050 !important;
    // }
    .tnt_construction {
        background-color: #80404050 !important;
        border-left: 2px solid #804040 !important;
    }    
    /* Phase 4: Visual progress indicators */
    .tnt_progress_visited {
        background-color: #90EE9050 !important;
        border-left: 2px solid #32CD32 !important;
    }
    
    /* Ensure progress indicators work with selected state */
    body #tnt_info_resources .tnt_selected .tnt_progress_visited,
    body #tnt_info_buildings_content .tnt_selected .tnt_progress_visited {
        background-color: #90EE9050 !important;
        border-left: 2px solid #32CD32 !important;
    }
    
    /* Progress indicator takes precedence over construction during active switching */
    // .tnt_progress_visited.tnt_construction {
    //     background-color: #90EE9050 !important;
    //     border-left: 2px solid #32CD32 !important;
    // }
    .tnt_progress_visited.tnt_construction {
        background-color: #d4edda !important;
        color: #155724 !important;
    }    
    /* === RESOURCE STORAGE INDICATORS (FIX FOR ISSUE #002) === */
    
    /* Storage danger - high storage warning (RED text, no borders) */
    .tnt_storage_danger {
        //background-color: #ffaaaa !important;
        color: #ff0000 !important;
    }
    
    /* Storage minimum - low resource warning (YELLOW background, no borders) */
    .tnt_storage_min {
        background-color: #ffaaaa !important;
    }
    
    /* Ensure storage indicators work with TNT table styling */
    body #tnt_info_resources .tnt_storage_danger,
    body #tnt_info_buildings_content .tnt_storage_danger {
        //background-color: #ffaaaa !important;
    }
    
    body #tnt_info_resources .tnt_storage_min,
    body #tnt_info_buildings_content .tnt_storage_min {
        background-color: #ffaaaa !important;
    }
    
    /* Storage indicators with current city selection */
    body #tnt_info_resources .tnt_selected .tnt_storage_danger,
    body #tnt_info_buildings_content .tnt_selected .tnt_storage_danger {
        //background-color: #ffaaaa !important;
    }
    
    body #tnt_info_resources .tnt_selected .tnt_storage_min,
    body #tnt_info_buildings_content .tnt_selected .tnt_storage_min {
        background-color: #ffaaaa !important;
    }
    
    /* Storage indicators take precedence over construction status */
    .tnt_storage_danger.tnt_construction {
        //background-color: #ffaaaa !important;
    }
    
    .tnt_storage_min.tnt_construction {
        background-color: #ffaaaa !important;
    }
    
    /* Remove old category spacer styles that are no longer needed */
    .tnt_category_spacer {
        display: none !important;
    }
    /* Construction status styling applies to the first cell in any row across all tables */
    .tnt_construction{
        background-color: #80404050 !important;
    }
    /* Current city highlighting with 2px black border - no background change */
    body #tnt_info_resources .tnt_selected,
    body #tnt_info_buildings_content .tnt_selected {
        border: 2px solid black !important;
    }
    body #tnt_info_resources .tnt_selected td,
    body #tnt_info_buildings_content .tnt_selected td {
        border-top: 2px solid black !important;
        border-bottom: 2px solid black !important;
        // color: #000 !important;
    }
    body #tnt_info_resources .tnt_selected td:first-child,
    body #tnt_info_buildings_content .tnt_selected td:first-child {
        border-left: 2px solid black !important;
    }
    body #tnt_info_resources .tnt_selected td:last-child,
    body #tnt_info_buildings_content .tnt_selected td:last-child {
        border-right: 2px solid black !important;
    }
    /* Make tradegood production more visible with dark grey text color */
    body #tnt_info_resources .tnt_bold,
    body #tnt_info_buildings_content .tnt_bold {
        color: #333333 !important;
        font-weight: bold !important;
    }
    .tnt_resource_icon{
        vertical-align:middle !important;
        width:18px !important;
        height:16px !important;
        display:inline-block !important;
    }
    .tnt_building_icon {
        width: 36px !important;
        height: 32px !important;
    }
    img[src*='/city/wall.png'].tnt_building_icon {
        transform: scale(0.8) !important;
        transform-origin: 0 0;
        margin-right: -8px;
    }
    body #tnt_info_resources .tnt_population{ text-align:right !important; }
    body #tnt_info_resources .tnt_citizens{ text-align:right !important; }
    body #tnt_info_resources .tnt_wood{ text-align:right !important; }
    body #tnt_info_resources .tnt_marble{ text-align:right !important; }
    body #tnt_info_resources .tnt_wine{ text-align:right !important; }
    body #tnt_info_resources .tnt_crystal{ text-align:right !important; }
    body #tnt_info_resources .tnt_sulfur{ text-align:right !important; }
    body #tnt_info_resources .tnt_city{ text-align:left !important; }
    body #tnt_info_buildings_content .tnt_city{ text-align:left !important; }
    body #tnt_info_buildings_content .tnt_building_level{ text-align:center !important; }
    
    /* Override Ikariam's container table styles specifically for our TNT tables */
    #container body #tnt_info_resources #tnt_resources_table.table01,
    #container body #tnt_info_buildings_content #tnt_buildings_table.table01 {
        border: none !important;
        margin: 0px !important;
        background-color: #fdf7dd !important;
        border-bottom: none !important;
        text-align: center !important;
        width: auto !important;
    }
    #container body #tnt_info_resources #tnt_resources_table.table01 td,
    #container body #tnt_info_buildings_content #tnt_buildings_table.table01 td {
        text-align: center !important;
        vertical-align: middle !important;
        padding: 4px !important;
        border: 1px #000000 solid !important;
    }
    #container body #tnt_info_resources #tnt_resources_table.table01 th,
    #container body #tnt_info_buildings_content #tnt_buildings_table.table01 th {
        background-color: #faeac6 !important;
        text-align: center !important;
        height: auto !important;
        padding: 4px !important;
        font-weight: bold !important;
        border: 1px #000000 solid !important;
    }
    
    #mainview a:hover{ text-decoration:none; }
    #tntOptions {
        position:absolute;
        top:40px;
        left:380px;
        width:620px;
        border:1px #755931 solid;
        border-top:none;
        background-color: #FEE8C3;
        padding:10px 10px 0px   10px;
    }

    #tntOptions legend{ font-weight:bold; }
    .tntHide, #infocontainer .tntLvl, #actioncontainer .tntLvl{ display:none; }
    #tntInfoWidget {
        position:fixed;
        bottom:0px;
        left:0px;
        width:716px;
        background-color: #DBBE8C;
        z-index:100000000;
    }
    #tntInfoWidget .accordionTitle {

        background: url(/cdn/all/both/layout/bg_maincontentbox_header.jpg) no-repeat;
        padding: 6px 0 0;
        width: 728px;
    }
    #tntInfoWidget .accordionContent {
        background: url(/cdn/all/both/layout/bg_maincontentbox_left.png) left center repeat-y #FAF3D7;
        overflow: hidden;
        padding: 0;
        position: relative;
        width: 725px;
    }
    #tntInfoWidget .scroll_disabled {
        background: url(/cdn/all/both/layout/bg_maincontentbox_left.png) repeat-y scroll left center transparent;
        width: 9px;
    }
    #tntInfoWidget .scroll_area {
        background: url(/cdn/all/both/interface/scroll_bg.png) right top repeat-y transparent;
        display: block;
        height: 100%;
        overflow: hidden;
        position: absolute;
        right: -3px;
        width: 24px;
        z-index: 100000;
    }
    .txtCenter{ text-align:center; }
    .tnt_center{ text-align:center!important; white-space:nowrap; }
    .tnt_right{ text-align:right!important; white-space:nowrap; }
    .tnt_left{ text-align:left!important; white-space:nowrap; }
    #tnt_info_resources{
        position:fixed;
        bottom:20px;
        left:0px;
        width:auto;
        height:auto;
        background-color: #DBBE8C;
        z-index:100000000;
    }
    #tnt_info_resources .tnt_back, #tnt_info_resources .tnt_foreward {
        cursor: pointer;
        display: block!important;
        height: 18px;
        width: 18px;
        border: 1px solid #8B4513;
        background: #D2B48C;
        border-radius: 2px;
        text-align: center;
        line-height: 16px;
        font-size: 12px;
        min-width: 18px;
        min-height: 18px;
    }
    #tnt_info_resources .tnt_back {
        left: 2px;
        position: absolute;
        top: 2px;
    }
    #tnt_info_resources .tnt_back:before {
        content: "◀";
        color: #333;
        display: inline-block;
        width: 100%;
        height: 100%;
    }
    #tnt_info_resources .tnt_back:hover {
        background: #DDD;
    }
    #tnt_info_resources .tnt_back:hover:before {
        color: #000;
    }
    #tnt_info_resources .tnt_foreward {
        left: 2px;
        position: absolute;
        top: 3px;
    }
    #tnt_info_resources .tnt_foreward:before {
        content: "▶";
        color: #333;
        display: inline-block;
        width: 100%;
        height: 100%;
    }
    #tnt_info_resources .tnt_foreward:hover {
        background: #DDD;
    }
    #tnt_info_resources .tnt_foreward:hover:before {
        color: #000;
    }
    #tnt_info_updateCities {
        position:fixed;
        bottom:20px;
        right:0px;
        width:auto;
        height:auto;
        background-color: #DBBE8C;
        z-index:100000000;
    }
    .tnt_panel_minimize_btn {
        cursor: pointer;
        display: block!important;
        height: 18px;
        width: 18px;
        position: absolute;
        left: 2px;
        top: 2px;
        z-index: 10;
        border: 1px solid #8B4513;
        background: #D2B48C;
        border-radius: 2px;
        text-align: center;
        line-height: 16px;
        font-size: 12px;
        min-width: 18px;
        min-height: 18px;
        box-sizing: border-box;
        overflow: hidden;
    }
    .tnt_panel_minimize_btn.tnt_back:before { 
        content: "◀";
        color: #333;
        display: inline-block;
        width: 100%;
        height: 100%;
        line-height: 16px;
        text-align: center;
        font-size: 10px;
        vertical-align: middle;
    }
    .tnt_panel_minimize_btn.tnt_back:hover { 
        background: #DDD;
    }
    .tnt_panel_minimize_btn.tnt_back:hover:before { 
        color: #000;
    }
    .tnt_panel_minimize_btn.tnt_foreward { 
        top: 3px; 
    }
    .tnt_panel_minimize_btn.tnt_foreward:before { 
        content: "▶";
        color: #333;
        display: inline-block;
        width: 100%;
        height: 100%;
        line-height: 16px;
        text-align: center;
        font-size: 10px;
        vertical-align: middle;
    }
    .tnt_panel_minimize_btn.tnt_foreward:hover { 
        background: #DDD;
    }
    .tnt_panel_minimize_btn.tnt_foreward:hover:before { 
        color: #000;
    }
    .tnt_table_toggle_btn {
        cursor: pointer;
        display: inline-block;
        height: 18px;
        width: 18px;
        vertical-align: middle;
        float: right;
        margin-left: 6px;
        border: 1px solid #8B4513;
        background: #D2B48C;
        border-radius: 2px;
        text-align: center;
        line-height: 16px;
        font-size: 12px;
        min-width: 18px;
        min-height: 18px;
        box-sizing: border-box;
        overflow: hidden;
    }
    .tnt_table_toggle_btn:before {
        content: "⇄";
        color: #333;
        display: inline-block;
        width: 100%;
        height: 100%;
        line-height: 16px;
        text-align: center;
        font-size: 10px;
        vertical-align: middle;
    }
    .tnt_table_toggle_btn:hover { 
        background: #DDD;
    }
    .tnt_table_toggle_btn:hover:before {
        color: #000;
    }
    .tnt_table_toggle_btn.active:before {
        content: "⇄";
        color: #006600;
        font-weight: bold;
    }
    /* Remove duplicate old button styles - keep only this section */
    .tnt_city .tnt_panel_minimize_btn { 
        display: none !important;
    }
    
    /* Change the minimized state to show the first cell completely for both tables */
    #tnt_info_resources.minimized,
    #tnt_info_buildings.minimized {
        width: auto !important;
        min-width: auto !important;
        max-width: none !important;
        overflow: hidden !important;
    }
    
    /* Simple minimized state - just hide columns */
    #tnt_info_resources.minimized table tr td:not(:first-child),
    #tnt_info_resources.minimized table tr th:not(:first-child),
    #tnt_info_buildings.minimized table tr td:not(:first-child),
    #tnt_info_buildings.minimized table tr th:not(:first-child) {
        display: none !important;
    }
    
    /* Show only the first cell when minimized - keep as table-cell */
    #tnt_info_resources.minimized table tr th:first-child,
    #tnt_info_resources.minimized table tr td:first-child,
    #tnt_info_buildings.minimized table tr th:first-child,
    #tnt_info_buildings.minimized table tr td:first-child {
        display: table-cell !important;
        width: auto !important;
        min-width: 60px !important;
    }
    
    /* Special handling for the header row when minimized */
    #tnt_info_resources.minimized table thead tr,
    #tnt_info_buildings.minimized table thead tr {
        display: table-row !important;
    }
    
    #tnt_info_resources.minimized table thead tr th:first-child,
    #tnt_info_buildings.minimized table thead tr th:first-child {
        display: table-cell !important;
        width: auto !important;
    }
    
    /* Ensure the tables maintain proper structure when minimized */
    #tnt_info_resources.minimized table,
    #tnt_info_buildings.minimized table {
        width: auto !important;
    }
    
    /* FORCE exact same heights in minimized state - now completely independent */
    #tnt_info_resources.minimized table tr.tnt_category_header,
    #tnt_info_buildings.minimized table tr.tnt_category_header {
        height: 25px !important;
        max-height: 25px !important;
        display: table-row !important;
    }
    
    #tnt_info_resources.minimized table tr.tnt_category_header th,
    #tnt_info_buildings.minimized table tr.tnt_category_header th {
        height: 25px !important;
        max-height: 25px !important;
        padding: 4px !important;
        vertical-align: middle !important;
        box-sizing: border-box !important;
        line-height: 17px !important;
        overflow: hidden !important;
    }
    
    /* External controls remain visible and positioned in minimized state */
    #tnt_info_resources.minimized .tnt_external_controls,
    #tnt_info_buildings.minimized .tnt_external_controls {
        position: absolute !important;
        top: 2px !important;
        left: 2px !important;
        width: 116px !important;
        height: 18px !important;
        z-index: 1000 !important;
        pointer-events: none !important;
        padding: 0 !important;
        box-sizing: border-box !important;
    }
    
    /* Remove all conflicting minimized button positioning - buttons are now external */
    /* 
    #tnt_info_resources.minimized .tnt_control_buttons,
    #tnt_info_buildings.minimized .tnt_control_buttons {
        // REMOVED - buttons are external now
    }
    
    #tnt_info_resources.minimized .tnt_control_buttons span,
    #tnt_info_buildings.minimized .tnt_control_buttons span {
        // REMOVED - buttons are external now  
    }
    */
    
    /* FORCE exact same subcategory header height in minimized state */
    #tnt_info_resources.minimized table tr.tnt_subcategory_header,
    #tnt_info_buildings.minimized table tr.tnt_subcategory_header {
        height: 41px !important;
        min-height: 41px !important;
        max-height: 41px !important;
        display: table-row !important;
    }
    
    #tnt_info_resources.minimized table tr.tnt_subcategory_header th:first-child,
    #tnt_info_buildings.minimized table tr.tnt_subcategory_header th:first-child {
        display: table-cell !important;
        height: 41px !important;
        min-height: 41px !important;
        max-height: 41px !important;
        line-height: 1.2 !important;
        vertical-align: middle !important;
        box-sizing: border-box !important;
        padding: 4px !important;
    }
    
    /* Override any conflicting styles for subcategory header cells in minimized state */
    #tnt_info_resources.minimized table tr.tnt_subcategory_header th,
    #tnt_info_buildings.minimized table tr.tnt_subcategory_header th {
        height: 41px !important;
        min-height: 41px !important;
        max-height: 41px !important;
        line-height: 1.2 !important;
        vertical-align: middle !important;
        box-sizing: border-box !important;
        padding: 4px !important;
    }
    #tnt_info_resources .tnt_building_maxed {
        background-color: #d4edda !important;
    }
`);
// Ensure the styles are applied immediately