Torn Property Manager TPM

[PC/Laptop] 🏘️ Complete property portfolio management suite. Real-time dashboard with color-coded alerts, tenant intelligence with permanent history, ROI tracking across portfolio/property types, market analysis with IQR filtering, smart lease forms with auto-fill & live calculator, notes system, export/import, auto-rotating storage. Join investors managing millions in property value!

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

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Torn Property Manager TPM
// @version      TPM.V3.0.0
// @description  [PC/Laptop] 🏘️ Complete property portfolio management suite. Real-time dashboard with color-coded alerts, tenant intelligence with permanent history, ROI tracking across portfolio/property types, market analysis with IQR filtering, smart lease forms with auto-fill & live calculator, notes system, export/import, auto-rotating storage. Join investors managing millions in property value!
// @author       Sprinkers [4056515]
// @match        https://*.torn.com/properties.php*
// @grant        GM_xmlhttpRequest
// @grant        window.close
// @connect      api.torn.com
// @run-at       document-end
// @namespace https://greasyfork.org/users/1576692
// ==/UserScript==

(function() {
    'use strict';
    const CONFIG = {
        API_BATCH_SIZE: 100,
        DEFAULT_SECOND_PROPNAV: 10,
        DEFAULT_ITEMS_PER_PAGE: 10,
        MIN_API_KEY_LENGTH: 16,
        DEFAULT_WARN_EXPIRE: 3,
        DEFAULT_WARN_CLOSE: 7,
        DEFAULT_HISTORY_LIMIT: 10,
        STORAGE_LIMIT_KB: 5000,
        STORAGE_WARN_LEVEL: 60,
        STORAGE_CRITICAL_LEVEL: 85,
        MAX_RETRY_ATTEMPTS: 3
    };
    const KEY_BINDS = {
        CLOSE_ALL: 'Escape'
    };
    const RATE_LIMIT = {
        CALLS_PER_MINUTE: 50,
        CALLS_PER_HOUR: 1000,
        MIN_INTERVAL_MS: 1200,
        lastCallTime: 0,
        callCount: {
            minute: 0,
            hour: 0,
            lastMinuteReset: Date.now(),
            lastHourReset: Date.now()
        },
        retryCount: 0,
        maxRetries: 3,
        notificationShown: false
    };
    const TAB_COORDINATION = {
        PROPERTIES_LOCK: 'tpm_tab_lock_properties',
        LOGS_LOCK: 'tpm_tab_lock_logs',
        VERIFY_LOCK: 'tpm_tab_lock_verify',
        LOCK_TIMEOUT: 10000,
        lastUpdate: Date.now()
    };
    const EVENT_CODES = {
        PROPERTY_RENTED: 1,
        RENTAL_EXPIRED: 2,
        EXTENSION_OFFERED: 3,
        EXTENSION_ACCEPTED: 4,
        OFFER_DECLINED: 5,
        LEASE_ENDED: 6,
        EXTENSION_WITHDRAWN: 7
    };
    const EVENT_NAMES = {
        1: 'property_rented',
        2: 'rental_expired',
        3: 'extension_offered',
        4: 'extension_accepted',
        5: 'offer_declined',
        6: 'lease_ended',
        7: 'extension_withdrawn'
    };
    const STORE_EVENTS = {
        UPDATED: 'tpm_store_updated'
    };
    const EVENT_MAP = {
        5937: EVENT_CODES.PROPERTY_RENTED,
        5939: EVENT_CODES.RENTAL_EXPIRED,
        5940: EVENT_CODES.EXTENSION_OFFERED,
        5943: EVENT_CODES.EXTENSION_ACCEPTED,
        5948: EVENT_CODES.OFFER_DECLINED,
        5951: EVENT_CODES.LEASE_ENDED,
        5953: EVENT_CODES.EXTENSION_WITHDRAWN
    };
    const TPM_USER_AGENT = 'TPM.3.0.0';
    const tabChannel = new BroadcastChannel('tpm_tab_sync');
    let tpmArchive = {};
    const STATUS_DISPLAY = {
        'rented': '🟢',
        'none': '⚠️🪧',
        'for_rent': '🔔',
        'for_sale': '📢',
        'in_use': '🕌',
        'undefined': '☢️'
    };
    const STYLES = {
        container: 'width:96%;background:var(--tpm-bg);padding:15px;border-radius:8px;line-height:1.4;',
        input: 'width:98%;padding:8px;background:var(--tpm-input-bg);color:var(--tpm-text);border:1px solid var(--tpm-border);border-radius:4px;font-size:13px;',
        button: 'background: var(--tpm-button-bg); color: var(--tpm-text); border: none; padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size:12px; transition: background 0.2s;',
        statusColors: {
            expired: 'rgba(180, 50, 50, 0.25)',
            expiredfull: 'rgba(180, 50, 50, 0.9)',
            warning: 'rgba(200, 80, 200, 0.25)',
            warningfull: 'rgba(200, 80, 200, 0.9)',
            gettingclose: 'rgba(200, 180, 50, 0.25)',
            gettingclosefull: 'rgba(200, 180, 50, 0.9)',
            forsale: 'rgba(120, 40, 120, 0.25)',
            forsalefull: 'rgba(120, 40, 120, 0.9)',
            forrent: 'rgba(40, 180, 100, 0.25)',
            forrentfull: 'rgba(40, 180, 100, 0.9)',
            offered: 'rgba(100, 70, 150, 0.25)',
            offeredfull: 'rgba(100, 70, 150, 0.9)',
            offerask: 'rgba(100, 70, 150, 0.25)',
            offeraskfull: 'rgba(100, 70, 150, 0.9)',
            allgood: 'rgba(30, 70, 150, 0.25)',
            allgoodfull: 'rgba(30, 70, 150, 0.9)',
            hover: 'rgba(30, 90, 20, 0.35)'
        }
    };
    function safeJSON(key) {
        try {
            const data = localStorage.getItem(key);
            if (!data) return key === 'prop_cache' ? [] : {};
            const parsed = JSON.parse(data);
            return (typeof parsed === 'object' && parsed !== null) ? parsed : (key === 'prop_cache' ? [] : {});
        } catch (e) {
            console.error(`TPM: Storage corrupted for ${key}:`, e);
            const backup = key === 'prop_cache' ? [] : {};
            localStorage.setItem(key, JSON.stringify(backup));
            return backup;
        }
    }
    function showUpdatePopup(text) {
        const overlay = document.createElement("div");
        overlay.className = "tpm-update-overlay";
        overlay.innerHTML = `
            <div class="tpm-update-modal">
                <div class="tpm-update-title">🏘 Torn Property Manager Update</div>
                <div class="tpm-update-body">${text}</div>
                <div class="tpm-update-close" style="text-align:right; margin-top:15px;">
                    <button class="tpm-button" id="tpm-update-close-btn">Close</button>
                </div>
            </div>
        `;
        document.body.appendChild(overlay);
        document.getElementById("tpm-update-close-btn").onclick = () => overlay.remove();
    }
    function enforceVersionReset() {
        const storedVersion = localStorage.getItem('tpm_script_version');
        const apiKey = localStorage.getItem('tpmApiKey');
        const SCRIPT_VERSION = "TPM.V3.0.0";
        const UPDATE_LOGS = {
            "TPM.V3.0.0": `<div style="font-family: Arial, sans-serif; line-height: 1.6;">
                <div style="font-size: 16px; font-weight: bold; color: #4CAF50; margin-bottom: 10px;">📢 Torn Property Manager v3.0.0</div>
                <ul style="margin: 0 0 10px 0; padding-left: 20px;">
                    <li>Complete portfolio ROI tracking</li>
                    <li>Responsive dark/light modes to torns settings</li>
                    <li>Individual property ROI percentages</li>
                    <li>Vacancy loss calculation (monthly)</li>
                    <li>Market price storage for accurate valuations</li>
                    <li>Fixed lease form auto-fill with last records</li>
                    <li>30/60/90 day income projections</li>
                    <li>Auto-fill with last recorded values</li>
                    <li>Notes Book tenant system</li>
                    <li>Smart storage with auto-rotation</li>
                    <li>Cross-tab synchronization</li>
                    <li>Improved rate limit handling</li>
                    <li>Escape key closes sections</li>
                </ul>
                <div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #444;">
                    ⚠ Now requires FULL ACCESS API key for log reading<br>
                    📖 See Help for complete documentation
                </div>
            </div>`
        };
        if (storedVersion !== SCRIPT_VERSION) {
            console.log(`TPM Update: ${storedVersion} -> ${SCRIPT_VERSION}`);
            let message = UPDATE_LOGS[SCRIPT_VERSION] || "Torn Property Manager Updated.";
            if (apiKey) {
                localStorage.removeItem('tpmApiKey');
                message += "\n\n⚠ For security, please re-enter your API key.";
            }
            showUpdatePopup(message);
            localStorage.setItem('tpm_script_version', SCRIPT_VERSION);
        }
    }
    enforceVersionReset();
    function checkRateLimit() {
        const now = Date.now();
        if (now - RATE_LIMIT.callCount.lastMinuteReset > 60000) {
            RATE_LIMIT.callCount.minute = 0;
            RATE_LIMIT.callCount.lastMinuteReset = now;
            RATE_LIMIT.notificationShown = false;
            RATE_LIMIT.consecutiveRateLimits = 0;
        }
        if (now - RATE_LIMIT.callCount.lastHourReset > 3600000) {
            RATE_LIMIT.callCount.hour = 0;
            RATE_LIMIT.callCount.lastHourReset = now;
            RATE_LIMIT.notificationShown = false;
            RATE_LIMIT.consecutiveRateLimits = 0;
        }
        const timeSinceLastCall = now - RATE_LIMIT.lastCallTime;
        if (RATE_LIMIT.lastCallTime !== 0 && timeSinceLastCall < RATE_LIMIT.MIN_INTERVAL_MS) {
            const waitTime = RATE_LIMIT.MIN_INTERVAL_MS - timeSinceLastCall;
            if (isDebugMode()) console.log(`TPM: Rate limit - waiting ${waitTime}ms`);
            RATE_LIMIT.consecutiveRateLimits = (RATE_LIMIT.consecutiveRateLimits || 0) + 1;
            if (!RATE_LIMIT.notificationShown && RATE_LIMIT.consecutiveRateLimits >= 10) {
                showNotification('⚠️ Rate limiting active: Protecting your account. API calls are now spaced 1.2 seconds apart.', 'warning');
                RATE_LIMIT.notificationShown = true;
            }
            return waitTime;
        }
        if (RATE_LIMIT.callCount.minute >= RATE_LIMIT.CALLS_PER_MINUTE) {
            const waitTime = 60000 - (now - RATE_LIMIT.callCount.lastMinuteReset);
            if (isDebugMode()) console.log(`TPM: Minute rate limit reached, waiting ${waitTime}ms`);
            RATE_LIMIT.consecutiveRateLimits = (RATE_LIMIT.consecutiveRateLimits || 0) + 1;
            if (!RATE_LIMIT.notificationShown && RATE_LIMIT.consecutiveRateLimits >= 10) {
                showNotification('⚠️ Minute rate limit active: Protecting your account. Maximum 50 calls per minute enforced.', 'warning');
                RATE_LIMIT.notificationShown = true;
            }
            return waitTime;
        }
        if (RATE_LIMIT.callCount.hour >= RATE_LIMIT.CALLS_PER_HOUR) {
            const waitTime = 3600000 - (now - RATE_LIMIT.callCount.lastHourReset);
            if (isDebugMode()) console.log(`TPM: Hour rate limit reached, waiting ${waitTime}ms`);
            RATE_LIMIT.consecutiveRateLimits = (RATE_LIMIT.consecutiveRateLimits || 0) + 1;
            if (!RATE_LIMIT.notificationShown && RATE_LIMIT.consecutiveRateLimits >= 10) {
                showNotification('⚠️ Hourly rate limit active: Protecting your account. Maximum 1000 calls per hour enforced.', 'warning');
                RATE_LIMIT.notificationShown = true;
            }
            return waitTime;
        }
        RATE_LIMIT.consecutiveRateLimits = 0;
        return 0;
    }
    function updateRateLimitCounters() {
        RATE_LIMIT.lastCallTime = Date.now();
        RATE_LIMIT.callCount.minute++;
        RATE_LIMIT.callCount.hour++;
        RATE_LIMIT.retryCount = 0;
    }
    function acquireTabLock(lockKey) {
        const now = Date.now();
        const lockData = localStorage.getItem(lockKey);
        if (lockData) {
            try {
                const lock = JSON.parse(lockData);
                if (now - lock.timestamp < TAB_COORDINATION.LOCK_TIMEOUT) {
                    return false;
                }
            } catch (e) {}
        }
        const newLock = {
            tabId: Math.random().toString(36).substr(2, 9),
            timestamp: now
        };
        localStorage.setItem(lockKey, JSON.stringify(newLock));
        const verifyLock = JSON.parse(localStorage.getItem(lockKey));
        return verifyLock.tabId === newLock.tabId;
    }
    function releaseTabLock(lockKey) {
        localStorage.removeItem(lockKey);
    }
    function waitForTabUpdate(lockKey, timeout = 10000) {
        return new Promise((resolve) => {
            const startTime = Date.now();
            const checkInterval = setInterval(() => {
                const lockExists = localStorage.getItem(lockKey);
                const lastCall = parseInt(localStorage.getItem(lockKey === TAB_COORDINATION.PROPERTIES_LOCK ? 'prop_last_call' : 'tenant_logs_last_call')) || 0;
                if (!lockExists && lastCall > TAB_COORDINATION.lastUpdate) {
                    clearInterval(checkInterval);
                    resolve(true);
                } else if (Date.now() - startTime > timeout) {
                    clearInterval(checkInterval);
                    resolve(false);
                }
            }, 500);
        });
    }
    function initOptimizedStorage() {
        if (!localStorage.getItem('tpm_store_v2')) {
            const oldLogs = safeJSON("tpm_lease_logs");
            const oldTenants = safeJSON("tpm_tenant_database");
            const newStore = {
                events: {},
                tenants: {},
                properties: {},
                archive: tpmArchive,
                version: 2
            };
            if (oldTenants && typeof oldTenants === 'object') {
                Object.entries(oldTenants).forEach(([id, t]) => {
                    newStore.tenants[id] = {
                        n: t.name || 'Unknown',
                        f: t.firstSeen || Date.now(),
                        l: t.lastSeen || Date.now(),
                        a: t.aliases || [],
                        c: t.totalLeases || 0,
                        s: t.reliabilityScore || 50
                    };
                });
            }
            if (oldLogs && typeof oldLogs === 'object') {
                Object.entries(oldLogs).forEach(([propID, propData]) => {
                    if (propData && typeof propData === 'object') {
                        newStore.events[propID] = [];
                        Object.entries(propData).forEach(([tenantID, events]) => {
                            if (Array.isArray(events)) {
                                events.forEach(e => {
                                    if (e && e.time) {
                                        const eventCode = e.logType ?
                                            (EVENT_MAP[e.logType] || 0) :
                                            (e.event === 'property_rented' ? 1 :
                                             e.event === 'rental_expired' ? 2 :
                                             e.event === 'extension_offered' ? 3 :
                                             e.event === 'extension_accepted' ? 4 :
                                             e.event === 'offer_declined' ? 5 :
                                             e.event === 'lease_ended' ? 6 :
                                             e.event === 'extension_withdrawn' ? 7 : 0);
                                        newStore.events[propID].push([
                                            e.time || 0,
                                            eventCode,
                                            e.rent || 0,
                                            e.days || 0,
                                            e.rent || 0,
                                            tenantID
                                        ]);
                                    }
                                });
                            }
                        });
                    }
                });
            }
            localStorage.setItem('tpm_store_v2', JSON.stringify(newStore));
        }
    }
    initOptimizedStorage();
    tpmArchive = safeJSON("tpm_archive");
    if (!tpmArchive.totalLifetimeIncome) {
        tpmArchive = {
            totalLifetimeIncome: 0,
            totalLifetimeLeases: 0,
            totalUniqueTenants: 0,
            yearlyIncome: {},
            propertyTypes: {},
            lastArchiveUpdate: Date.now(),
            totalPropertiesArchived: 0
        };
        localStorage.setItem('tpm_archive', JSON.stringify(tpmArchive));
    }
    function getStore() {
        try {
            const storeStr = localStorage.getItem('tpm_store_v2');
            return storeStr ? JSON.parse(storeStr) : { events: {}, tenants: {}, archive: tpmArchive, version: 2 };
        } catch (e) {
            console.error('TPM: Store corrupted, creating new store', e);
            const newStore = { events: {}, tenants: {}, archive: tpmArchive, version: 2 };
            localStorage.setItem('tpm_store_v2', JSON.stringify(newStore));
            return newStore;
        }
    }
    function saveStore(store) {
        try {
            localStorage.setItem('tpm_store_v2', JSON.stringify(store));
            tabChannel.postMessage({
                type: 'STORE_UPDATED',
                data: { store: store, archive: tpmArchive },
                timestamp: Date.now()
            });
            window.dispatchEvent(new CustomEvent(STORE_EVENTS.UPDATED, {
                detail: { store, timestamp: Date.now() }
            }));
        } catch (e) {
            console.error('TPM: Failed to save store', e);
        }
    }
    function addEventToStore(propertyID, tenantID, eventCode, dailyRent, days, totalPayment, time) {
        if (!propertyID || propertyID === 'unknown' || !tenantID || tenantID === 'unknown') return;
        if (typeof dailyRent !== 'number' || isNaN(dailyRent)) dailyRent = 0;
        if (typeof days !== 'number' || isNaN(days)) days = 0;
        if (typeof totalPayment !== 'number' || isNaN(totalPayment)) totalPayment = 0;
        const store = getStore();
        if (!store.events) store.events = {};
        if (!store.events[propertyID]) store.events[propertyID] = [];
        const compressed = [
            time || Date.now(),
            eventCode || 0,
            Math.round(dailyRent) || 0,
            days || 0,
            Math.round(totalPayment) || 0,
            String(tenantID)
        ];
        store.events[propertyID].push(compressed);
        if (store.events[propertyID].length > 500) {
            store.events[propertyID] = store.events[propertyID].slice(-250);
        }
        saveStore(store);
    }
    function storeTenantInfo(tenantID, tenantName) {
        if (!tenantID || tenantID === 'unknown' || tenantID === '0' || tenantID === '') return;
        if (!tenantName || tenantName === 'Unknown' || tenantName === 'Unknown Tenant') {
            tenantName = 'Unknown';
        }
        if (tenantName.toLowerCase().includes('unknown') && tenantID.length < 5) return;
        if (tenantName.toLowerCase().includes('empty') && tenantID.length < 5) return;
        const store = getStore();
        if (!store.tenants) store.tenants = {};
        if (!store.tenants[tenantID]) {
            store.tenants[tenantID] = {
                n: tenantName,
                f: Date.now(),
                l: Date.now(),
                a: [],
                c: 0,
                s: 50
            };
        } else {
            if (store.tenants[tenantID].n !== tenantName && !store.tenants[tenantID].a.includes(tenantName) && tenantName !== 'Unknown') {
                store.tenants[tenantID].a.push(tenantName);
            }
            store.tenants[tenantID].l = Date.now();
            if (store.tenants[tenantID].n === 'Unknown' || !store.tenants[tenantID].n || store.tenants[tenantID].n === 'Unknown Tenant') {
                store.tenants[tenantID].n = tenantName;
            }
        }
        saveStore(store);
    }
    function showNotesBook(initialTenantID = null, initialTenantName = null, initialIsActive = null) {
        const overlay = document.createElement("div");
        overlay.className = "tpm-update-overlay";
        overlay.innerHTML = `
            <div class="tpm-note-popup">
                <h4 style="color:var(--tpm-info); margin:0 0 10px 0; display:flex; justify-content:space-between;">
                    <span>📚 Notes Book</span>
                    <span style="cursor:pointer; font-size:16px;" id="close-notes-book">✖</span>
                </h4>
                <div class="tpm-notes-book">
                    <div class="tpm-notes-sidebar">
                        <input type="text" class="tpm-notes-search" placeholder="🔍 Search by name..." id="notes-search">
                        <div class="tpm-filter-options">
                            <label><input type="checkbox" id="show-inactive" checked> Show inactive</label>
                        </div>
                        <div id="notes-list-container" style="overflow-y:auto; max-height:300px;">
                            <div class="tpm-loading"><div class="tpm-spinner"></div><span>Loading tenants...</span></div>
                        </div>
                        <div class="tpm-notes-total" id="notes-total">Loading...</div>
                    </div>
                    <div class="tpm-notes-page" id="notes-page">
                        <div style="text-align:center; color:var(--tpm-text-soft); padding:20px;">Select a tenant from the list</div>
                    </div>
                </div>
                <div style="margin-top:10px; display:flex; gap:10px; justify-content:flex-end;">
                    <button class="tpm-button" id="clear-all-notes">🗑️ Clear All Notes</button>
                </div>
            </div>
        `;
        document.body.appendChild(overlay);
        document.getElementById("close-notes-book").onclick = () => overlay.remove();
        const searchInput = document.getElementById("notes-search");
        const showInactiveCheck = document.getElementById("show-inactive");
        const listContainer = document.getElementById("notes-list-container");
        const notesPage = document.getElementById("notes-page");
        const notesTotal = document.getElementById("notes-total");
        let currentTenant = null;
        function loadTenantList() {
            const properties = JSON.parse(localStorage.getItem('prop_cache') || "[]");
            const store = getStore();
            const tenantMap = new Map();
            properties.forEach(([id, p]) => {
                if (p.rented_by && p.rented_by.id) {
                    const tenantId = String(p.rented_by.id);
                    const tenantName = p.rented_by.name;
                    if (tenantName && tenantName !== "Unknown" && !tenantName.toLowerCase().includes("empty")) {
                        if (!tenantMap.has(tenantId)) {
                            tenantMap.set(tenantId, {
                                id: tenantId,
                                name: tenantName,
                                isActive: true
                            });
                        }
                    }
                }
            });
            if (store.events) {
                Object.entries(store.events).forEach(([propID, events]) => {
                    events.forEach(e => {
                        const tenantId = e[5];
                        if (tenantId && tenantId.length > 5 && !tenantMap.has(tenantId)) {
                            const tenantName = store.tenants?.[tenantId]?.n || 'Unknown';
                            if (tenantName !== 'Unknown' && !tenantName.toLowerCase().includes('empty')) {
                                tenantMap.set(tenantId, {
                                    id: tenantId,
                                    name: tenantName,
                                    isActive: false
                                });
                            }
                        }
                    });
                });
            }
            const notes = safeJSON("tpm_tenant_notes");
            Object.keys(notes).forEach(tenantId => {
                if (!tenantMap.has(tenantId)) {
                    const firstNote = notes[tenantId][0] || {};
                    const tenantName = firstNote.tenantName;
                    if (tenantName && tenantName !== "Unknown" && !tenantName.toLowerCase().includes("empty")) {
                        tenantMap.set(tenantId, {
                            id: tenantId,
                            name: tenantName,
                            isActive: firstNote.isActive || false
                        });
                    }
                }
            });
            const tenants = Array.from(tenantMap.values()).map(t => ({
                ...t,
                count: notes[t.id]?.length || 0,
                hasNotes: notes[t.id]?.length > 0
            })).sort((a,b) => a.name.localeCompare(b.name));
            const searchTerm = searchInput.value.toLowerCase();
            const showInactive = showInactiveCheck.checked;
            const filtered = tenants.filter(t =>
                (showInactive || t.isActive) &&
                t.name.toLowerCase().includes(searchTerm)
            );
            notesTotal.textContent = `${filtered.length} tenants (${tenants.length} total)`;
            if (filtered.length === 0) {
                listContainer.innerHTML = '<div style="color:var(--tpm-text-soft); text-align:center; padding:10px;">No tenants found</div>';
                return;
            }
            listContainer.innerHTML = '<ul class="tpm-notes-list">' + filtered.map(t => `
                <li class="${t.id === currentTenant ? 'active' : ''} ${t.isActive ? '' : 'inactive'}" data-tenant="${t.id}" data-name="${t.name}">
                    <span class="tpm-notes-icon">${t.hasNotes ? '📖' : '📕'}</span>
                    <span class="tpm-notes-name">${t.name} [${t.id}]</span>
                    <span class="tpm-notes-count">(${t.count})</span>
                </li>
            `).join('') + '</ul>';
            listContainer.querySelectorAll('li').forEach(li => {
                li.onclick = () => {
                    document.querySelectorAll('.tpm-notes-list li').forEach(l => l.classList.remove('active'));
                    li.classList.add('active');
                    const tenantId = li.dataset.tenant;
                    const tenantName = li.dataset.name;
                    currentTenant = tenantId;
                    loadTenantNotes(tenantId, tenantName);
                };
            });
            if (filtered.length === 1 && !currentTenant) {
                const first = filtered[0];
                currentTenant = first.id;
                document.querySelector(`li[data-tenant="${first.id}"]`)?.classList.add('active');
                loadTenantNotes(first.id, first.name);
            } else if (initialTenantID && !currentTenant) {
                setTimeout(() => {
                    const match = filtered.find(t => t.id === String(initialTenantID));
                    if (match) {
                        currentTenant = initialTenantID;
                        document.querySelector(`li[data-tenant="${initialTenantID}"]`)?.classList.add('active');
                        loadTenantNotes(initialTenantID, initialTenantName || match.name);
                    }
                }, 100);
            }
        }
        function loadTenantNotes(tenantId, tenantName) {
            notesPage.innerHTML = '<div class="tpm-loading"><div class="tpm-spinner"></div><span>Loading notes...</span></div>';
            setTimeout(() => {
                const notes = safeJSON("tpm_tenant_notes")[tenantId] || [];
                const notesHtml = notes.map((note, index) => `
                    <div class="tpm-note-item ${note.isActive ? 'active' : 'inactive'}">
                        <span class="tpm-note-remove" data-tenant="${tenantId}" data-index="${index}">✖</span>
                        <div style="margin-bottom:4px;">${note.note}</div>
                        <div class="tpm-note-date">${note.formattedDate} ${note.isActive ? '• Active' : '• Inactive'}</div>
                    </div>
                `).join('');
                notesPage.innerHTML = `
                    <h5 style="margin:0 0 10px 0; color:var(--tpm-info);">${tenantName} [${tenantId}]</h5>
                    <div style="max-height:300px; overflow-y:auto; margin-bottom:10px;">
                        ${notesHtml || '<div style="color:var(--tpm-text-soft); text-align:center; padding:10px;">No notes for this tenant. Add one below!</div>'}
                    </div>
                    <textarea id="note-text-${tenantId}" placeholder="Enter note (max 1000 characters)..." maxlength="1000" style="width: 100%;box-sizing: border-box;resize: vertical;min-height: 80px;padding: 8px;margin: 5px 0;background: var(--tpm-input-bg);color: var(--tpm-text-strong);border: 1px solid var(--tpm-border);border-radius: 4px;font-size: 12px;font-family: inherit;"></textarea>
                    <div style="display:flex; gap:10px; justify-content:flex-end; margin-top:10px;">
                        <button class="tpm-button" style="background:var(--tpm-success-btn-bg);" id="save-note-${tenantId}">Add Note</button>
                    </div>
                `;
                document.getElementById(`save-note-${tenantId}`).onclick = () => {
                    const text = document.getElementById(`note-text-${tenantId}`).value.trim();
                    if (text) {
                        const isActive = notes.length ? notes[0].isActive : true;
                        addTenantNote(tenantId, tenantName, text, isActive);
                        loadTenantNotes(tenantId, tenantName);
                        loadTenantList();
                    }
                };
                notesPage.querySelectorAll('.tpm-note-remove').forEach(btn => {
                    btn.onclick = (e) => {
                        e.stopPropagation();
                        const tenant = btn.dataset.tenant;
                        const index = parseInt(btn.dataset.index);
                        removeTenantNote(tenant, index);
                        loadTenantNotes(tenant, tenantName);
                        loadTenantList();
                    };
                });
            }, 300);
        }
        document.getElementById('clear-all-notes').onclick = () => {
            if (confirm('⚠️ Are you sure? This will delete ALL tenant notes permanently.')) {
                localStorage.removeItem('tpm_tenant_notes');
                showNotification('All notes cleared', 'info');
                overlay.remove();
            }
        };
        searchInput.oninput = () => loadTenantList();
        showInactiveCheck.onchange = () => loadTenantList();
        loadTenantList();
    }
    function getTenantName(tenantID) {
        if (!tenantID || tenantID === 'unknown') return 'Unknown Tenant';
        const store = getStore();
        return store.tenants?.[tenantID]?.n || 'Unknown Tenant';
    }
    function updateTenantStats(tenantID, income, days, skipIfRecent) {
        if (!tenantID || tenantID === 'unknown') return;
        const store = getStore();
        if (store.tenants?.[tenantID]) {
            if (skipIfRecent && Date.now() - store.tenants[tenantID].l < 60000) return;
            store.tenants[tenantID].c = (store.tenants[tenantID].c || 0) + 1;
            let score = store.tenants[tenantID].s || 50;
            if (days >= 60) score += 5;
            if (days >= 30) score += 3;
            if (days < 7) score -= 5;
            if (store.tenants[tenantID].c > 1) score += 3;
            store.tenants[tenantID].s = Math.max(0, Math.min(100, score));
            saveStore(store);
        }
    }
    function cleanupTenantDB() {
        const store = getStore();
        if (!store.tenants) return;
        const twoYearsAgo = Date.now() - (730 * 24 * 60 * 60 * 1000);
        let removed = 0;
        Object.keys(store.tenants).forEach(id => {
            if (store.tenants[id].l < twoYearsAgo && (store.tenants[id].c || 0) === 0 && store.tenants[id].c < 2) {
                delete store.tenants[id];
                removed++;
            }
        });
        if (removed > 0) {
            saveStore(store);
            if (isDebugMode()) console.log(`TPM: Cleaned up ${removed} inactive tenants`);
        }
    }
    function showNotification(message, type = 'info') {
        const overlay = document.createElement("div");
        overlay.className = "tpm-notification-overlay";
        const colors = {
            success: { bg: 'var(--tpm-note-item-active)', border: 'var(--tpm-success)', icon: '✅' },
            error: { bg: 'var(--tpm-note-item-inactive)', border: 'var(--tpm-danger)', icon: '❌' },
            warning: { bg: 'var(--tpm-note-item-inactive)', border: 'var(--tpm-warning)', icon: '⚠️' },
            info: { bg: 'var(--tpm-card-bg)', border: 'var(--tpm-info)', icon: 'ℹ️' }
        };
        const color = colors[type] || colors.info;
        overlay.innerHTML = `
            <div class="tpm-notification-modal" style="background:${color.bg}; border-color:${color.border};">
                <div class="tpm-notification-title" style="color:${color.border};">${color.icon} Torn Property Manager</div>
                <div class="tpm-notification-body">${message}</div>
                <div class="tpm-notification-close">
                    <button class="tpm-button" id="tpm-notification-close-btn">OK</button>
                </div>
            </div>
        `;
        document.body.appendChild(overlay);
        let timeoutId;
        if (type !== 'error') {
            timeoutId = setTimeout(() => {
                if (document.body.contains(overlay)) overlay.remove();
            }, 5000);
        }
        document.getElementById("tpm-notification-close-btn").onclick = () => {
            if (timeoutId) clearTimeout(timeoutId);
            overlay.remove();
        };
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) {
                if (timeoutId) clearTimeout(timeoutId);
                overlay.remove();
            }
        });
    }
    function updateArchiveFromEvents(events, propertyType) {
        if (!events || !Array.isArray(events)) return;
        events.forEach(e => {
            const income = Array.isArray(e) ? e[4] : (e.income || 0);
            if (!income) return;
            tpmArchive.totalLifetimeIncome += income;
            tpmArchive.totalLifetimeLeases++;
            const time = Array.isArray(e) ? e[0] : e.time;
            const year = new Date(time).getFullYear();
            tpmArchive.yearlyIncome[year] = (tpmArchive.yearlyIncome[year] || 0) + income;
            if (!tpmArchive.propertyTypes[propertyType]) {
                tpmArchive.propertyTypes[propertyType] = { totalIncome: 0, totalLeases: 0 };
            }
            tpmArchive.propertyTypes[propertyType].totalIncome += income;
            tpmArchive.propertyTypes[propertyType].totalLeases++;
        });
        localStorage.setItem('tpm_archive', JSON.stringify(tpmArchive));
    }
    function getStoragePressure() {
        let totalBytes = 0;
        for (let key in localStorage) {
            if (key.startsWith('tpm_')) totalBytes += localStorage[key].length * 2;
        }
        return (totalBytes / 1024 / CONFIG.STORAGE_LIMIT_KB) * 100;
    }
    function getRetentionMonths(pressure) {
        if (pressure < 70) return 999;
        if (pressure < 80) return 36;
        if (pressure < 90) return 24;
        return 12;
    }
    function rotateLogsIfNeeded() {
        const pressure = getStoragePressure();
        if (pressure < 70) return;
        const store = getStore();
        if (!store.events) return;
        const cutoffMonths = getRetentionMonths(pressure);
        const cutoffTime = Date.now() - (cutoffMonths * 30 * 24 * 60 * 60 * 1000);
        let rotated = 0;
        let totalEvents = 0;
        Object.keys(store.events).forEach(propID => {
            totalEvents += store.events[propID].length;
        });
        const targetRemovalPercent = pressure > 95 ? 0.4 : pressure > 90 ? 0.3 : 0.2;
        const targetRemoval = Math.floor(totalEvents * targetRemovalPercent);
        let removed = 0;
        Object.keys(store.events).forEach(propID => {
            if (removed >= targetRemoval) return;
            const [oldEvents, keptEvents] = store.events[propID].reduce((acc, e) => {
                if (removed < targetRemoval && (e[0] < cutoffTime || pressure > 95)) {
                    acc[0].push(e);
                    removed++;
                } else {
                    acc[1].push(e);
                }
                return acc;
            }, [[], []]);
            if (oldEvents.length) {
                updateArchiveFromEvents(oldEvents, 'Unknown');
                rotated += oldEvents.length;
            }
            store.events[propID] = keptEvents;
        });
        saveStore(store);
        if (pressure > 95) {
            showNotification('⚠️ CRITICAL: Storage at ' + pressure.toFixed(1) + '%. Automatic cleanup performed. Consider exporting and clearing old data.', 'warning');
        } else if (pressure > 90) {
            showNotification('⚠️ Storage at ' + pressure.toFixed(1) + '%. Old lease records being rotated to save space.', 'warning');
        } else if (rotated > 0) {
            showNotification(`🧹 Rotated ${rotated} old events to save space (keeping last ${cutoffMonths} months)`, 'info');
        }
    }
    function getCustoms() {
        return {
            propnavbar: parseInt(localStorage.getItem('propnavbar')) || CONFIG.DEFAULT_SECOND_PROPNAV,
            items_per_page: parseInt(localStorage.getItem('items_per_page')) || CONFIG.DEFAULT_ITEMS_PER_PAGE,
            warn_lease_ex: parseInt(localStorage.getItem('warn_lease_ex')) || CONFIG.DEFAULT_WARN_EXPIRE,
            warn_lease_wa: parseInt(localStorage.getItem('warn_lease_wa')) || CONFIG.DEFAULT_WARN_CLOSE,
            historyLimit: parseInt(localStorage.getItem('history_limit')) || CONFIG.DEFAULT_HISTORY_LIMIT
        };
    }
    function getLinkTarget() {
        const pref = localStorage.getItem('tpm_link_target') || 'new';
        return pref === 'same' ? '_self' : '_blank';
    }
    function isLeaseAutofillEnabled() {
        const val = localStorage.getItem('tpm_autofill_lease');
        return val === null ? true : val === 'true';
    }
    function isDebugMode() {
        return localStorage.getItem('tpm_debug_mode') === 'true';
    }
    function updateThemeVariables() {
        const isDarkMode = document.body.classList.contains('dark-mode');
        const root = document.documentElement;
        if (isDarkMode) {
            root.style.setProperty('--tpm-bg', '#2d2d2d');
            root.style.setProperty('--tpm-card-bg', '#1f1f1f');
            root.style.setProperty('--tpm-card-header-bg', '#262626');
            root.style.setProperty('--tpm-card-footer-bg', '#1a1a1a');
            root.style.setProperty('--tpm-table-bg', '#262626');
            root.style.setProperty('--tpm-table-header-bg', '#1f1f1f');
            root.style.setProperty('--tpm-input-bg', '#333');
            root.style.setProperty('--tpm-button-bg', '#3a3a3a');
            root.style.setProperty('--tpm-button-hover', '#4a4a4a');
            root.style.setProperty('--tpm-border', '#333');
            root.style.setProperty('--tpm-text', '#ddd');
            root.style.setProperty('--tpm-text-soft', '#aaa');
            root.style.setProperty('--tpm-text-strong', '#fff');
            root.style.setProperty('--tpm-link', '#b5d6ff');
            root.style.setProperty('--tpm-success', '#9fdf9f');
            root.style.setProperty('--tpm-info', '#b5d6ff');
            root.style.setProperty('--tpm-warning', '#ffe08c');
            root.style.setProperty('--tpm-danger', '#ffa6a6');
            root.style.setProperty('--tpm-alert', '#ffa6ff');
            root.style.setProperty('--tpm-nav-bg', 'rgba(255,255,255,0.05)');
            root.style.setProperty('--tpm-nav-item-bg', '#444');
            root.style.setProperty('--tpm-nav-item-hover', '#555');
            root.style.setProperty('--tpm-divider', '#444');
            root.style.setProperty('--tpm-note-popup-bg', '#262626');
            root.style.setProperty('--tpm-note-item-active', '#1a3a1a');
            root.style.setProperty('--tpm-note-item-inactive', '#3a3a3a');
            root.style.setProperty('--tpm-note-remove', '#ffa6a6');
            root.style.setProperty('--tpm-note-remove-hover', '#ff6b6b');
            root.style.setProperty('--tpm-notes-sidebar-border', '#444');
            root.style.setProperty('--tpm-notes-search-bg', '#333');
            root.style.setProperty('--tpm-notes-list-hover', '#333');
            root.style.setProperty('--tpm-notes-list-active', '#1a3a1a');
            root.style.setProperty('--tpm-danger-btn-bg', '#8b3a3a');
            root.style.setProperty('--tpm-danger-btn-hover', '#a54545');
            root.style.setProperty('--tpm-success-btn-bg', '#2a5a2a');
            root.style.setProperty('--tpm-success-btn-hover', '#357535');
            root.style.setProperty('--tpm-calculator-bg', '#1a2a1a');
            root.style.setProperty('--tpm-calculator-border', '#2a3a2a');
        } else {
            root.style.setProperty('--tpm-bg', '#f5f5f5');
            root.style.setProperty('--tpm-card-bg', '#f0f0f0');
            root.style.setProperty('--tpm-card-header-bg', '#e8e8e8');
            root.style.setProperty('--tpm-card-footer-bg', '#e0e0e0');
            root.style.setProperty('--tpm-table-bg', '#f5f5f5');
            root.style.setProperty('--tpm-table-header-bg', '#e8e8e8');
            root.style.setProperty('--tpm-input-bg', '#fff');
            root.style.setProperty('--tpm-button-bg', '#e0e0e0');
            root.style.setProperty('--tpm-button-hover', '#d0d0d0');
            root.style.setProperty('--tpm-border', '#ccc');
            root.style.setProperty('--tpm-text', '#333');
            root.style.setProperty('--tpm-text-soft', '#666');
            root.style.setProperty('--tpm-text-strong', '#000');
            root.style.setProperty('--tpm-link', '#0066cc');
            root.style.setProperty('--tpm-success', '#2e7d32');
            root.style.setProperty('--tpm-info', '#0277bd');
            root.style.setProperty('--tpm-warning', '#b76e00');
            root.style.setProperty('--tpm-danger', '#c62828');
            root.style.setProperty('--tpm-alert', '#aa00aa');
            root.style.setProperty('--tpm-nav-bg', 'rgba(0,0,0,0.05)');
            root.style.setProperty('--tpm-nav-item-bg', '#ddd');
            root.style.setProperty('--tpm-nav-item-hover', '#ccc');
            root.style.setProperty('--tpm-divider', '#aaa');
            root.style.setProperty('--tpm-note-popup-bg', '#f5f5f5');
            root.style.setProperty('--tpm-note-item-active', '#c8e6c9');
            root.style.setProperty('--tpm-note-item-inactive', '#e0e0e0');
            root.style.setProperty('--tpm-note-remove', '#c62828');
            root.style.setProperty('--tpm-note-remove-hover', '#b71c1c');
            root.style.setProperty('--tpm-notes-sidebar-border', '#ccc');
            root.style.setProperty('--tpm-notes-search-bg', '#fff');
            root.style.setProperty('--tpm-notes-list-hover', '#e0e0e0');
            root.style.setProperty('--tpm-notes-list-active', '#c8e6c9');
            root.style.setProperty('--tpm-danger-btn-bg', '#dc3545');
            root.style.setProperty('--tpm-danger-btn-hover', '#c82333');
            root.style.setProperty('--tpm-success-btn-bg', '#28a745');
            root.style.setProperty('--tpm-success-btn-hover', '#218838');
            root.style.setProperty('--tpm-calculator-bg', '#e8f0e8');
            root.style.setProperty('--tpm-calculator-border', '#c0d0c0');
        }
    }
    updateThemeVariables();
    const themeObserver = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            if (mutation.attributeName === 'class') {
                updateThemeVariables();
            }
        });
    });
    themeObserver.observe(document.body, { attributes: true });
    const TPM_UI_STYLE = `
        <style>
            .tpm-stats-bar{display:grid;grid-template-columns: repeat(auto-fit,minmax(100px,1fr));gap:8px;margin-bottom:12px;}
            .tpm-stat{background:var(--tpm-card-bg);border:1px solid var(--tpm-border);border-radius:8px;padding:8px;text-align:center;}
            .tpm-stat-label{display:block;font-size:11px;color:var(--tpm-text-soft);margin-bottom:3px;text-transform:uppercase;letter-spacing:0.5px;}
            .tpm-stat-value{font-size:18px;font-weight:700;color:var(--tpm-text-strong);}
            .tpm-stat.danger .tpm-stat-value{color:var(--tpm-danger);}
            .tpm-panel-close{position:absolute;top:8px;right:10px;font-size:13px;cursor:pointer;color:var(--tpm-text-soft);padding:4px 8px;border-radius:4px;}
            .tpm-panel-close:hover{background:var(--tpm-button-bg);color:var(--tpm-text-strong);}
            .tpm-stat.alert .tpm-stat-value{color:var(--tpm-alert);}
            .tpm-stat.warn .tpm-stat-value{color:var(--tpm-warning);}
            .tpm-card{background:var(--tpm-card-bg);border-radius:8px;border:1px solid var(--tpm-border);overflow:hidden;margin-bottom:10px;}
            .tpm-card-header{background:var(--tpm-card-header-bg);padding:10px 12px;border-bottom:1px solid var(--tpm-border);font-size:14px;font-weight:600;cursor:pointer;}
            .tpm-card-header:hover{background:var(--tpm-button-hover);}
            .tpm-card-body{padding:12px;}
            .tpm-card-footer{background:var(--tpm-card-footer-bg);padding:8px 12px;border-top:1px solid var(--tpm-border);font-size:12px;}
            .tpm-grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;}
            .tpm-grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;}
            .tpm-grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;}
            .tpm-text-small{font-size:11px;color:var(--tpm-text-soft);}
            .tpm-text-normal{font-size:13px;color:var(--tpm-text);}
            .tpm-text-large{font-size:16px;font-weight:600;color:var(--tpm-text-strong);}
            .tpm-text-success{color:var(--tpm-success);}
            .tpm-text-info{color:var(--tpm-info);}
            .tpm-text-warning{color:var(--tpm-warning);}
            .tpm-text-danger{color:var(--tpm-danger);}
            .tpm-link{color:var(--tpm-link);text-decoration:none;font-size:13px;}
            .tpm-link:hover{color:var(--tpm-success);text-decoration:underline;}
            .tpm-button{background:var(--tpm-button-bg);color:var(--tpm-text-strong);border:none;padding:5px 10px;border-radius:4px;cursor:pointer;font-size:12px;font-weight:500;}
            .tpm-button:hover{background:var(--tpm-button-hover);}
            .tpm-button-sm{padding:3px 6px;font-size:11px;}
            .tpm-button-icon{background:var(--tpm-button-bg);color:var(--tpm-text-strong);padding:5px;border-radius:4px;text-decoration:none;font-size:12px;cursor:pointer;}
            .tpm-button-icon:hover{background:var(--tpm-button-hover);}
            .tpm-badge{display:inline-block;padding:3px 8px;border-radius:12px;font-size:11px;font-weight:600;}
            .tpm-table{width:100%;border-collapse:collapse;color:var(--tpm-text-strong);background:var(--tpm-table-bg);border-radius:8px;overflow:hidden;font-size:13px;}
            .tpm-table th{background:var(--tpm-table-header-bg);padding:10px 8px;font-size:12px;color:var(--tpm-info);text-align:left;border-bottom:2px solid var(--tpm-border);}
            .tpm-table td{padding:8px;border-bottom:1px solid var(--tpm-border);}
            .tpm-table tr:last-child td{border-bottom:none;}
            .tpm-nav{display:flex;justify-content:center;align-items:center;gap:12px;background:var(--tpm-nav-bg);padding:6px 16px;border-radius:40px;margin:10px 0;}
            .tpm-nav-item{background:var(--tpm-nav-item-bg);color:var(--tpm-text-strong);border:none;padding:6px 12px;border-radius:6px;cursor:pointer;font-size:12px;}
            .tpm-nav-item:hover{background:var(--tpm-nav-item-hover);}
            .tpm-nav-divider{width:1px;height:20px;background:var(--tpm-divider);}
            .tpm-mb-1{margin-bottom:5px;}
            .tpm-mb-2{margin-bottom:10px;}
            .tpm-mb-3{margin-bottom:15px;}
            .tpm-mb-4{margin-bottom:20px;}
            .tpm-mt-1{margin-top:5px;}
            .tpm-mt-2{margin-top:10px;}
            .tpm-mt-3{margin-top:15px;}
            .tpm-flex{display:flex;}
            .tpm-flex-between{display:flex;justify-content:space-between;align-items:center;}
            .tpm-gap-1{gap:5px;}
            .tpm-gap-2{gap:10px;}
            .tpm-update-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:99999;display:flex;align-items:center;justify-content:center;}
            .tpm-update-modal{width:500px;background:var(--tpm-card-bg);border:1px solid var(--tpm-info);border-radius:12px;padding:20px;color:var(--tpm-text);font-size:13px;}
            .tpm-update-title{font-size:18px;font-weight:700;color:var(--tpm-info);margin-bottom:15px;}
            .tpm-notification-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:100000;display:flex;align-items:center;justify-content:center;}
            .tpm-notification-modal{width:400px;background:var(--tpm-card-bg);border:2px solid;border-radius:12px;padding:20px;color:var(--tpm-text);font-size:13px;box-shadow:0 0 20px rgba(0,0,0,0.5);}
            .tpm-notification-title{font-size:16px;font-weight:600;margin-bottom:15px;padding-bottom:10px;border-bottom:1px solid var(--tpm-border);}
            .tpm-notification-body{margin-bottom:20px;line-height:1.5;white-space:pre-line;}
            .tpm-notification-close{text-align:right;}
            .tpm-note-popup{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:750px;height:500px;background:var(--tpm-note-popup-bg);border:2px solid var(--tpm-info);border-radius:12px;padding:20px;z-index:100001;color:var(--tpm-text);box-shadow:0 0 20px rgba(0,0,0,0.5);display:flex;flex-direction:column;}
            .tpm-note-popup textarea{width:100%;height:60px;background:var(--tpm-input-bg);color:var(--tpm-text-strong);border:1px solid var(--tpm-border);border-radius:4px;padding:6px;margin:5px 0;font-size:12px;resize:vertical;}
            .tpm-note-item{padding:8px;margin-bottom:8px;border-radius:4px;font-size:11px;position:relative;line-height:1.4;}
            .tpm-note-item.active{background:var(--tpm-note-item-active);border-left:3px solid var(--tpm-success);}
            .tpm-note-item.inactive{background:var(--tpm-note-item-inactive);border-left:3px solid var(--tpm-text-soft);opacity:0.8;}
            .tpm-note-date{color:var(--tpm-text-soft);font-size:9px;margin-top:4px;padding-top:2px;border-top:1px dotted var(--tpm-border);}
            .tpm-note-remove{float:right;cursor:pointer;color:var(--tpm-note-remove);font-size:12px;padding:0 5px;}
            .tpm-note-remove:hover{color:var(--tpm-note-remove-hover);}
            .tpm-notes-book{display:flex;height:100%;gap:15px;}
            .tpm-notes-sidebar{width:250px;border-right:1px solid var(--tpm-notes-sidebar-border);padding-right:15px;overflow-y:auto;}
            .tpm-notes-page{flex:1;padding-left:15px;overflow-y:auto;position:relative;}
            .tpm-notes-page::before{content:'';position:absolute;top:0;left:0;width:10px;height:100%;background:linear-gradient(to right, rgba(0,0,0,0.1), transparent);pointer-events:none;}
            .tpm-notes-search{width:100%;padding:6px;margin-bottom:10px;background:var(--tpm-notes-search-bg);color:var(--tpm-text-strong);border:1px solid var(--tpm-border);border-radius:4px;font-size:12px;}
            .tpm-notes-list{list-style:none;padding:0;margin:0;}
            .tpm-notes-list li{padding:6px 8px;margin-bottom:2px;border-radius:4px;cursor:pointer;font-size:12px;display:flex;align-items:center;}
            .tpm-notes-list li:hover{background:var(--tpm-notes-list-hover);}
            .tpm-notes-list li.active{background:var(--tpm-notes-list-active);color:var(--tpm-success);}
            .tpm-notes-list li.inactive{opacity:0.6;}
            .tpm-notes-icon{font-size:12px;margin-right:6px;width:16px;text-align:center;}
            .tpm-notes-name{flex:1;}
            .tpm-notes-count{color:var(--tpm-text-soft);font-size:10px;margin-left:4px;}
            .tpm-notes-total{font-size:11px;color:var(--tpm-text-soft);margin-top:10px;padding-top:5px;border-top:1px solid var(--tpm-notes-sidebar-border);}
            .tpm-loading{display:flex;align-items:center;justify-content:center;padding:20px;gap:8px;}
            .tpm-spinner{width:20px;height:20px;border:2px solid var(--tpm-border);border-top-color:var(--tpm-info);border-radius:50%;animation:spin 1s linear infinite;}
            @keyframes spin{0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}}
            .tpm-filter-options{display:flex;gap:8px;margin:8px 0;font-size:11px;}
            .tpm-filter-options label{cursor:pointer;}
            .tpm-note-icon {cursor:pointer;font-size:12px;margin-right:4px;color:var(--tpm-text-soft);}
            .tpm-note-icon:hover {color:var(--tpm-info);}
            .tpm-active-indicator {font-size:10px;color:var(--tpm-success);margin-left:6px;font-weight:normal;}
            .tpm-inactive-indicator {font-size:10px;color:var(--tpm-text-soft);margin-left:6px;font-weight:normal;}
            .tpm-performer-card {background:var(--tpm-card-header-bg);border-radius:6px;padding:10px;border-left:3px solid;transition:all 0.2s;}
            .tpm-performer-card:hover {background:var(--tpm-button-hover);}
            .tpm-performer-rank {font-size:9px;color:var(--tpm-text-soft);margin-bottom:4px;}
            .tpm-performer-name {font-size:13px;font-weight:600;display:flex;align-items:center;flex-wrap:wrap;gap:4px;margin-bottom:2px;}
            .tpm-performer-stats {font-size:11px;display:flex;justify-content:space-between;margin-top:6px;padding-top:4px;border-top:1px dashed var(--tpm-border);}
            .tpm-performer-value {color:var(--tpm-success);font-weight:600;}
            .tpm-performer-days {color:var(--tpm-warning);}
            .tpm-performer-rent {color:var(--tpm-info);}
            .tpm-property-name {color:var(--tpm-text-soft);font-size:10px;margin-top:2px;}
            .tpm-calculator {background:var(--tpm-calculator-bg);border:1px solid var(--tpm-calculator-border);border-radius:6px;padding:10px 16px;margin:4px 0;font-size:13px;}
            .tpm-calculator > div {display:flex;justify-content:space-between;align-items:center;width:100%;gap:8px;}
            .tpm-calculator-value {font-weight:600;color:var(--tpm-success);background:rgba(46,125,50,0.1);padding:4px 10px;border-radius:20px;display:inline-block;}
            .tpm-history-section {margin-top:15px;border-top:1px solid var(--tpm-border);padding-top:10px;overflow-y:auto;}
            .tpm-history-title {font-size:12px;color:var(--tpm-info);margin-bottom:8px;font-weight:600;position:sticky;top:0;background:var(--tpm-card-header-bg);padding:4px 0;}
            .tpm-history-item {font-size:11px;padding:8px 4px;border-bottom:1px dotted var(--tpm-border);display:flex;flex-direction:column;gap:2px;}
            .tpm-history-item:last-child {border-bottom:none;}
            .tpm-history-main {display:flex;justify-content:space-between;}
            .tpm-history-details {display:flex;justify-content:space-between;font-size:10px;color:var(--tpm-text-soft);}
            .tpm-history-empty {font-size:11px;color:var(--tpm-text-soft);text-align:center;padding:8px;background:var(--tpm-card-bg);border-radius:4px;}
            .tpm-market-suggestion {background:var(--tpm-card-bg);border-radius:4px;padding:6px 10px;margin:5px 0;font-size:11px;color:var(--tpm-text-soft);}
            .tpm-loader-container {position:relative;display:inline-block;margin-left:10px;}
            .tpm-inline-loader {display:inline-flex;align-items:center;gap:5px;padding:4px 8px;background:var(--tpm-card-bg);border-radius:4px;font-size:11px;}
            .help-section, .help-section p, .help-section li, .help-section td, .help-section th { color: var(--tpm-text) !important; font-size:13px !important; }
            .help-section a { color: var(--tpm-link) !important; }
            .help-section code { color: var(--tpm-warning) !important; background: var(--tpm-input-bg); padding: 2px 5px; border-radius: 3px; }
            .help-section h5 { color: var(--tpm-info) !important; font-size:15px !important; margin:15px 0 10px 0; }
        </style>
    `;
    document.head.insertAdjacentHTML("beforeend", TPM_UI_STYLE);
    function getStorageInfo() {
        let totalBytes = 0;
        let logCount = 0;
        for (let key in localStorage) {
            if (key.startsWith('tpm_')) totalBytes += localStorage[key].length * 2;
        }
        const store = getStore();
        if (store.events) {
            Object.values(store.events).forEach(events => {
                logCount += events.length;
            });
        }
        const pressure = (totalBytes / 1024 / CONFIG.STORAGE_LIMIT_KB) * 100;
        return {
            sizeKB: (totalBytes / 1024).toFixed(1),
            logCount: logCount,
            pressure: pressure,
            retentionMonths: getRetentionMonths(pressure)
        };
    }
    function encryptAPIKey(key) {
        return btoa(key.split('').reverse().join(''));
    }
    function decryptAPIKey(encrypted) {
        try {
            const decrypted = atob(encrypted).split('').reverse().join('');
            return /^[a-zA-Z0-9]{16}$/.test(decrypted) ? decrypted : '';
        } catch (e) {
            return '';
        }
    }
    function exportData() {
        const store = getStore();
        const apiKey = localStorage.getItem('tpmApiKey');
        const data = {
            store: store,
            autofill: safeJSON("tpm_property_autofill"),
            prevExtensions: safeJSON("tpm_prev_extensions"),
            tenantNotes: safeJSON("tpm_tenant_notes"),
            rentHistory: safeJSON("tpm_rent_history"),
            archive: tpmArchive,
            apiKey: apiKey ? encryptAPIKey(apiKey) : '',
            exportDate: new Date().toISOString(),
            version: "3.0.0"
        };
        const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'});
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `tpm-backup-${new Date().toISOString().slice(0,10)}.json`;
        a.click();
        URL.revokeObjectURL(url);
        showNotification('Data exported successfully!', 'success');
    }
    function importData(jsonData) {
        try {
            const data = JSON.parse(jsonData);
            if (data.store) localStorage.setItem('tpm_store_v2', JSON.stringify(data.store));
            if (data.autofill) localStorage.setItem('tpm_property_autofill', JSON.stringify(data.autofill));
            if (data.prevExtensions) localStorage.setItem('tpm_prev_extensions', JSON.stringify(data.prevExtensions));
            if (data.tenantNotes) localStorage.setItem('tpm_tenant_notes', JSON.stringify(data.tenantNotes));
            if (data.rentHistory) localStorage.setItem('tpm_rent_history', JSON.stringify(data.rentHistory));
            if (data.archive) {
                tpmArchive = data.archive;
                localStorage.setItem('tpm_archive', JSON.stringify(data.archive));
            }
            if (data.apiKey) {
                const decrypted = decryptAPIKey(data.apiKey);
                if (decrypted) localStorage.setItem('tpmApiKey', decrypted);
            }
            showNotification('Data imported successfully! Reloading...', 'success');
            setTimeout(() => location.reload(), 1500);
        } catch (e) {
            showNotification('Invalid backup file!', 'error');
        }
    }
    function addTenantNote(tenantID, tenantName, note, isActive) {
        const notes = safeJSON("tpm_tenant_notes");
        if (!notes[tenantID]) notes[tenantID] = [];
        notes[tenantID].push({
            note: note.substring(0, 1000),
            tenantName: tenantName,
            timestamp: Date.now(),
            formattedDate: new Date().toLocaleDateString(),
            isActive: isActive
        });
        localStorage.setItem('tpm_tenant_notes', JSON.stringify(notes));
        refreshTablesFromCache();
    }
    function removeTenantNote(tenantID, index) {
        const notes = safeJSON("tpm_tenant_notes");
        if (notes[tenantID] && notes[tenantID][index]) {
            notes[tenantID].splice(index, 1);
            if (notes[tenantID].length === 0) {
                delete notes[tenantID];
            }
            localStorage.setItem('tpm_tenant_notes', JSON.stringify(notes));
            refreshTablesFromCache();
        }
    }
    function getTenantNotes(tenantID) {
        const notes = safeJSON("tpm_tenant_notes");
        return notes[tenantID] || [];
    }
    function trackRentChange(propertyID, oldRent, newRent, tenantID, tenantName) {
        if (oldRent === newRent) return;
        const history = safeJSON("tpm_rent_history");
        if (!history[propertyID]) history[propertyID] = [];
        history[propertyID].push({
            oldRent: oldRent,
            newRent: newRent,
            tenantID: tenantID,
            tenantName: tenantName,
            timestamp: Date.now(),
            formattedDate: new Date().toLocaleDateString()
        });
        localStorage.setItem('tpm_rent_history', JSON.stringify(history));
    }
    function getRentHistory(propertyID) {
        const history = safeJSON("tpm_rent_history");
        return history[propertyID] || [];
    }
    let activeLoadingCount = 0;
    function showInlineLoader(container, message = 'Loading...') {
        const loaderId = 'tpm-loader-' + Date.now();
        const loader = document.createElement('div');
        loader.id = loaderId;
        loader.className = 'tpm-loader-container';
        loader.innerHTML = `<div class="tpm-inline-loader"><div class="tpm-spinner" style="width:14px;height:14px;"></div><span>${message}</span></div>`;
        container.appendChild(loader);
        activeLoadingCount++;
        return loaderId;
    }
    function hideInlineLoader(loaderId) {
        const loader = document.getElementById(loaderId);
        if (loader) loader.remove();
        activeLoadingCount = Math.max(0, activeLoadingCount - 1);
    }
    async function rateLimitedFetch(url, retryCount = 0) {
        const waitTime = checkRateLimit();
        if (waitTime > 0) {
            await new Promise(resolve => setTimeout(resolve, waitTime));
        }
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), 15000);
        try {
            const response = await fetch(url, {
                headers: {
                    'User-Agent': TPM_USER_AGENT
                },
                signal: controller.signal
            });
            clearTimeout(timeoutId);
            updateRateLimitCounters();
            const clonedResponse = response.clone();
            try {
                const data = await clonedResponse.json();
                if (data.error) {
                    const code = data.error.code;
                    if (code === 5) {
                        console.error("TPM: Rate limit hit, backing off");
                        if (retryCount < CONFIG.MAX_RETRY_ATTEMPTS) {
                            showNotification(`⚠️ Rate limit hit, retrying (${retryCount + 1}/${CONFIG.MAX_RETRY_ATTEMPTS})...`, 'warning');
                            releaseTabLock(TAB_COORDINATION.PROPERTIES_LOCK);
                            await new Promise(resolve => setTimeout(resolve, 10000 * (retryCount + 1)));
                            return rateLimitedFetch(url, retryCount + 1);
                        } else {
                            showNotification('⚠️ Rate limit exceeded maximum retries. Please try again later.', 'error');
                            return null;
                        }
                    } else if (code === 2) {
                        showNotification('❌ Invalid API key. Please check your key.', 'error');
                        return null;
                    } else if (code === 7 || code === 6) {
                        showNotification('❌ Insufficient permissions. FULL ACCESS key required.', 'error');
                        return null;
                    } else {
                        console.error(`TPM: API error ${code}:`, data.error);
                        showNotification(`❌ API error: ${data.error.error || 'Unknown error'}`, 'error');
                        return null;
                    }
                }
            } catch (e) {
            }
            return response;
        } catch (error) {
            clearTimeout(timeoutId);
            console.error("TPM: Fetch error", error);
            releaseTabLock(TAB_COORDINATION.PROPERTIES_LOCK);
            if (retryCount < CONFIG.MAX_RETRY_ATTEMPTS) {
                await new Promise(resolve => setTimeout(resolve, 5000 * (retryCount + 1)));
                return rateLimitedFetch(url, retryCount + 1);
            }
            throw error;
        }
    }
    function handleApiError(data) {
        if (!data || !data.error) return false;
        const code = data.error.code;
        if (code === 2) {
            localStorage.removeItem('tpmApiKey');
            showNotification("⚠️ Your Torn API key is no longer valid.\n\nIt may have been revoked or regenerated.\nYour stored key has been removed.\nPlease enter a new FULL ACCESS key in Settings.", 'warning');
            return true;
        }
        if (code === 5) {
            console.error("TPM: Rate limit detected in API error handler");
            return true;
        }
        if (code === 7 || code === 6) {
            showNotification("❌ This script requires a FULL ACCESS API key to read user logs.", 'error');
            return true;
        }
        return false;
    }
    async function getAllProperties(apiKey, forceOverride = false) {
        const refreshBtn = document.getElementById('refreshBtn');
        if (!acquireTabLock(TAB_COORDINATION.PROPERTIES_LOCK)) {
            if (isDebugMode()) console.log('TPM: Another tab is fetching properties, waiting...');
            const updated = await waitForTabUpdate(TAB_COORDINATION.PROPERTIES_LOCK);
            if (updated) {
                const cachedData = localStorage.getItem('prop_cache');
                if (cachedData) {
                    if (isDebugMode()) console.log('TPM: Using properties from another tab');
                    return JSON.parse(cachedData);
                }
            }
            releaseTabLock(TAB_COORDINATION.PROPERTIES_LOCK);
            return null;
        }
        const loaderId = showInlineLoader(document.getElementById('torn-property-manager-root'), 'Fetching properties...');
        const NOW = Date.now();
        const COOLDOWN = 30 * 60 * 1000;
        const OVERRIDE_DELAY = 60 * 1000;
        const lastCall = parseInt(localStorage.getItem('prop_last_call')) || 0;
        const cachedData = localStorage.getItem('prop_cache');
        const waitTime = forceOverride ? OVERRIDE_DELAY : COOLDOWN;
        if (lastCall !== 0) {
            const timePassed = NOW - lastCall;
            if (timePassed < waitTime) {
                const remaining = Math.ceil((waitTime - timePassed) / 1000);
                if (forceOverride) {
                    showNotification(`⚠️ Override cooldown active. Please wait ${remaining} more seconds.`, 'warning');
                    if (refreshBtn) {
                        refreshBtn.disabled = true;
                        refreshBtn.style.opacity = '0.5';
                        refreshBtn.style.cursor = 'not-allowed';
                        setTimeout(() => {
                            refreshBtn.disabled = false;
                            refreshBtn.style.opacity = '1';
                            refreshBtn.style.cursor = 'pointer';
                        }, waitTime - timePassed);
                    }
                }
                hideInlineLoader(loaderId);
                releaseTabLock(TAB_COORDINATION.PROPERTIES_LOCK);
                if (cachedData) return JSON.parse(cachedData);
                throw new Error(`Rate limited. Please wait ${remaining}s.`);
            }
        }
        let all = [], offset = 0, more = true;
        try {
            while (more) {
                let res;
                try {
                    res = await rateLimitedFetch(`https://api.torn.com/v2/user/properties?filters=ownedByUser&key=${apiKey}&offset=${offset}`);
                    if (!res) {
                        hideInlineLoader(loaderId);
                        releaseTabLock(TAB_COORDINATION.PROPERTIES_LOCK);
                        return cachedData ? JSON.parse(cachedData) : [];
                    }
                } catch (fetchError) {
                    console.error("TPM: Fetch failed, skipping batch", fetchError);
                    break;
                }
                const data = await res.json();
                if (handleApiError(data)) {
                    hideInlineLoader(loaderId);
                    releaseTabLock(TAB_COORDINATION.PROPERTIES_LOCK);
                    if (refreshBtn) {
                        refreshBtn.disabled = false;
                        refreshBtn.style.opacity = '1';
                        refreshBtn.style.cursor = 'pointer';
                    }
                    return cachedData ? JSON.parse(cachedData) : [];
                }
                if (data.error) throw new Error(data.error.error);
                if (!data.properties) break;
                const entries = Object.entries(data.properties);
                all = all.concat(entries);
                more = entries.length >= CONFIG.API_BATCH_SIZE;
                offset += CONFIG.API_BATCH_SIZE;
                if (more) await new Promise(resolve => setTimeout(resolve, 1000));
            }
            await initialiseTenantIntel();
            const timestamp = Date.now();
            localStorage.setItem('prop_last_call', timestamp);
            localStorage.setItem('prop_cache', JSON.stringify(all));
            tabChannel.postMessage({
                type: 'PROPERTIES_UPDATED',
                data: { properties: all, timestamp },
                timestamp: timestamp
            });
            TAB_COORDINATION.lastUpdate = timestamp;
            if (isDebugMode()) console.log(`TPM: Fetched ${all.length} properties`);
            rotateLogsIfNeeded();
        } finally {
            hideInlineLoader(loaderId);
            releaseTabLock(TAB_COORDINATION.PROPERTIES_LOCK);
            if (refreshBtn) {
                refreshBtn.disabled = false;
                refreshBtn.style.opacity = '1';
                refreshBtn.style.cursor = 'pointer';
            }
        }
        return all;
    }
    async function initialiseTenantIntel() {
        const apiKey = localStorage.getItem("tpmApiKey");
        if (!apiKey) return;
        const logs = await fetchLeaseLogs(apiKey);
        processLeaseLogs(logs);
    }
    async function fetchLeaseLogs(apiKey) {
        if (!acquireTabLock(TAB_COORDINATION.LOGS_LOCK)) {
            if (isDebugMode()) console.log('TPM: Another tab is fetching logs, waiting...');
            const updated = await waitForTabUpdate(TAB_COORDINATION.LOGS_LOCK);
            if (updated) {
                const cached = localStorage.getItem('tenant_logs_cache');
                if (cached) {
                    if (isDebugMode()) console.log('TPM: Using logs from another tab');
                    const timestamp = Date.now();
                    localStorage.setItem('tenant_logs_last_call', timestamp);
                    TAB_COORDINATION.lastUpdate = timestamp;
                    return JSON.parse(cached);
                }
            }
            releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
            return [];
        }
        const NOW = Date.now();
        const COOLDOWN = 30 * 60 * 1000;
        const lastCall = parseInt(localStorage.getItem('tenant_logs_last_call')) || 0;
        const cached = localStorage.getItem('tenant_logs_cache');
        if (NOW - lastCall < COOLDOWN && cached) {
            releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
            return JSON.parse(cached);
        }
        return new Promise((resolve) => {
            const waitTime = checkRateLimit();
            if (waitTime > 0) {
                setTimeout(() => {
                    makeLogRequest(apiKey, resolve);
                }, waitTime);
            } else {
                makeLogRequest(apiKey, resolve);
            }
        });
    }
    function makeLogRequest(apiKey, resolve) {
        let retryTimeout;
        const timeoutId = setTimeout(() => {
            releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
            resolve([]);
        }, 15000);
        GM_xmlhttpRequest({
            method: "GET",
            url: `https://api.torn.com/user/?selections=log&log=5937,5939,5940,5943,5948,5951,5953&limit=1000&key=${apiKey}`,
            headers: {
                "Accept": "application/json",
                "User-Agent": TPM_USER_AGENT
            },
            timeout: 15000,
            onload: function(response) {
                clearTimeout(timeoutId);
                updateRateLimitCounters();
                try {
                    const data = JSON.parse(response.responseText);
                    if (data.error && data.error.code === 5) {
                        console.error("TPM: Rate limit hit in log request");
                        if (RATE_LIMIT.retryCount < CONFIG.MAX_RETRY_ATTEMPTS) {
                            RATE_LIMIT.retryCount++;
                            showNotification(`⚠️ Rate limit hit, retrying (${RATE_LIMIT.retryCount}/${CONFIG.MAX_RETRY_ATTEMPTS})...`, 'warning');
                            releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
                            retryTimeout = setTimeout(() => {
                                if (document.getElementById('torn-property-manager-root')) {
                                    makeLogRequest(apiKey, resolve);
                                } else {
                                    resolve([]);
                                }
                            }, 10000 * RATE_LIMIT.retryCount);
                        } else {
                            showNotification('⚠️ Rate limit exceeded maximum retries. Please try again later.', 'error');
                            releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
                            resolve([]);
                        }
                        return;
                    }
                    RATE_LIMIT.retryCount = 0;
                    if (data.error) {
                        const code = data.error.code;
                        if (code === 2) {
                            showNotification('❌ Invalid API key. Please check your key.', 'error');
                        } else if (code === 7 || code === 6) {
                            showNotification('❌ Insufficient permissions. FULL ACCESS key required.', 'error');
                        } else {
                            console.error("TPM: Log API error:", data.error);
                        }
                        releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
                        resolve([]);
                        return;
                    }
                    if (!data.log || typeof data.log !== 'object') {
                        releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
                        resolve([]);
                        return;
                    }
                    const logs = Object.values(data.log);
                    const timestamp = Date.now();
                    localStorage.setItem('tenant_logs_cache', JSON.stringify(logs));
                    localStorage.setItem('tenant_logs_last_call', timestamp);
                    tabChannel.postMessage({
                        type: 'LOGS_UPDATED',
                        data: { logs, timestamp },
                        timestamp: timestamp
                    });
                    TAB_COORDINATION.lastUpdate = timestamp;
                    if (isDebugMode()) console.log(`TPM: Fetched ${logs.length} lease events`);
                    releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
                    resolve(logs);
                } catch(e) {
                    console.error("TPM: Log parse error", e);
                    releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
                    resolve([]);
                }
            },
            onerror: () => {
                clearTimeout(timeoutId);
                console.error("TPM: Failed to fetch lease logs");
                releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
                resolve([]);
            },
            ontimeout: () => {
                clearTimeout(timeoutId);
                console.error("TPM: Log request timeout");
                releaseTabLock(TAB_COORDINATION.LOGS_LOCK);
                resolve([]);
            }
        });
        window.addEventListener('beforeunload', () => {
            if (retryTimeout) clearTimeout(retryTimeout);
            clearTimeout(timeoutId);
        });
    }
    async function verifyApiKey(key) {
        const saveBtn = document.querySelector('.save-settings');
        let retryTimeout;
        let buttonTimeout;
        if (!key || key.length !== 16 || !/^[a-zA-Z0-9]+$/.test(key)) {
            showNotification("❌ Invalid API key format. Must be 16 alphanumeric characters.", 'error');
            return false;
        }
        if (!acquireTabLock(TAB_COORDINATION.VERIFY_LOCK)) {
            showNotification("⚠️ Another tab is already verifying an API key. Please wait.", 'warning');
            return false;
        }
        if (saveBtn) {
            saveBtn.disabled = true;
            saveBtn.style.opacity = '0.5';
            saveBtn.style.cursor = 'not-allowed';
        }
        const cleanup = () => {
            if (retryTimeout) clearTimeout(retryTimeout);
            if (buttonTimeout) clearTimeout(buttonTimeout);
            releaseTabLock(TAB_COORDINATION.VERIFY_LOCK);
            if (saveBtn) {
                saveBtn.disabled = false;
                saveBtn.style.opacity = '1';
                saveBtn.style.cursor = 'pointer';
            }
        };
        window.addEventListener('beforeunload', cleanup);
        const timeoutId = setTimeout(() => {
            cleanup();
            window.removeEventListener('beforeunload', cleanup);
            showNotification("❌ API verification timed out.", 'error');
        }, 15000);
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://api.torn.com/user/?selections=log&limit=1&key=${key}`,
                headers: {
                    "Accept": "application/json",
                    "User-Agent": TPM_USER_AGENT
                },
                anonymous: true,
                timeout: 15000,
                onload: (response) => {
                    clearTimeout(timeoutId);
                    updateRateLimitCounters();
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.error && data.error.code === 5) {
                            console.error("TPM: Rate limit hit during key verification");
                            if (RATE_LIMIT.retryCount < CONFIG.MAX_RETRY_ATTEMPTS) {
                                RATE_LIMIT.retryCount++;
                                showNotification(`⚠️ Rate limit hit, retrying (${RATE_LIMIT.retryCount}/${CONFIG.MAX_RETRY_ATTEMPTS})...`, 'warning');
                                if (saveBtn) {
                                    buttonTimeout = setTimeout(() => {
                                        saveBtn.disabled = false;
                                        saveBtn.style.opacity = '1';
                                        saveBtn.style.cursor = 'pointer';
                                    }, 10000);
                                }
                                retryTimeout = setTimeout(() => {
                                    window.removeEventListener('beforeunload', cleanup);
                                    verifyApiKey(key).then(resolve);
                                }, 10000 * RATE_LIMIT.retryCount);
                            } else {
                                showNotification('⚠️ Rate limit exceeded maximum retries. Please try again later.', 'error');
                                cleanup();
                                window.removeEventListener('beforeunload', cleanup);
                                resolve(false);
                            }
                            return;
                        }
                        RATE_LIMIT.retryCount = 0;
                        cleanup();
                        window.removeEventListener('beforeunload', cleanup);
                        if (data.error) {
                            const code = data.error.code;
                            if (code === 2) showNotification("❌ API key invalid or revoked.", 'error');
                            else if (code === 7 || code === 6) showNotification("❌ This script requires a FULL ACCESS API key to read user logs.", 'error');
                            else showNotification(`❌ Torn API error: ${data.error.error}`, 'error');
                            return resolve(false);
                        }
                        if (data.log !== undefined) {
                            showNotification("✅ API key verified successfully!", 'success');
                            return resolve(true);
                        }
                        showNotification("❌ Unexpected API response.", 'error');
                        resolve(false);
                    } catch (e) {
                        console.error("TPM: JSON parse error:", e);
                        cleanup();
                        window.removeEventListener('beforeunload', cleanup);
                        resolve(false);
                    }
                },
                onerror: () => {
                    clearTimeout(timeoutId);
                    showNotification("❌ Failed to reach Torn API.", 'error');
                    cleanup();
                    window.removeEventListener('beforeunload', cleanup);
                    resolve(false);
                },
                ontimeout: () => {
                    clearTimeout(timeoutId);
                    showNotification("❌ API verification timed out.", 'error');
                    cleanup();
                    window.removeEventListener('beforeunload', cleanup);
                    resolve(false);
                }
            });
        });
    }
    function processLeaseLogs(rawLogs) {
        if (!rawLogs || !Array.isArray(rawLogs) || rawLogs.length === 0) return;
        const processedLogs = safeJSON("tpm_processed_logs") || {};
        let newProcessed = { ...processedLogs };
        let newEvents = 0;
        rawLogs.forEach(log => {
            if (!log || !log.data) return;
            const logId = log.id || `${log.timestamp}-${log.log}-${log.data.property_id}-${log.data.user_id}`;
            if (processedLogs[logId]) return;
            const propertyID = String(log.data.property_id || log.data.property || "unknown");
            const tenantID = String(log.data.user_id || log.data.user || log.data.renter || "unknown");
            const tenantName = log.data.user_name || "Unknown";
            if (propertyID === "unknown" || tenantID === "unknown") return;
            const totalPayment = typeof log.data.rent === 'number' ? log.data.rent : 0;
            const days = typeof log.data.days === 'number' ? log.data.days : 0;
            const dailyRent = (totalPayment > 0 && days > 0) ? Math.round(totalPayment / days) : 0;
            if (tenantID.length > 5) {
                storeTenantInfo(tenantID, tenantName);
            }
            const eventCode = EVENT_MAP[log.log] || 0;
            const eventTime = log.timestamp * 1000;
            addEventToStore(propertyID, tenantID, eventCode, dailyRent, days, totalPayment, eventTime);
            newProcessed[logId] = true;
            newEvents++;
            if ((eventCode === EVENT_CODES.PROPERTY_RENTED || eventCode === EVENT_CODES.EXTENSION_ACCEPTED) && totalPayment > 0 && days > 0 && tenantID.length > 5) {
                updateTenantStats(tenantID, totalPayment, days, true);
            }
        });
        if (newEvents > 0) {
            localStorage.setItem('tpm_processed_logs', JSON.stringify(newProcessed));
        }
        rotateLogsIfNeeded();
        cleanupTenantDB();
    }
    function logLeaseEvent(propertyID, tenantID, tenantName, event, days, rent) {
        const eventCode = EVENT_CODES[event.toUpperCase()] || 0;
        addEventToStore(propertyID, tenantID, eventCode, rent, days, Date.now());
        storeTenantInfo(tenantID, tenantName);
        if (eventCode === EVENT_CODES.PROPERTY_RENTED || eventCode === EVENT_CODES.EXTENSION_ACCEPTED) {
            updateTenantStats(tenantID, rent, days, true);
        }
        if (isDebugMode()) console.log(`TPM: Logged lease event: ${event} for property ${propertyID}`);
    }
    function cleanInvalidLogs(currentProperties) {
        const store = getStore();
        if (!store.events) return;
        const cache = safeJSON("prop_cache");
        const cacheIDs = cache.map(([id, p]) => String(p.id));
        const liveIDs = currentProperties.map(p => String(p.id));
        const validIDs = new Set([...liveIDs, ...cacheIDs]);
        let removedCount = 0;
        Object.keys(store.events).forEach(id => {
            if (!validIDs.has(String(id))) {
                if (store.events[id].length) {
                    updateArchiveFromEvents(store.events[id], 'Unknown');
                }
                delete store.events[id];
                removedCount++;
                tpmArchive.totalPropertiesArchived++;
            }
        });
        if (removedCount > 0) {
            saveStore(store);
            localStorage.setItem('tpm_archive', JSON.stringify(tpmArchive));
            if (isDebugMode()) console.log(`TPM: Archived logs for ${removedCount} sold properties`);
        }
    }
    function getLastUpdateTimeUTC() {
        const lastCall = localStorage.getItem('prop_last_call');
        if (!lastCall) return "Never";
        const date = new Date(parseInt(lastCall));
        return date.toLocaleString('en-US', {
            month: 'short',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit',
            timeZone: 'UTC'
        }) + ' UTC';
    }
    function FormatCurrency(value) {
        if (value === 0) return "$0";
        const abs = Math.abs(value);
        if (abs < 1_000_000) return "$" + value.toLocaleString();
        const units = ["M", "B", "T", "Qa", "Qi"];
        let num = value / 1_000_000;
        let unitIndex = 0;
        while (Math.abs(num) >= 1000 && unitIndex < units.length - 1) {
            num /= 1000;
            unitIndex++;
        }
        return "$" + num.toFixed(1).replace(/\.0$/, "") + units[unitIndex];
    }
    function getCurrentPropertyID() {
        const match = location.hash.match(/[Ii][Dd]=(\d+)/);
        return match ? match[1] : null;
    }
    function getPropertyAutofill(propertyId) {
        const stored = localStorage.getItem('tpm_property_autofill');
        if (!stored) return null;
        try {
            const parsed = JSON.parse(stored);
            return parsed[propertyId] || null;
        } catch (e) {
            console.warn("TPM: Autofill data corrupted");
            return null;
        }
    }
    function getRecentDecline(propertyID) {
        const store = getStore();
        if (!store.events?.[propertyID]) return null;
        const declines = store.events[propertyID].filter(e => e[1] === EVENT_CODES.OFFER_DECLINED).sort((a,b) => b[0] - a[0]);
        if (!declines.length) return null;
        const hours = (Date.now() - declines[0][0]) / 3600000;
        return hours < 24 ? "recent" : "old";
    }
    function getTenantStats(tenantID) {
        const store = getStore();
        let totalIncome = 0, totalLeases = 0, totalDays = 0;
        if (store.events) {
            Object.values(store.events).forEach(events => {
                events.forEach(e => {
                    if (e[5] === tenantID) {
                        totalIncome += e[4] || 0;
                        totalDays += e[3] || 0;
                        totalLeases++;
                    }
                });
            });
        }
        return {
            income: totalIncome,
            leases: totalLeases,
            days: totalDays,
            avgLease: totalLeases ? Math.round(totalDays / totalLeases) : 0
        };
    }
    function calculateTenantReliability(tenantID) {
        const store = getStore();
        return store.tenants?.[tenantID]?.s || 50;
    }
    function getRowColor(p, customs) {
        if (p.status === "none") return STYLES.statusColors.expired;
        if (p.extension) return STYLES.statusColors.offered;
        if (p.offerask) return STYLES.statusColors.offerask;
        if (p.status === "for_sale") return STYLES.statusColors.forsale;
        if (p.status === "for_rent") return STYLES.statusColors.forrent;
        if (p.status === "rented") {
            if (p.daysLeft <= 0) return STYLES.statusColors.expired;
            if (p.daysLeft <= customs.warn_lease_ex) return STYLES.statusColors.warning;
            if (p.daysLeft <= customs.warn_lease_wa) return STYLES.statusColors.gettingclose;
            return STYLES.statusColors.allgood;
        }
        return STYLES.statusColors.allgood;
    }
    function buildStatusDisplay(p, customs) {
        let display = '';
        if (p.status === "for_rent") display += '🔔';
        else if (p.status === "for_sale") display += '📢';
        else if (p.status === "in_use") display += '🕌';
        if (p.daysLeft <= 0 && p.status !== "in_use" && !p.offerask) display += '🔴';
        else if (p.status === "rented") {
            if (p.daysLeft <= customs.warn_lease_ex && p.daysLeft > 0) display += '⚠️';
            else if (p.daysLeft <= customs.warn_lease_wa && p.daysLeft > customs.warn_lease_ex) display += '🟡';
            else if (p.daysLeft > customs.warn_lease_wa) display += '🟢';
        }
        if (p.offerask) {
            display += '📝🆕';
            p.renterid = p.offerask.id;
            p.rentername = p.offerask.name;
        }
        if (p.extension && p.extension.period) {
            display += `📝➕${p.extension.period}`;
            const declineStatus = getRecentDecline(p.id);
            if (declineStatus === "recent") display += "⏱️❌";
            else if (declineStatus === "old") display += "❌";
        }
        if (!display) display = STATUS_DISPLAY[p.status] || "☢️ Error";
        return display;
    }
    async function loadProperties() {
        const apiKey = localStorage.getItem('tpmApiKey');
        if (!apiKey) {
            renderSettings("⚠️ Please set your API key.");
            showNotification("⚠️ Please set your API key in Settings.", 'warning');
            const container = document.getElementById('settings-content');
            if (container) {
                container.style.display = 'block';
                container.scrollIntoView({ behavior: 'smooth', block: 'center' });
            }
            return;
        }
        const loaderId = showInlineLoader(document.getElementById('torn-property-manager-root'), 'Loading properties...');
        try {
            const raw = await getAllProperties(apiKey);
            if (!raw || !Array.isArray(raw)) return;
            const customs = getCustoms();
            let properties = raw.map(([id, p]) => {
                const oldData = safeJSON("tpm_property_autofill")[p.id];
                if (oldData && oldData.rent !== p.cost_per_day && p.cost_per_day > 0) {
                    trackRentChange(p.id, oldData.rent, p.cost_per_day, p.rented_by?.id, p.rented_by?.name);
                }
                let buttonValue = "Lease", renew = `https://www.torn.com/properties.php#/p=options&ID=${p.id}&tab=lease`;
                if (p.status === "rented") {
                    buttonValue = "Renew";
                    renew = `https://www.torn.com/properties.php#/p=options&ID=${p.id}&tab=offerExtension`;
                } else if (p.status === "in_use") {
                    buttonValue = "Owner";
                    renew = "#";
                }
                return {
                    id: p.id,
                    name: p.property.name,
                    status: p.status,
                    happy: p.status === "in_use" ? "—" : p.happy,
                    upkeep: p.status === "in_use" ? "—" : FormatCurrency(p.upkeep.property + (p.upkeep.staff || 0)),
                    staffupkeep: p.upkeep.staff || 0,
                    ownerid: p.owner.id,
                    rentername: p.status === "rented" ? p.rented_by.name : (p.status === "in_use" ? "👤 Owner" : "Empty"),
                    renterid: p.status === "rented" ? p.rented_by.id : (p.status === "in_use" ? p.owner.id : p.owner.id),
                    daysLeft: p.status === "in_use" ? "—" : (p.rental_period_remaining || 0),
                    lease: p.status === "in_use" ? "—" : (p.rental_period || 0),
                    rent: p.status === "in_use" ? "—" : (p.cost_per_day || 0),
                    extension: p.lease_extension,
                    offerask: p.renter_asked,
                    renew: renew,
                    buttonValue: buttonValue,
                    market_price: p.market_price || 1000000
                };
            });
            const existing = JSON.parse(localStorage.getItem('tpm_property_autofill') || "{}");
            const updatedAutofill = Object.fromEntries(properties.map(p => {
                const prev = existing[p.id] || {};
                return [p.id, {
                    rent: p.rent > 0 && p.rent !== "—" ? p.rent : (prev.rent || 0),
                    lease: p.lease > 0 && p.lease !== "—" ? p.lease : (prev.lease || 0),
                    type: p.name,
                    status: p.status,
                    market_price: p.market_price || prev.market_price || 1000000
                }];
            }));
            localStorage.setItem('tpm_property_autofill', JSON.stringify(updatedAutofill));
            cleanInvalidLogs(properties);
            properties.sort((a, b) => (a.daysLeft === "—" ? 999 : a.daysLeft) - (b.daysLeft === "—" ? 999 : b.daysLeft));
            renderAlerts(properties, customs);
            renderTable(properties, customs);
            renderSettings();
        } finally {
            hideInlineLoader(loaderId);
        }
    }
    function refreshTablesFromCache() {
        const propertiesContainer = document.getElementById('prop-content');
        if (propertiesContainer && propertiesContainer.style.display === 'block') {
            loadProperties();
        }
        const intelContainer = document.getElementById('tenantintel-content');
        if (intelContainer && intelContainer.style.display === 'block') {
            renderTenantIntel();
        }
    }
    function renderAlerts(properties, customs) {
        let expired = 0, warning = 0, close = 0, isOffered = 0, isOwned = 0;
        properties.forEach(p => {
            isOwned++;
            if (p.status === "in_use") return;
            if (["rented", "none", "for_rent", "for_sale"].includes(p.status)) {
                if (p.daysLeft <= 0) expired++;
                else if (p.daysLeft <= customs.warn_lease_ex) warning++;
                else if (p.daysLeft <= customs.warn_lease_wa) close++;
            }
            if (p.extension || p.offerask) isOffered++;
        });
        let alertDiv = document.getElementById('alert-summary');
        if (!alertDiv) {
            const container = document.querySelector('#properties-page-wrap div');
            if (!container) return;
            alertDiv = document.createElement('div');
            alertDiv.id = 'alert-summary';
            container.insertBefore(alertDiv, container.children[1]);
        }
        alertDiv.innerHTML = `
            <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
                <span style="font-size:12px; color:var(--tpm-info);">🏘️ Property Manager <span style="color:var(--tpm-text-soft); font-size:11px;">v3.0.0</span></span>
                <span style="font-size:11px; color:var(--tpm-text-soft);">Updated: ${getLastUpdateTimeUTC()}</span>
            </div>
            <div class="tpm-stats-bar">
                <div class="tpm-stat"><span class="tpm-stat-label">🏘️ Owned</span><span class="tpm-stat-value">${isOwned}</span></div>
                <div class="tpm-stat danger"><span class="tpm-stat-label">🔴 Expired/Empty</span><span class="tpm-stat-value">${expired}</span></div>
                <div class="tpm-stat alert"><span class="tpm-stat-label">⚠️ Attn ≤${customs.warn_lease_ex}d</span><span class="tpm-stat-value">${warning}</span></div>
                <div class="tpm-stat warn"><span class="tpm-stat-label">🟡 Warn ≤${customs.warn_lease_wa}d</span><span class="tpm-stat-value">${close}</span></div>
                <div class="tpm-stat"><span class="tpm-stat-label">📝 Offers</span><span class="tpm-stat-value">${isOffered}</span></div>
            </div>
        `;
    }
    let currentPage = 1;
    let searchTerm = "";
    function renderTable(properties, customs) {
        let container = document.getElementById('prop-content');
        if (container) {
            const closeBtn = container.querySelector('.tpm-panel-close');
            container.innerHTML = '';
            if (closeBtn) container.appendChild(closeBtn);
        }
        const filtered = properties.filter(p => p.name.toLowerCase().includes(searchTerm.toLowerCase()) || p.rentername.toLowerCase().includes(searchTerm.toLowerCase()));
        const itemsPerPage = customs.items_per_page;
        const totalPages = Math.ceil(filtered.length / itemsPerPage) || 1;
        if (currentPage > totalPages) currentPage = 1;
        const start = (currentPage - 1) * itemsPerPage;
        const paginatedItems = filtered.slice(start, start + itemsPerPage);
        container = document.getElementById('prop-content');
        if (!container) return;
        const getNavHtml = (idx) => `
            <div class="tpm-nav">
                <div class="tpm-flex tpm-gap-2">
                    <button class="tpm-nav-item btn-prev" ${currentPage === 1 ? 'disabled' : ''}>◀</button>
                    <span style="font-size:13px; color:var(--tpm-text-strong); min-width:70px; text-align:center;">${currentPage}/${totalPages}</span>
                    <button class="tpm-nav-item btn-next" ${currentPage === totalPages ? 'disabled' : ''}>▶</button>
                </div>
                <div class="tpm-nav-divider"></div>
                <input type="text" class="prop-search-bar" data-idx="${idx}" placeholder="🔍 Search..." style="background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:5px 8px; border-radius:4px; font-size:12px; width:140px;" value="${searchTerm}">
            </div>
        `;
        container.insertAdjacentHTML('beforeend', getNavHtml(0));
        container.insertAdjacentHTML('beforeend', `
            <table class="tpm-table">
                <thead>
                    <tr>
                        <th style="padding:8px;">🏘️ Property</th>
                        <th style="padding:8px; text-align:right;">😃 Happy</th>
                        <th style="padding:8px; text-align:right;">💵 Upkeep</th>
                        <th style="padding:8px;">📊 Status</th>
                        <th style="padding:8px;">👤 Occupant</th>
                        <th style="padding:8px; text-align:right;">✒️ Lease</th>
                        <th style="padding:8px; text-align:right;">💰 Rent</th>
                        <th style="padding:8px; text-align:center;">🎬 Action</th>
                    </tr>
                </thead>
                <tbody id="prop-body"></tbody>
            </table>
        `);
        const body = document.getElementById('prop-body');
        if (!body) return;
        body.innerHTML = '';
        paginatedItems.forEach((p, index) => {
            const row = document.createElement('tr');
            row.style.backgroundColor = p.status === "in_use" ? "var(--tpm-card-bg)" : getRowColor(p, customs);
            const reliabilityScore = p.status === "rented" ? calculateTenantReliability(p.renterid) : 0;
            row.onmouseenter = () => { if (p.status !== "in_use") row.style.backgroundColor = STYLES.statusColors.hover; };
            row.onmouseleave = () => { row.style.backgroundColor = p.status === "in_use" ? "var(--tpm-card-bg)" : getRowColor(p, customs); };
            const displayStatus = buildStatusDisplay(p, customs);
            const showIcon = p.renterid && p.renterid.toString().length > 5 && p.status === 'rented';
            const hasNotes = showIcon && getTenantNotes(p.renterid).length > 0;
            const noteIcon = showIcon ? `<span style="cursor:pointer; font-size:12px; color:var(--tpm-text-soft); margin-right:4px;" class="tpm-note-icon" data-tenant="${p.renterid}" data-name="${p.rentername}" data-active="${p.status === 'rented'}">${hasNotes ? '📖' : '📕'}</span>` : '';
            row.innerHTML = `
                <td style="padding:8px;">
                    <a href="https://www.torn.com/properties.php#/p=options&ID=${p.id}&tab=customize" target="${getLinkTarget()}" style="color:var(--tpm-link); text-decoration:none; font-size:13px; font-weight:600;">${p.name}</a>
                    <div style="font-size:10px; color:var(--tpm-text-soft);">ID: ${p.id}</div>
                </td>
                <td style="padding:8px; text-align:right; font-size:13px; color:var(--tpm-success);">${p.happy}</td>
                <td style="padding:8px; text-align:right; font-size:13px; color:var(--tpm-success);">${p.upkeep}</td>
                <td style="padding:8px; font-size:13px; color:var(--tpm-text);">${displayStatus}</td>
                <td style="padding:8px; font-size:13px;">
                    ${noteIcon}<a href="https://www.torn.com/profiles.php?XID=${p.renterid}" target="${getLinkTarget()}" style="color:var(--tpm-link); text-decoration:none;">${p.rentername}</a>
                </td>
                <td style="padding:8px; text-align:right;">
                    <span style="font-size:13px; color:var(--tpm-warning);">${p.daysLeft}</span>
                    <div style="font-size:10px; color:var(--tpm-text-soft);">of ${p.lease}</div>
                </td>
                <td style="padding:8px; text-align:right; font-size:13px; color:var(--tpm-success);">${typeof p.rent === 'number' ? FormatCurrency(p.rent) : p.rent}</td>
                <td style="padding:8px; text-align:center;">
                    ${p.renew === "#" ? `<span style="color:var(--tpm-text-soft); font-size:11px;">${p.buttonValue}</span>` : `<a href="${p.renew}" target="${getLinkTarget()}" class="tpm-button tpm-button-sm">${p.buttonValue}</a>`}
                </td>
            `;
            body.appendChild(row);
        });
        if (filtered.length > customs.propnavbar && customs.items_per_page >= customs.propnavbar) container.insertAdjacentHTML('beforeend', getNavHtml(1));
        container.querySelectorAll('.prop-search-bar').forEach(input => {
            input.oninput = (e) => {
                const targetIdx = e.target.getAttribute('data-idx');
                const cursor = e.target.selectionStart;
                searchTerm = e.target.value;
                currentPage = 1;
                renderTable(properties, customs);
                let nextInput = document.querySelector(`.prop-search-bar[data-idx="${targetIdx}"]`);
                if (!nextInput) nextInput = document.querySelector('.prop-search-bar[data-idx="0"]');
                if (nextInput) {
                    nextInput.focus();
                    nextInput.setSelectionRange(cursor, cursor);
                }
            };
        });
        container.querySelectorAll('.btn-prev').forEach(btn => { btn.onclick = () => { if (currentPage > 1) { currentPage--; renderTable(properties, customs); } }; });
        container.querySelectorAll('.btn-next').forEach(btn => { btn.onclick = () => { if (currentPage < totalPages) { currentPage++; renderTable(properties, customs); } }; });
        container.querySelectorAll('.tpm-note-icon').forEach(icon => {
            icon.onclick = (e) => {
                e.stopPropagation();
                const tenant = icon.dataset.tenant;
                const name = icon.dataset.name;
                const active = icon.dataset.active === 'true';
                showNotesBook(tenant, name, active);
            };
        });
    }
    function handleCollapsibleClick(e) {
        const header = e.target.closest('.tpm-collapsible-header');
        if (!header) return;
        e.preventDefault();
        e.stopPropagation();
        const content = header.nextElementSibling;
        if (!content) return;
        const isOpen = content.style.display === 'block';
        content.style.display = isOpen ? 'none' : 'block';
    }
    function initCollapsibleListeners() {
        const container = document.getElementById('torn-property-manager-root');
        if (!container) return;
        container.addEventListener('click', handleCollapsibleClick);
    }
    function renderTenantIntel() {
        const container = document.getElementById("tenantintel-content");
        if (!container) return;
        const loaderId = showInlineLoader(container, 'Loading tenant intelligence...');
        container.innerHTML = "";
        try {
            const store = getStore();
            const properties = JSON.parse(localStorage.getItem('prop_cache') || "[]");
            const autofill = JSON.parse(localStorage.getItem('tpm_property_autofill') || "{}");
            const tenantNotes = safeJSON("tpm_tenant_notes");
            const rentHistory = safeJSON("tpm_rent_history");
            const customs = getCustoms();
            const historyLimit = customs.historyLimit || 10;
            if (!properties.length) {
                container.innerHTML = `<div style="padding:20px; text-align:center; color:var(--tpm-text-soft); font-size:13px;">No property data available. Please refresh.</div>`;
                return;
            }
            if (isDebugMode()) {
                console.log("TPM: Store events:", Object.keys(store.events || {}).length);
                console.log("TPM: Properties:", properties.length);
                console.log("TPM: Autofill data:", Object.keys(autofill).length);
            }
            const propertyDetails = {};
            const marketData = {};
            let totalRented = 0, totalRentable = 0, totalDailyIncome = 0, totalLifetimeIncome = tpmArchive.totalLifetimeIncome || 0, totalHappy = 0, totalLeaseDays = 0, totalProperties = 0, totalVacantLoss = 0, totalMarketValue = 0;
            const incomeEvents = new Set([EVENT_CODES.PROPERTY_RENTED, EVENT_CODES.EXTENSION_ACCEPTED]);
            const thisYear = new Date().getFullYear();
            const lastYear = thisYear - 1;
            let thisYearIncome = tpmArchive.yearlyIncome[thisYear] || 0, lastYearIncome = tpmArchive.yearlyIncome[lastYear] || 0;
            properties.forEach(([id, p]) => {
                if (p.status === "in_use") return;
                if (!marketData[p.property?.name]) {
                    marketData[p.property?.name] = { rents: [], count: 0, rentedCount: 0, totalIncome: 0, totalMarketValue: 0 };
                }
                if (p.status === "rented" && p.cost_per_day > 0) {
                    marketData[p.property?.name].rents.push(p.cost_per_day);
                    marketData[p.property?.name].rentedCount++;
                }
                marketData[p.property?.name].count++;
            });
            properties.forEach(([id, p]) => {
                if (p.status === "in_use") return;
                const propID = String(p.id);
                totalProperties++;
                const marketPrice = autofill[propID]?.market_price || (p.market_price || 1000000);
                totalMarketValue += marketPrice;
                const currentTenant = p.status === "rented" ? {
                    id: p.rented_by?.id,
                    name: p.rented_by?.name,
                    daysLeft: p.rental_period_remaining || 0,
                    leaseLength: p.rental_period || 0,
                    rent: p.cost_per_day || 0,
                    propertyName: p.property?.name
                } : null;
                if (p.status === "rented" && p.cost_per_day > 0) {
                    totalDailyIncome += p.cost_per_day;
                }
                const isRentable = true;
                if (isRentable) {
                    totalRentable++;
                    if (p.status === "rented") {
                        totalRented++;
                        totalHappy += p.happy || 0;
                        totalLeaseDays += p.rental_period || 0;
                    }
                }
                const propertyEvents = store.events?.[propID] || [];
                let totalIncome = 0, totalLeaseCount = 0;
                propertyEvents.forEach(e => {
                    const isIncomeEvent = incomeEvents.has(e[1]);
                    const eventIncome = isIncomeEvent ? (e[4] || 0) : 0;
                    if (isIncomeEvent) {
                        totalIncome += eventIncome;
                        totalLifetimeIncome += eventIncome;
                        totalLeaseCount++;
                        const eventYear = new Date(e[0]).getFullYear();
                        if (eventYear === thisYear) thisYearIncome += eventIncome;
                        if (eventYear === lastYear) lastYearIncome += eventIncome;
                    }
                });
                if (p.status === "none" || p.status === "for_rent") {
                    let estimatedRent = 0;
                    if (p.cost_per_day > 0) {
                        estimatedRent = p.cost_per_day;
                    } else if (autofill[propID]?.rent > 0) {
                        estimatedRent = autofill[propID].rent;
                    } else if (marketData[p.property?.name]?.rents.length > 0) {
                        const typeRents = marketData[p.property?.name].rents;
                        estimatedRent = Math.round(typeRents.reduce((a, b) => a + b, 0) / typeRents.length);
                    } else {
                        estimatedRent = 1000;
                    }
                    totalVacantLoss += estimatedRent * 30;
                }
                if (totalIncome === 0 && p.status === "rented" && autofill[propID]) {
                    const estRent = autofill[propID].rent || 0;
                    const estLease = autofill[propID].lease || 0;
                    if (estRent > 0 && estLease > 0) {
                        totalIncome = estRent * estLease;
                        totalLifetimeIncome += totalIncome;
                        totalLeaseCount = 1;
                    }
                }
                propertyDetails[propID] = {
                    id: propID,
                    name: p.property?.name || "Unknown",
                    type: p.property?.name || "Unknown",
                    status: p.status,
                    happy: p.happy || 0,
                    upkeep: (p.upkeep?.property || 0) + (p.upkeep?.staff || 0),
                    currentTenant: currentTenant,
                    rent: p.cost_per_day || 0,
                    leaseLength: p.rental_period || 0,
                    daysLeft: p.rental_period_remaining || 0,
                    extension: p.lease_extension,
                    offerask: p.renter_asked,
                    lastRent: autofill[propID]?.rent || 0,
                    lastLease: autofill[propID]?.lease || 0,
                    marketPrice: marketPrice,
                    lifetimeIncome: totalIncome,
                    totalLeases: totalLeaseCount,
                    hasLogs: propertyEvents.length > 0,
                    hasAutofill: !!autofill[propID],
                    rentHistory: rentHistory[propID] || []
                };
                marketData[p.property?.name].totalIncome += totalIncome;
                marketData[p.property?.name].totalMarketValue += marketPrice;
            });
            const occupancyRate = totalRentable > 0 ? Math.round((totalRented / totalRentable) * 100) : 0;
            const avgRent = totalRented ? Math.round(totalDailyIncome / totalRented) : 0;
            const avgHappy = totalRented ? Math.round(totalHappy / totalRented) : 0;
            const avgLease = totalRented ? Math.round(totalLeaseDays / totalRented) : 0;
            const rentPerHappy = avgHappy ? Math.round(avgRent / avgHappy) : 0;
            const portfolioROI = totalMarketValue > 0 ? Math.round((totalLifetimeIncome / totalMarketValue) * 100) : 0;
            const growthPercent = lastYearIncome === 0 ? 100 : Math.round(((thisYearIncome - lastYearIncome) / lastYearIncome) * 100);
            const tenantEntries = [];
            Object.values(propertyDetails).forEach(p => {
                if (p.currentTenant && p.status === "rented") {
                    tenantEntries.push({
                        tenantId: String(p.currentTenant.id),
                        tenantName: p.currentTenant.name,
                        propertyName: p.name,
                        propertyType: p.type,
                        leaseLength: p.currentTenant.leaseLength,
                        rent: p.currentTenant.rent,
                        lifetimeIncome: p.lifetimeIncome,
                        isActive: true
                    });
                }
            });
            const topLongest = [...tenantEntries].sort((a,b) => b.leaseLength - a.leaseLength).slice(0,6);
            const topValue = [...tenantEntries].sort((a,b) => b.lifetimeIncome - a.lifetimeIncome).slice(0,6);
            const topPerformersId = `top-performers-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
            const topPerformersHtml = `
                <div class="tpm-card tpm-collapsible" data-section-id="${topPerformersId}" style="margin-bottom:12px;">
                    <div class="tpm-card-header tpm-flex-between tpm-collapsible-header" data-target="${topPerformersId}">
                        <span><span style="color:var(--tpm-info);">🏆 Top Performers</span></span>
                        <span style="color:var(--tpm-text-soft); font-size:14px;" class="tpm-collapsible-arrow">▶</span>
                    </div>
                    <div id="${topPerformersId}" class="tpm-card-body tpm-collapsible-content" style="display:none;">
                        <div style="margin-bottom:20px;">
                            <div style="font-size:14px; color:var(--tpm-warning); margin-bottom:10px; font-weight:600;">🏆 Longest Current Leases</div>
                            <div class="tpm-grid-3" style="gap:10px;">
                                ${topLongest.map((t, i) => {
                                    const score = calculateTenantReliability(t.tenantId);
                                    const scoreColor = score >= 80 ? 'var(--tpm-success)' : score >= 60 ? 'var(--tpm-warning)' : score >= 40 ? 'var(--tpm-info)' : 'var(--tpm-danger)';
                                    const hasNotes = getTenantNotes(t.tenantId).length > 0;
                                    const activeStatus = `<span class="tpm-active-indicator">● Active</span>`;
                                    return `
                                        <div class="tpm-performer-card" style="border-left-color:${scoreColor};">
                                            <div class="tpm-performer-rank">#${i+1}</div>
                                            <div class="tpm-performer-name">
                                                <span class="tpm-note-icon" data-tenant="${t.tenantId}" data-name="${t.tenantName}" data-active="true">${hasNotes ? '📖' : '📕'}</span>
                                                <a href="https://www.torn.com/profiles.php?XID=${t.tenantId}" target="${getLinkTarget()}" style="color:var(--tpm-link);">${t.tenantName}</a>
                                                ${activeStatus}
                                            </div>
                                            <div style="font-size:10px; color:var(--tpm-text-soft); margin:2px 0;">${t.propertyName}</div>
                                            <div class="tpm-performer-stats">
                                                <span class="tpm-performer-days">${t.leaseLength} days</span>
                                                <span class="tpm-performer-value">${FormatCurrency(t.rent)}/d</span>
                                            </div>
                                        </div>
                                    `;
                                }).join('')}
                            </div>
                        </div>
                        <div>
                            <div style="font-size:14px; color:var(--tpm-success); margin-bottom:10px; font-weight:600;">💰 Top Lifetime Value (Current Tenants)</div>
                            <div class="tpm-grid-3" style="gap:10px;">
                                ${topValue.map((t, i) => {
                                    const score = calculateTenantReliability(t.tenantId);
                                    const scoreColor = score >= 80 ? 'var(--tpm-success)' : score >= 60 ? 'var(--tpm-warning)' : score >= 40 ? 'var(--tpm-info)' : 'var(--tpm-danger)';
                                    const hasNotes = getTenantNotes(t.tenantId).length > 0;
                                    const activeStatus = `<span class="tpm-active-indicator">● Active</span>`;
                                    return `
                                        <div class="tpm-performer-card" style="border-left-color:${scoreColor};">
                                            <div class="tpm-performer-rank">#${i+1}</div>
                                            <div class="tpm-performer-name">
                                                <span class="tpm-note-icon" data-tenant="${t.tenantId}" data-name="${t.tenantName}" data-active="true">${hasNotes ? '📖' : '📕'}</span>
                                                <a href="https://www.torn.com/profiles.php?XID=${t.tenantId}" target="${getLinkTarget()}" style="color:var(--tpm-link);">${t.tenantName}</a>
                                                ${activeStatus}
                                            </div>
                                            <div style="font-size:10px; color:var(--tpm-text-soft); margin:2px 0;">${t.propertyName}</div>
                                            <div class="tpm-performer-stats">
                                                <span class="tpm-performer-value">${FormatCurrency(t.lifetimeIncome)}</span>
                                                <span style="color:var(--tpm-text-soft);">${FormatCurrency(t.rent)}/d</span>
                                            </div>
                                        </div>
                                    `;
                                }).join('')}
                            </div>
                        </div>
                    </div>
                </div>
            `;
            const financialHtml = `
                <div class="tpm-mb-3" style="background:var(--tpm-card-header-bg); border-radius:8px; padding:12px;">
                    <div style="display:grid; grid-template-columns:repeat(3,1fr); gap:15px; margin-bottom:10px; text-align:center;">
                        <div><div style="font-size:11px; color:var(--tpm-success);">Daily Income</div><div style="font-size:16px; font-weight:600; color:var(--tpm-success);">${FormatCurrency(totalDailyIncome)}/day</div></div>
                        <div><div style="font-size:11px; color:var(--tpm-info);">Lifetime Income</div><div style="font-size:16px; font-weight:600; color:var(--tpm-info);">${FormatCurrency(totalLifetimeIncome)}</div></div>
                        <div><div style="font-size:11px; color:var(--tpm-warning);">Portfolio ROI</div><div style="font-size:16px; font-weight:600; color:var(--tpm-warning);">${portfolioROI}%</div></div>
                    </div>
                    <div style="display:grid; grid-template-columns:repeat(3,1fr); gap:15px; text-align:center;">
                        <div><div style="font-size:11px; color:var(--tpm-info);">Avg Happy Rented</div><div style="font-size:16px; font-weight:600; color:var(--tpm-info);">${avgHappy}</div></div>
                        <div><div style="font-size:11px; color:var(--tpm-success);">Rent/Happy Rented</div><div style="font-size:16px; font-weight:600; color:var(--tpm-success);">${FormatCurrency(rentPerHappy)}</div></div>
                        <div><div style="font-size:11px; color:var(--tpm-info);">Avg Rent</div><div style="font-size:16px; font-weight:600; color:var(--tpm-info);">${FormatCurrency(avgRent)}/day</div></div>
                    </div>
                    <div style="display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-top:15px; padding-top:15px; border-top:1px solid var(--tpm-border); text-align:center;">
                        <div><div style="font-size:11px; color:var(--tpm-success);">Occupancy</div><div style="font-size:16px; font-weight:600; color:var(--tpm-success);">${occupancyRate}%</div></div>
                        <div><div style="font-size:11px; color:var(--tpm-danger);">Vacancy Loss</div><div style="font-size:16px; font-weight:600; color:var(--tpm-danger);">${FormatCurrency(totalVacantLoss)}/mo</div></div>
                        <div><div style="font-size:11px; color:var(--tpm-warning);">30d Projection</div><div style="font-size:16px; font-weight:600; color:var(--tpm-warning);">${FormatCurrency(totalDailyIncome * 30)}</div></div>
                        <div><div style="font-size:11px; color:var(--tpm-info);">90d Projection</div><div style="font-size:16px; font-weight:600; color:var(--tpm-info);">${FormatCurrency(totalDailyIncome * 90)}</div></div>
                    </div>
                    <div style="margin-top:10px; padding-top:10px; border-top:1px solid var(--tpm-border); display:flex; justify-content:space-around; text-align:center;">
                        <div><span style="color:var(--tpm-text-soft); font-size:11px;">📈 ${thisYear} Income</span><br><span style="color:var(--tpm-success); font-size:14px;">${FormatCurrency(thisYearIncome)}</span></div>
                        <div><span style="color:var(--tpm-text-soft); font-size:11px;">📉 ${lastYear} Income</span><br><span style="color:var(--tpm-warning); font-size:14px;">${FormatCurrency(lastYearIncome)}</span></div>
                        <div><span style="color:var(--tpm-text-soft); font-size:11px;">Growth</span><br><span style="color:${growthPercent >= 0 ? 'var(--tpm-success)' : 'var(--tpm-danger)'}; font-size:14px;">${growthPercent >= 0 ? '+' : ''}${growthPercent}%</span></div>
                    </div>
                </div>
            `;
            const propertiesByType = {};
            Object.values(propertyDetails).forEach(p => {
                if (!propertiesByType[p.type]) propertiesByType[p.type] = [];
                propertiesByType[p.type].push(p);
            });
            const propertiesHtml = Object.entries(propertiesByType).map(([type, props]) => {
                const totalTypeIncome = props.reduce((sum, p) => sum + p.lifetimeIncome, 0);
                const totalTypeMarketValue = props.reduce((sum, p) => sum + (p.marketPrice || 1000000), 0);
                const typeROI = totalTypeMarketValue > 0 ? Math.round((totalTypeIncome / totalTypeMarketValue) * 100) : 0;
                const sectionId = `type-${type.replace(/\s+/g, '-')}-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
                return `
                    <div class="tpm-card tpm-collapsible" data-section-id="${sectionId}" style="margin-bottom:12px;">
                        <div class="tpm-card-header tpm-flex-between tpm-collapsible-header" data-target="${sectionId}" style="display:grid; grid-template-columns:1fr auto auto; gap:15px; align-items:center;">
                            <span style="color:var(--tpm-info); font-weight:600;">${type}</span>
                            <span style="color:var(--tpm-text-soft); font-size:12px;">${props.length} properties</span>
                            <span style="color:var(--tpm-success); font-size:12px; font-weight:600;">ROI: ${typeROI}%</span>
                            <span style="color:var(--tpm-text-soft); font-size:14px;" class="tpm-collapsible-arrow">▶</span>
                        </div>
                        <div id="${sectionId}" class="tpm-card-body tpm-collapsible-content" style="display:none;">
                            ${props.sort((a,b) => a.status === "rented" && b.status === "rented" ? a.daysLeft - b.daysLeft : 0).map(p => {
                                const statusIcon = buildStatusDisplay(p, customs);
                                const propertyROI = (p.marketPrice && p.marketPrice > 0) ? Math.round((p.lifetimeIncome / p.marketPrice) * 100) : 0;
                                const tenantNotesList = p.currentTenant ? getTenantNotes(p.currentTenant.id) : [];
                                const rentChanges = p.rentHistory.slice(-5).reverse();
                                const hasNotes = p.currentTenant && getTenantNotes(p.currentTenant.id).length > 0;
                                return `
                                    <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px; margin-bottom:8px;">
                                        <div class="tpm-flex-between">
                                            <div>
                                                <a href="https://www.torn.com/properties.php#/p=options&ID=${p.id}&tab=customize" target="${getLinkTarget()}" style="color:var(--tpm-link); text-decoration:none; font-size:14px; font-weight:600;">${p.name}</a>
                                                <span style="font-size:11px; color:var(--tpm-text-soft); margin-left:6px;">ID: ${p.id}</span>
                                            </div>
                                            <span style="font-size:14px;">${statusIcon}</span>
                                        </div>
                                        <div style="display:grid; grid-template-columns:1fr 1fr 1fr 1fr 1fr; gap:8px; margin:6px 0; font-size:12px;">
                                            <div><span style="color:var(--tpm-text-soft);">Happy:</span> <span style="color:var(--tpm-success);">${p.happy}</span></div>
                                            <div><span style="color:var(--tpm-text-soft);">Upkeep:</span> <span class="tpm-text-success">${FormatCurrency(p.upkeep)}</span></div>
                                            <div><span style="color:var(--tpm-text-soft);">Rent:</span> <span class="tpm-text-success">${p.rent > 0 ? FormatCurrency(p.rent)+'/d' : (p.lastRent > 0 ? FormatCurrency(p.lastRent)+'*' : '-')}</span></div>
                                            <div><span style="color:var(--tpm-text-soft);">Lease:</span> <span class="tpm-text-warning">${p.status === "rented" ? p.daysLeft+'d' : (p.lastLease ? p.lastLease+'d*' : '-')}</span></div>
                                            <div><span style="color:var(--tpm-text-soft);">ROI:</span> <span class="tpm-text-info">${propertyROI}%</span></div>
                                        </div>
                                        <div class="tpm-flex-between" style="margin-bottom:4px;">
                                            <div style="font-size:12px; display:flex; align-items:center; gap:4px; flex-wrap:wrap;">
                                                ${p.currentTenant ? `<span class="tpm-note-icon" data-tenant="${p.currentTenant.id}" data-name="${p.currentTenant.name}" data-active="true">${hasNotes ? '📖' : '📕'}</span> <a href="https://www.torn.com/profiles.php?XID=${p.currentTenant.id}" target="${getLinkTarget()}" style="color:var(--tpm-link);">${p.currentTenant.name}</a>` : '<span style="color:var(--tpm-text-soft);">Vacant</span>'}
                                            </div>
                                            <div style="font-size:12px; color:var(--tpm-info);">
                                                Lifetime: ${FormatCurrency(p.lifetimeIncome)}
                                                ${p.lifetimeIncome === 0 && p.status === 'rented' ? ' (active)' : ''}
                                            </div>
                                        </div>
                                        <div class="tpm-flex-between" style="margin-bottom:6px;">
                                            <div class="tpm-flex tpm-gap-1">
                                                <a href="https://www.torn.com/properties.php#/p=options&ID=${p.id}&tab=customize" target="${getLinkTarget()}" class="tpm-button-icon" style="font-size:11px;">🏠</a>
                                                ${p.status === "rented" ? `<a href="https://www.torn.com/properties.php#/p=options&ID=${p.id}&tab=offerExtension" target="${getLinkTarget()}" class="tpm-button-icon" style="font-size:11px;">↩️</a>` : `<a href="https://www.torn.com/properties.php#/p=options&ID=${p.id}&tab=lease" target="${getLinkTarget()}" class="tpm-button-icon" style="font-size:11px;">📝</a>`}
                                            </div>
                                            ${p.hasAutofill ? '<span style="font-size:10px; color:var(--tpm-text-soft);">History available</span>' : ''}
                                        </div>
                                        ${rentChanges.length > 0 ? `
                                            <details style="margin-top:4px;">
                                                <summary style="font-size:10px; color:var(--tpm-info); cursor:pointer;">Rent Changes (${rentChanges.length})</summary>
                                                <div style="margin-top:4px; max-height:100px; overflow-y:auto; background:var(--tpm-card-bg); padding:4px; border-radius:4px;">
                                                    ${rentChanges.map(r => `
                                                        <div style="font-size:9px; padding:2px; border-bottom:1px solid var(--tpm-border);">
                                                            ${r.formattedDate}: ${FormatCurrency(r.oldRent)} → ${FormatCurrency(r.newRent)}/d
                                                        </div>
                                                    `).join('')}
                                                </div>
                                            </details>
                                        ` : ''}
                                        ${tenantNotesList.length > 0 ? `
                                            <details style="margin-top:4px;">
                                                <summary style="font-size:10px; color:var(--tpm-info); cursor:pointer;">Notes (${tenantNotesList.length})</summary>
                                                <div style="margin-top:4px; max-height:100px; overflow-y:auto; background:var(--tpm-card-bg); padding:4px; border-radius:4px;">
                                                    ${tenantNotesList.slice(-3).map(n => `
                                                        <div style="font-size:9px; padding:2px; border-bottom:1px solid var(--tpm-border);">
                                                            ${n.formattedDate}: ${n.note}
                                                        </div>
                                                    `).join('')}
                                                </div>
                                            </details>
                                        ` : ''}
                                        <div id="history-${p.id}" class="tpm-history-placeholder"></div>
                                    </div>
                                `;
                            }).join('')}
                        </div>
                    </div>
                `;
            }).join('');
            container.innerHTML = `
                <div class="tpm-panel-close" data-target="tenantintel-content">✖</div>
                <div class="tpm-mb-2" style="display:flex; justify-content:space-between; align-items:center;">
                    <span style="color:var(--tpm-info); font-size:15px;">Portfolio Overview</span>
                    <span style="color:var(--tpm-text-soft); font-size:11px;">Total: ${totalProperties} (${totalRented}/${totalRentable} rented)</span>
                </div>
                ${financialHtml}
                ${topPerformersHtml}
                ${propertiesHtml}
                <div class="tpm-text-small tpm-text-center tpm-mt-2" style="color:var(--tpm-text-soft); padding:8px;">
                    * estimated from historical data · Click headers to expand/collapse
                </div>
            `;
            initCollapsibleListeners();
            container.querySelectorAll('.tpm-note-icon').forEach(icon => {
                icon.onclick = (e) => {
                    e.stopPropagation();
                    const tenant = icon.dataset.tenant;
                    const name = icon.dataset.name;
                    const active = icon.dataset.active === 'true';
                    showNotesBook(tenant, name, active);
                };
            });
            Object.values(propertyDetails).forEach(p => {
                const placeholder = document.getElementById(`history-${p.id}`);
                if (placeholder) {
                    addLeaseHistory(placeholder, p.id);
                }
            });
        } finally {
            hideInlineLoader(loaderId);
        }
    }
    function renderSettings(message = '') {
        const container = document.getElementById('settings-content');
        if (container) {
            const closeBtn = container.querySelector('.tpm-panel-close');
            container.innerHTML = '';
            if (closeBtn) container.appendChild(closeBtn);
        }
        const customs = getCustoms();
        const storage = getStorageInfo();
        container.insertAdjacentHTML('beforeend', `
            <div style="margin-top:15px; background:var(--tpm-card-bg); border-radius:8px; padding:16px;">
                <div class="tpm-flex-between tpm-mb-2" style="border-bottom:1px solid var(--tpm-border); padding-bottom:8px;">
                    <h4 style="color:var(--tpm-info); margin:0; font-size:16px;">⚙️ Settings</h4>
                    ${message ? `<span style="color:var(--tpm-danger); font-size:12px;">${message}</span>` : ''}
                </div>
                <div style="display:flex; flex-direction:column; gap:15px;">
                    <div style="background:var(--tpm-card-header-bg); padding:12px; border-radius:6px;">
                        <label style="display:block; color:var(--tpm-text-soft); font-size:12px; margin-bottom:4px;">API Key (Full Access)</label>
                        <input type="password" class="set-api" style="width:100%; background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:8px; border-radius:4px; font-size:13px;" value="${localStorage.getItem('tpmApiKey') || ''}">
                        <div class="tpm-text-small" style="margin-top:4px;">30-min cache, 60-sec override via refresh button · Optimized storage keeps 5x more history</div>
                    </div>
                    <div style="display:grid; grid-template-columns:1fr 1fr; gap:10px;">
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">Properties Per Page</label>
                            <input type="number" class="set-ipp" style="width:100%; background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:5px; border-radius:3px; font-size:13px;" value="${customs.items_per_page}" min="5" max="50">
                        </div>
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">Nav Threshold</label>
                            <input type="number" class="set-secnav" style="width:100%; background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:5px; border-radius:3px; font-size:13px;" value="${customs.propnavbar}" min="5" max="50">
                        </div>
                    </div>
                    <div style="display:grid; grid-template-columns:1fr 1fr; gap:10px;">
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">⚠️ Attention (days)</label>
                            <input type="number" class="set-ex" style="width:100%; background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:5px; border-radius:3px; font-size:13px;" value="${customs.warn_lease_ex}" min="1" max="30">
                        </div>
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">🟡 Warning (days)</label>
                            <input type="number" class="set-wa" style="width:100%; background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:5px; border-radius:3px; font-size:13px;" value="${customs.warn_lease_wa}" min="1" max="60">
                        </div>
                    </div>
                    <div style="display:grid; grid-template-columns:1fr 1fr; gap:10px;">
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">History Display Limit</label>
                            <input type="number" class="set-history-limit" style="width:100%; background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:5px; border-radius:3px; font-size:13px;" value="${customs.historyLimit}" min="5" max="50">
                        </div>
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">Debug Mode</label>
                            <select class="set-debug-mode" style="width:100%; background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:5px; border-radius:3px; font-size:13px;">
                                <option value="false" ${localStorage.getItem('tpm_debug_mode') !== 'true' ? 'selected' : ''}>Disabled</option>
                                <option value="true" ${localStorage.getItem('tpm_debug_mode') === 'true' ? 'selected' : ''}>Enabled</option>
                            </select>
                        </div>
                    </div>
                    <div style="display:grid; grid-template-columns:1fr 1fr; gap:10px;">
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">Link Opening</label>
                            <select class="set-linktarget" style="width:100%; background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:5px; border-radius:3px; font-size:13px;">
                                <option value="new" ${(localStorage.getItem('tpm_link_target') || 'new') === 'new' ? 'selected' : ''}>New Tab</option>
                                <option value="same" ${localStorage.getItem('tpm_link_target') === 'same' ? 'selected' : ''}>Same Tab</option>
                            </select>
                        </div>
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">Auto-Fill Forms</label>
                            <select class="set-autofill-lease" style="width:100%; background:var(--tpm-input-bg); color:var(--tpm-text-strong); border:1px solid var(--tpm-border); padding:5px; border-radius:3px; font-size:13px;">
                                <option value="true" ${(localStorage.getItem('tpm_autofill_lease') ?? 'true') === 'true' ? 'selected' : ''}>Enabled</option>
                                <option value="false" ${localStorage.getItem('tpm_autofill_lease') === 'false' ? 'selected' : ''}>Disabled</option>
                            </select>
                        </div>
                    </div>
                    <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                        <div style="color:var(--tpm-info); font-size:12px; margin-bottom:5px;">💾 Storage Management</div>
                        <div style="display:flex; justify-content:space-between; font-size:11px; margin-bottom:5px;">
                            <span>Usage: ${storage.sizeKB} KB / 5000 KB</span>
                            <span style="color:${storage.pressure > 85 ? 'var(--tpm-danger)' : (storage.pressure > 60 ? 'var(--tpm-warning)' : 'var(--tpm-success)')};">${storage.pressure.toFixed(1)}%</span>
                        </div>
                        <div style="font-size:10px; color:var(--tpm-text-soft); margin-bottom:8px;">
                            Optimized storage: 70-80% smaller · Keeping last ${storage.retentionMonths === 999 ? 'all' : storage.retentionMonths + ' months'} · Archive: ${tpmArchive.totalPropertiesArchived} sold
                        </div>
                    </div>
                    <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                        <div style="color:var(--tpm-info); font-size:12px; margin-bottom:5px;">📊 Storage Info</div>
                        <div style="display:flex; justify-content:space-between; font-size:11px;">
                            <span>Log events: ${storage.logCount}</span>
                            <span>Archive: ${FormatCurrency(tpmArchive.totalLifetimeIncome)}</span>
                        </div>
                    </div>
                    <div style="display:grid; grid-template-columns:1fr 1fr; gap:10px;">
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">📤 Export Data</label>
                            <button class="tpm-button" id="exportDataBtn" style="width:100%; margin-top:5px;">Download Backup</button>
                        </div>
                        <div style="background:var(--tpm-card-header-bg); padding:10px; border-radius:6px;">
                            <label class="tpm-text-small" style="font-size:11px;">📥 Import Data</label>
                            <input type="file" id="importDataFile" accept=".json" style="display:none;">
                            <button class="tpm-button" id="importDataBtn" style="width:100%; margin-top:5px;">Restore Backup</button>
                        </div>
                    </div>
                </div>
                <div class="tpm-flex-between tpm-mt-2" style="border-top:1px solid var(--tpm-border); padding-top:12px;">
                    <button class="tpm-button" id="clearAllData" style="background:var(--tpm-danger-btn-bg); color:white; font-weight:bold;">🗑️ Clear All Data</button>
                    <button class="save-settings tpm-button" style="background:var(--tpm-success-btn-bg); color:white; font-weight:bold;">💾 Save & Reload</button>
                </div>
            </div>
        `);
        document.getElementById('exportDataBtn')?.addEventListener('click', exportData);
        document.getElementById('importDataBtn')?.addEventListener('click', () => document.getElementById('importDataFile').click());
        document.getElementById('importDataFile')?.addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = (event) => importData(event.target.result);
                reader.readAsText(file);
            }
        });
        document.getElementById('clearAllData')?.addEventListener('click', () => {
            if (confirm('⚠️ Are you sure? This will clear ALL TPM stored data including API key and lease history.')) {
                const keysToRemove = [];
                for (let key in localStorage) {
                    if (key.startsWith('tpm_') || key === 'tpmApiKey' || key === 'prop_cache' || key === 'prop_last_call' || key === 'tenant_logs_cache' || key === 'tenant_logs_last_call') {
                        keysToRemove.push(key);
                    }
                }
                keysToRemove.forEach(key => localStorage.removeItem(key));
                showNotification('TPM data cleared. Reloading...', 'success');
                setTimeout(() => location.reload(), 1500);
            }
        });
        const saveBtn = document.querySelector('.save-settings');
        if (saveBtn) {
            saveBtn.onclick = async () => {
                const apiInput = document.querySelector('.set-api');
                if (!apiInput) return;
                const apiKey = apiInput.value.trim();
                if (!apiKey) {
                    showNotification('Please enter an API key', 'error');
                    return;
                }
                const isValid = await verifyApiKey(apiKey);
                if (isValid) {
                    localStorage.setItem('tpmApiKey', apiKey);
                    showNotification('API key saved. Reloading...', 'success');
                    setTimeout(() => location.reload(), 1500);
                }
            };
        }
    }
    function renderHelp() {
        const container = document.getElementById('help-content');
        if (container) {
            const closeBtn = container.querySelector('.tpm-panel-close');
            container.innerHTML = '';
            if (closeBtn) container.appendChild(closeBtn);
        }
        const customs = getCustoms();
        const storage = getStorageInfo();
        container.insertAdjacentHTML('beforeend', `
            <div class="help-section" style="margin-top:15px; background:var(--tpm-card-bg); border-radius:8px; padding:20px; max-height:70vh; overflow-y:auto;">
                <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px; border-bottom:1px solid var(--tpm-border); padding-bottom:10px; position:sticky; top:0; background:var(--tpm-card-bg); z-index:10;">
                    <h4 style="color:var(--tpm-info); margin:0; font-size:20px; font-weight:600;">📘 Torn Property Manager Documentation</h4>
                    <span style="color:var(--tpm-text-soft); font-size:12px;">v3.0.0 | Sprinkers [4056515]</span>
                </div>

                <div style="display:grid; grid-template-columns:repeat(3,1fr); gap:10px; margin-bottom:20px;">
                    <div style="background:var(--tpm-card-header-bg); border-radius:6px; padding:10px; text-align:center;">
                        <div style="color:var(--tpm-text-soft); font-size:11px;">Storage Used</div>
                        <div style="color:var(--tpm-info); font-size:16px; font-weight:600;">${storage.sizeKB} KB</div>
                        <div style="color:${storage.pressure > 85 ? 'var(--tpm-danger)' : (storage.pressure > 60 ? 'var(--tpm-warning)' : 'var(--tpm-success)')}; font-size:11px;">${storage.pressure.toFixed(1)}%</div>
                    </div>
                    <div style="background:var(--tpm-card-header-bg); border-radius:6px; padding:10px; text-align:center;">
                        <div style="color:var(--tpm-text-soft); font-size:11px;">Log Events</div>
                        <div style="color:var(--tpm-info); font-size:16px; font-weight:600;">${storage.logCount}</div>
                    </div>
                    <div style="background:var(--tpm-card-header-bg); border-radius:6px; padding:10px; text-align:center;">
                        <div style="color:var(--tpm-text-soft); font-size:11px;">Retention</div>
                        <div style="color:var(--tpm-warning); font-size:16px; font-weight:600;">${storage.retentionMonths === 999 ? 'All' : storage.retentionMonths + ' months'}</div>
                    </div>
                </div>

                <div style="display:grid; grid-template-columns:repeat(2,1fr); gap:20px; margin-bottom:20px;">
                    <div style="background:var(--tpm-card-header-bg); border-radius:8px; padding:15px;">
                        <div style="color:var(--tpm-info); font-size:16px; font-weight:600; margin-bottom:15px; border-bottom:1px solid var(--tpm-border); padding-bottom:5px;">🏘️ Property Dashboard</div>
                        <div style="display:grid; grid-template-columns:repeat(2,1fr); gap:10px; font-size:12px;">
                            <div>
                                <div style="font-weight:600; margin-bottom:5px; color:var(--tpm-text-strong);">Status Colors</div>
                                <div style="color:var(--tpm-text);"><span style="color:var(--tpm-success);">🟢</span> Rented Safe</div>
                                <div style="color:var(--tpm-text);"><span style="color:var(--tpm-warning);">🟡</span> Getting Close</div>
                                <div style="color:var(--tpm-text);"><span style="color:var(--tpm-alert);">⚠️</span> Expiring Soon</div>
                                <div style="color:var(--tpm-text);"><span style="color:var(--tpm-danger);">🔴</span> Expired/Empty</div>
                                <div style="color:var(--tpm-text);"><span style="color:var(--tpm-warning);">🔔</span> For Rent</div>
                                <div style="color:var(--tpm-text);"><span style="color:var(--tpm-info);">📢</span> For Sale</div>
                                <div style="color:var(--tpm-text);"><span style="color:var(--tpm-text-soft);">🕌</span> Personal Use</div>
                                <div style="color:var(--tpm-text);"><span style="color:var(--tpm-info);">📝</span> Offer/Extension</div>
                            </div>
                            <div>
                                <div style="font-weight:600; margin-bottom:5px; color:var(--tpm-text-strong);">Features</div>
                                <div>• Search by property/tenant</div>
                                <div>• Pagination (${customs.items_per_page} per page)</div>
                                <div>• Quick action buttons</div>
                                <div>• Hover highlighting</div>
                                <div>• Alert summary bar</div>
                                <div>• Last updated timestamp</div>
                                <div>• Notes icons (📖/📕)</div>
                            </div>
                        </div>
                    </div>

                    <div style="background:var(--tpm-card-header-bg); border-radius:8px; padding:15px;">
                        <div style="color:var(--tpm-info); font-size:16px; font-weight:600; margin-bottom:15px; border-bottom:1px solid var(--tpm-border); padding-bottom:5px;">📊 Tenant Intelligence</div>
                        <div style="font-size:12px;">
                            <div style="font-weight:600; margin-bottom:5px; color:var(--tpm-text-strong);">Portfolio Metrics</div>
                            <div style="display:grid; grid-template-columns:repeat(2,1fr); gap:5px; margin-bottom:10px;">
                                <div>• Daily Income</div><div>• Lifetime Income</div>
                                <div>• Portfolio ROI %</div><div>• Occupancy Rate</div>
                                <div>• Vacancy Loss</div><div>• 30/60/90d Projections</div>
                                <div>• Yearly Trends</div><div>• Growth %</div>
                                <div>• Avg Happy</div><div>• Rent/Happy Ratio</div>
                            </div>
                            <div style="font-weight:600; margin:10px 0 5px 0; color:var(--tpm-text-strong);">Top Performers</div>
                            <div>• Longest current leases</div>
                            <div>• Highest lifetime value</div>
                            <div>• Reliability scoring</div>
                            <div style="font-weight:600; margin:10px 0 5px 0; color:var(--tpm-text-strong);">Property Types</div>
                            <div>• Grouped by type with ROI %</div>
                            <div>• Individual property cards</div>
                            <div>• Complete lease history</div>
                            <div>• Rent change tracking</div>
                        </div>
                    </div>
                </div>

                <div style="display:grid; grid-template-columns:repeat(2,1fr); gap:20px; margin-bottom:20px;">
                    <div style="background:var(--tpm-card-header-bg); border-radius:8px; padding:15px;">
                        <div style="color:var(--tpm-info); font-size:16px; font-weight:600; margin-bottom:15px; border-bottom:1px solid var(--tpm-border); padding-bottom:5px;">📝 Lease/Renew Forms</div>
                        <div style="font-size:12px;">
                            <div style="margin-bottom:8px;"><span style="color:var(--tpm-success);">✓</span> Auto-fill with last recorded values</div>
                            <div style="margin-bottom:8px;"><span style="color:var(--tpm-success);">✓</span> Live calculator (updates as you type)</div>
                            <div style="margin-bottom:8px;"><span style="color:var(--tpm-success);">✓</span> Market analysis with IQR filtering</div>
                            <div style="margin-bottom:8px;"><span style="color:var(--tpm-success);">✓</span> Records discovered warning</div>
                            <div style="margin-bottom:8px;"><span style="color:var(--tpm-success);">✓</span> Complete property history</div>
                            <div style="margin-bottom:8px;"><span style="color:var(--tpm-success);">✓</span> System events (🏛️ Torn System)</div>
                            <div style="margin-bottom:8px;"><span style="color:var(--tpm-success);">✓</span> Unknown users (ID with profile link)</div>
                            <div style="margin-bottom:8px;"><span style="color:var(--tpm-success);">✓</span> Known users (name + notes icon)</div>
                            <div style="margin-bottom:8px;"><span style="color:var(--tpm-success);">✓</span> Escape key closes sections</div>
                        </div>
                    </div>

                    <div style="background:var(--tpm-card-header-bg); border-radius:8px; padding:15px;">
                        <div style="color:var(--tpm-info); font-size:16px; font-weight:600; margin-bottom:15px; border-bottom:1px solid var(--tpm-border); padding-bottom:5px;">📚 Notes Book</div>
                        <div style="font-size:12px;">
                            <div style="margin-bottom:8px;">• 📕/📖 icons indicate notes status</div>
                            <div style="margin-bottom:8px;">• Click icon to open Notes Book</div>
                            <div style="margin-bottom:8px;">• Searchable tenant list</div>
                            <div style="margin-bottom:8px;">• Show/hide inactive tenants</div>
                            <div style="margin-bottom:8px;">• Add/edit notes with timestamps</div>
                            <div style="margin-bottom:8px;">• Active/former tenant indicators</div>
                            <div style="margin-bottom:8px;">• Clear all notes option</div>
                            <div style="margin-bottom:8px;">• Notes persist across sessions</div>
                        </div>
                    </div>
                </div>

                <div style="display:grid; grid-template-columns:repeat(2,1fr); gap:20px; margin-bottom:20px;">
                    <div style="background:var(--tpm-card-header-bg); border-radius:8px; padding:15px;">
                        <div style="color:var(--tpm-info); font-size:16px; font-weight:600; margin-bottom:15px; border-bottom:1px solid var(--tpm-border); padding-bottom:5px;">💾 Storage Management</div>
                        <div style="font-size:12px;">
                            <div style="margin-bottom:8px;">• Optimized format (70-80% smaller)</div>
                            <div style="margin-bottom:8px;">• Auto-rotation at storage thresholds</div>
                            <div style="margin-bottom:8px;">• Archive preserves aggregates</div>
                            <div style="margin-bottom:8px;">• Export/Import full backup</div>
                            <div style="margin-bottom:8px;">• Pressure warnings at >90%</div>
                            <div style="margin-bottom:8px;">• Critical cleanup at >95%</div>
                        </div>
                    </div>

                    <div style="background:var(--tpm-card-header-bg); border-radius:8px; padding:15px;">
                        <div style="color:var(--tpm-info); font-size:16px; font-weight:600; margin-bottom:15px; border-bottom:1px solid var(--tpm-border); padding-bottom:5px;">🔌 API Integration</div>
                        <div style="font-size:12px;">
                            <div style="margin-bottom:8px;">• FULL ACCESS key required</div>
                            <div style="margin-bottom:8px;">• Rate limits: 50/min, 1000/hour</div>
                            <div style="margin-bottom:8px;">• 30-min cache, 60-sec override</div>
                            <div style="margin-bottom:8px;">• Cross-tab synchronization</div>
                            <div style="margin-bottom:8px;">• Auto-retry with backoff</div>
                            <div style="margin-bottom:8px;">• Batch processing (100 items)</div>
                        </div>
                    </div>
                </div>

                <div style="background:var(--tpm-card-header-bg); border-radius:8px; padding:15px; margin-bottom:20px;">
                    <div style="color:var(--tpm-info); font-size:16px; font-weight:600; margin-bottom:15px; border-bottom:1px solid var(--tpm-border); padding-bottom:5px;">⚙️ Settings</div>
                    <div style="display:grid; grid-template-columns:repeat(4,1fr); gap:15px; font-size:12px;">
                        <div>
                            <div style="font-weight:600;">API Key</div>
                            <div>FULL ACCESS</div>
                        </div>
                        <div>
                            <div style="font-weight:600;">Properties Per Page</div>
                            <div>${customs.items_per_page}</div>
                        </div>
                        <div>
                            <div style="font-weight:600;">Nav Threshold</div>
                            <div>${customs.propnavbar}</div>
                        </div>
                        <div>
                            <div style="font-weight:600;">⚠️ Attention</div>
                            <div>${customs.warn_lease_ex} days</div>
                        </div>
                        <div>
                            <div style="font-weight:600;">🟡 Warning</div>
                            <div>${customs.warn_lease_wa} days</div>
                        </div>
                        <div>
                            <div style="font-weight:600;">History Limit</div>
                            <div>${customs.historyLimit}</div>
                        </div>
                        <div>
                            <div style="font-weight:600;">Link Opening</div>
                            <div>${localStorage.getItem('tpm_link_target') === 'same' ? 'Same Tab' : 'New Tab'}</div>
                        </div>
                        <div>
                            <div style="font-weight:600;">Auto-Fill</div>
                            <div>${isLeaseAutofillEnabled() ? 'Enabled' : 'Disabled'}</div>
                        </div>
                        <div>
                            <div style="font-weight:600;">Debug Mode</div>
                            <div>${isDebugMode() ? 'On' : 'Off'}</div>
                        </div>
                    </div>
                </div>

                <div style="background:var(--tpm-card-header-bg); border-radius:8px; padding:15px; text-align:center; font-size:11px; color:var(--tpm-text-soft);">
                    <p style="margin:2px 0;">🔄 Refresh: 30-min cache, 60-sec override • 🌓 Auto dark/light mode • 📊 Click headers to expand</p>
                    <p style="margin:2px 0;">📦 All data stored locally • ⏱️ Rate limits respected • 🗂️ Smart rotation at 60% capacity</p>
                    <p style="margin:10px 0 0 0; color:var(--tpm-info);">Author: Sprinkers [4056515]</p>
                </div>
            </div>
        `);
    }
    let formProcessed = false;
    let formFillTimeout = null;
    function addMarketSuggestions(container, propertyType) {
        const existing = container.parentNode ? container.parentNode.querySelectorAll('.tpm-market-suggestion') : [];
        existing.forEach(el => el.remove());
        const stored = JSON.parse(localStorage.getItem('tpm_property_autofill') || "{}");
        const typeRecords = Object.values(stored).filter(v => v.type === propertyType && v.status === "rented" && v.rent > 0);
        const suggestionDiv = document.createElement('div');
        suggestionDiv.className = 'tpm-market-suggestion';
        if (typeRecords.length === 0) {
            suggestionDiv.innerHTML = `ℹ️ No historical data for ${propertyType} yet. Enter your first rental to start building history.`;
        } else {
            let rents = typeRecords.map(v => v.rent).sort((a, b) => a - b);
            const percentile = (arr, p) => {
                const index = (arr.length - 1) * p;
                const lower = Math.floor(index);
                const upper = Math.ceil(index);
                if (lower === upper) return arr[index];
                return Math.round(arr[lower] + (arr[upper] - arr[lower]) * (index - lower));
            };
            const q1 = percentile(rents, 0.25);
            const q3 = percentile(rents, 0.75);
            const iqr = q3 - q1;
            const min = q1 - 1.5 * iqr;
            const max = q3 + 1.5 * iqr;
            const filtered = rents.filter(v => v >= min && v <= max);
            if (filtered.length) rents = filtered;
            const median = percentile(rents, 0.50);
            const avg = Math.round(rents.reduce((a, b) => a + b, 0) / rents.length);
            const low = percentile(rents, 0.25);
            const high = percentile(rents, 0.75);
            suggestionDiv.innerHTML = `📊 Market rates for ${propertyType}: Avg ${FormatCurrency(avg)}/d | Median ${FormatCurrency(median)}/d | Range ${FormatCurrency(low)}-${FormatCurrency(high)}/d`;
        }
        if (container.parentNode && container.parentNode.firstChild) {
            container.parentNode.insertBefore(suggestionDiv, container);
        } else {
            container.insertAdjacentElement('beforebegin', suggestionDiv);
        }
    }
    function addLiveCalculator(container, dailyRent, propertyId, days) {
        const existing = container.parentNode ? container.parentNode.querySelectorAll('.tpm-calculator') : [];
        existing.forEach(el => el.remove());
        let costInput, daysInput;
        if (container && container.classList) {
            if (container.classList.contains('offerExtension-form')) {
                costInput = container.querySelector('input[data-name="offercost"]');
                daysInput = container.querySelector('input[data-name="days"]');
            } else {
                costInput = container.querySelector('input[data-name="money"]');
                daysInput = container.querySelector('input[data-name="days"]');
            }
        }
        if (!costInput || !daysInput) {
            const form = document.querySelector('.offerExtension-form, .lease-form');
            if (form && form !== container) {
                return addLiveCalculator(form, dailyRent, propertyId, days);
            }
            return;
        }
        const calculatorDiv = document.createElement('div');
        calculatorDiv.className = 'tpm-calculator';
        const currentDays = parseInt(daysInput.value) || days || 0;
        const currentDailyRent = dailyRent > 0 ? dailyRent : 0;
        const currentTotal = currentDailyRent * currentDays;
        calculatorDiv.innerHTML = `
            <div style="display:flex; justify-content:space-between; align-items:center; width:100%;">
                <span><span style="color:var(--tpm-text-soft);">Daily Rent:</span> <span id="tpm-calc-rent-${propertyId}" style="font-weight:600;">${FormatCurrency(currentDailyRent)}</span></span>
                <span style="color:var(--tpm-text-soft);">×</span>
                <span><span style="color:var(--tpm-text-soft);">Days:</span> <span id="tpm-calc-days-${propertyId}" style="font-weight:600;">${currentDays}</span></span>
                <span style="color:var(--tpm-text-soft);">=</span>
                <span><span style="color:var(--tpm-text-soft);">Total:</span> <span id="tpm-calc-total-${propertyId}" class="tpm-calculator-value">${FormatCurrency(currentTotal)}</span></span>
            </div>
        `;
        if (container.parentNode) {
            const warningDiv = container.querySelector('.tpm-autofill-warning');
            if (warningDiv) {
                warningDiv.insertAdjacentElement('afterend', calculatorDiv);
            } else {
                container.parentNode.insertBefore(calculatorDiv, container);
            }
        } else {
            container.insertAdjacentElement('beforebegin', calculatorDiv);
        }
        function updateCalculator() {
            const days = parseInt(daysInput.value) || 0;
            const currentRent = dailyRent > 0 ? dailyRent : (parseFloat(costInput.value) / (days || 1)) || 0;
            const total = currentRent * days;
            const rentSpan = document.getElementById(`tpm-calc-rent-${propertyId}`);
            const daysSpan = document.getElementById(`tpm-calc-days-${propertyId}`);
            const totalSpan = document.getElementById(`tpm-calc-total-${propertyId}`);
            if (rentSpan) rentSpan.textContent = FormatCurrency(currentRent);
            if (daysSpan) daysSpan.textContent = days;
            if (totalSpan) totalSpan.textContent = FormatCurrency(total);
        }
        costInput.addEventListener('input', updateCalculator);
        daysInput.addEventListener('input', updateCalculator);
        updateCalculator();
    }
    function resolveTenantName(tenantID) {
        const store = getStore();
        const stored = store.tenants?.[tenantID];
        if (stored && stored.n && stored.n !== 'Unknown' && stored.n !== 'Unknown Tenant') {
            return stored.n;
        }
        const properties = safeJSON("prop_cache");
        for (const [id, p] of properties) {
            if (p.rented_by?.id == tenantID && p.rented_by?.name && p.rented_by.name !== 'Unknown') {
                return p.rented_by.name;
            }
            if (p.renter_asked?.id == tenantID && p.renter_asked?.name && p.renter_asked.name !== 'Unknown') {
                return p.renter_asked.name;
            }
        }
        if (store.events) {
            for (const propEvents of Object.values(store.events)) {
                for (const e of propEvents) {
                    if (e[5] == tenantID) {
                        const tenant = store.tenants?.[e[5]];
                        if (tenant && tenant.n && tenant.n !== 'Unknown' && tenant.n !== 'Unknown Tenant') {
                            return tenant.n;
                        }
                    }
                }
            }
        }
        return tenantID;
    }
    function addLeaseHistory(container, propertyId) {
        const existing = container.parentNode
            ? container.parentNode.querySelectorAll('.tpm-history-section')
            : [];
        existing.forEach(el => {
            if (el._tpmListener) {
                window.removeEventListener(STORE_EVENTS.UPDATED, el._tpmListener);
            }
            el.remove();
        });
        const store = getStore();
        if (!store.events) return;
        const propertyEvents = store.events[propertyId] || [];
        const tenantNotes = safeJSON("tpm_tenant_notes") || {};
        const allEvents = [];
        const apiKey = localStorage.getItem('tpmApiKey');
        propertyEvents.forEach(e => {
            const tenantID = String(e[5] ?? '').trim();
            const tenant = store.tenants?.[tenantID];
            let displayName, hasNotes, type;
            const isSystem =
                !tenantID ||
                tenantID === '0' ||
                tenantID === 'unknown';
            if (isSystem) {
                displayName = '🏛️ Torn System';
                hasNotes = false;
                type = 'system';
            }
            else {
                let resolvedName = null;
                if (!tenant || !tenant.n || tenant.n === 'Unknown' || tenant.n === 'Unknown Tenant') {
                    resolvedName = resolveTenantName(tenantID);
                }
                if (resolvedName && resolvedName !== tenantID) {
                    displayName = resolvedName;
                    hasNotes = (tenantNotes?.[tenantID]?.length || 0) > 0;
                    type = 'knownUser';
                    if (apiKey && !window[`fetching_${tenantID}`]) {
                        window[`fetching_${tenantID}`] = true;
                        setTimeout(() => {
                            fetch(`https://api.torn.com/v2/user/${tenantID}?key=${apiKey}`)
                                .then(r => r.json())
                                .then(data => {
                                    if (data && data.name && data.name !== 'Unknown') {
                                        storeTenantInfo(tenantID, data.name);
                                    }
                                    delete window[`fetching_${tenantID}`];
                                })
                                .catch(() => delete window[`fetching_${tenantID}`]);
                        }, 100);
                    }
                }
                else if (tenant && tenant.n && tenant.n !== 'Unknown' && tenant.n !== 'Unknown Tenant') {
                    displayName = tenant.n;
                    hasNotes = (tenantNotes?.[tenantID]?.length || 0) > 0;
                    type = 'knownUser';
                }
                else {
                    displayName = tenantID;
                    hasNotes = false;
                    type = 'unknownUser';
                    if (apiKey && !window[`fetching_${tenantID}`]) {
                        window[`fetching_${tenantID}`] = true;
                        setTimeout(() => {
                            fetch(`https://api.torn.com/v2/user/${tenantID}?key=${apiKey}`)
                                .then(r => r.json())
                                .then(data => {
                                    if (data && data.name && data.name !== 'Unknown') {
                                        storeTenantInfo(tenantID, data.name);
                                    }
                                    delete window[`fetching_${tenantID}`];
                                })
                                .catch(() => delete window[`fetching_${tenantID}`]);
                        }, 100);
                    }
                }
            }
            allEvents.push({
                time: e[0],
                event: EVENT_NAMES[e[1]] || 'unknown',
                dailyRent: e[2],
                days: e[3],
                totalPayment: e[4],
                tenantID,
                displayName,
                hasNotes,
                type
            });
        });
        const sortedEvents = [...allEvents].sort(
            (a, b) => Number(b.time) - Number(a.time)
        );
        const historyDiv = document.createElement('details');
        historyDiv.className = 'tpm-history-section';
        historyDiv.dataset.propertyId = propertyId;
        let historySummary = sortedEvents.length > 0
            ? `📜 Complete Property History: ✔️ ${sortedEvents.length} Found.`
            : '📜 Complete Property History: ❌ 0 Found.';
        let historyHtml =
            `<summary style="font-size:11px; color:var(--tpm-info); cursor:pointer; margin-bottom:4px;">${historySummary}</summary>`;
        historyHtml +=
            '<div style="max-height:200px; overflow-y:auto; background:var(--tpm-card-bg); padding:8px; border-radius:4px;">';
        if (sortedEvents.length > 0) {
            sortedEvents.forEach(e => {
                const date = e.time
                    ? new Date(Number(e.time)).toLocaleDateString()
                    : 'Unknown date';
                const eventText = (e.event || '').toLowerCase();
                const eventClass =
                    eventText.includes('rented') ||
                    eventText.includes('accepted')
                        ? 'tpm-text-success'
                        : eventText.includes('decline')
                        ? 'tpm-text-danger'
                        : 'tpm-text-soft';
                let displayHtml;
                if (e.type === 'system') {
                    displayHtml = e.displayName;
                }
                else if (e.type === 'unknownUser') {
                    displayHtml =
                        `🏛️ Torn System: [No Name] [ID Link: <a href="https://www.torn.com/profiles.php?XID=${e.tenantID}" target="${getLinkTarget()}" style="color:var(--tpm-link);">${e.displayName}</a>]`;
                }
                else {
                    const noteIcon = e.hasNotes ? '📖' : '📕';
                    displayHtml =
                        `<span class="tpm-note-icon" data-tenant="${e.tenantID}" data-name="${e.displayName}" data-active="false">${noteIcon}</span> ` +
                        `<a href="https://www.torn.com/profiles.php?XID=${e.tenantID}" target="${getLinkTarget()}" style="color:var(--tpm-link);">${e.displayName}</a>`;
                }
                historyHtml +=
                    `<div class="tpm-history-item">
                        <div class="tpm-history-main">
                            <span>${date} - ${displayHtml}</span>
                            <span class="${eventClass}">${(e.event || '').replace(/_/g, ' ')}</span>
                        </div>
                        <div class="tpm-history-details">
                            <span>${e.days ? e.days + ' days' : ''}</span>
                            <span>${e.dailyRent ? '$' + Number(e.dailyRent).toLocaleString() + '/d' : ''}</span>
                            <span>${e.totalPayment ? 'Total: $' + Number(e.totalPayment).toLocaleString() : ''}</span>
                        </div>
                    </div>`;
            });
        } else {
            historyHtml +=
                '<div class="tpm-history-empty">No lease records found for this property</div>';
        }
        historyHtml += '</div>';
        historyDiv.innerHTML = historyHtml;
        const listener = (e) => {
            if (historyDiv.isConnected) {
                addLeaseHistory(container, propertyId);
            } else {
                window.removeEventListener(STORE_EVENTS.UPDATED, listener);
            }
        };
        historyDiv._tpmListener = listener;
        window.addEventListener(STORE_EVENTS.UPDATED, listener);
        if (container.parentNode) {
            const warningDiv = container.querySelector('.tpm-autofill-warning');
            if (warningDiv) {
                warningDiv.insertAdjacentElement('afterend', historyDiv);
            } else {
                container.parentNode.appendChild(historyDiv);
            }
        } else {
            container.appendChild(historyDiv);
        }
        setTimeout(() => {
            historyDiv.querySelectorAll('.tpm-note-icon').forEach(icon => {
                icon.onclick = (e) => {
                    e.stopPropagation();
                    const tenant = icon.dataset.tenant;
                    const name = icon.dataset.name;
                    const active = icon.dataset.active === 'true';
                    showNotesBook(tenant, name, active);
                };
            });
        }, 100);
    }
    function observeLeaseForms() {
        if (!isLeaseAutofillEnabled()) return;
        const url = new URL(window.location.href);
        if (!url.hash.includes('tab=offerExtension') && !url.hash.includes('tab=lease')) return;
        const propertyId = url.hash.match(/[Ii][Dd]=(\d+)/)?.[1];
        if (!propertyId) return;
        if (formFillTimeout) {
            clearTimeout(formFillTimeout);
            formFillTimeout = null;
        }
        const propertyData = getPropertyAutofill(propertyId);
        const store = getStore();
        const propertyEvents = store.events?.[propertyId] || [];
        const sortedEvents = propertyEvents.sort((a,b) => b[0] - a[0]);
        const lastEvent = sortedEvents.length > 0 ? sortedEvents[0] : null;
        let rentValue = 0;
        let leaseValue = 0;
        if (lastEvent && lastEvent[2] && lastEvent[3]) {
            rentValue = lastEvent[2];
            leaseValue = lastEvent[3];
            if (isDebugMode()) console.log(`TPM: Using last record - ${FormatCurrency(rentValue)}/d for ${leaseValue} days`);
        } else if (propertyData) {
            rentValue = propertyData?.rent ?? 0;
            leaseValue = propertyData?.lease ?? 0;
            if (isDebugMode()) console.log(`TPM: Using autofill - ${FormatCurrency(rentValue)}/d for ${leaseValue} days`);
        }
        const currentType = propertyData?.type || 'Unknown';
        const stored = JSON.parse(localStorage.getItem('tpm_property_autofill') || "{}");
        const typeRecords = Object.values(stored).filter(v => v.type === currentType && v.status === "rented" && v.rent > 0 && v.lease > 0);
        let hasValidRecords = false;
        if (typeRecords.length > 0) {
            hasValidRecords = true;
            if (rentValue === 0) {
                let rents = typeRecords.map(v => v.rent).sort((a, b) => a - b);
                const percentile = (arr, p) => {
                    const index = (arr.length - 1) * p;
                    const lower = Math.floor(index);
                    const upper = Math.ceil(index);
                    if (lower === upper) return arr[index];
                    return Math.round(arr[lower] + (arr[upper] - arr[lower]) * (index - lower));
                };
                const q1 = percentile(rents, 0.25);
                const q3 = percentile(rents, 0.75);
                const iqr = q3 - q1;
                const min = q1 - 1.5 * iqr;
                const max = q3 + 1.5 * iqr;
                const filtered = rents.filter(v => v >= min && v <= max);
                if (filtered.length) rents = filtered;
                const avgRent = Math.round(rents.reduce((a, b) => a + b, 0) / rents.length);
                const medianRent = percentile(rents, 0.50);
                rentValue = medianRent;
            }
        }
        if (leaseValue > 100) leaseValue = 100;
        if (leaseValue < 7) leaseValue = 7;
        let totalCost = rentValue * leaseValue;
        const checkAndFillForms = () => {
            if (formProcessed) return;
            const costInputs = document.querySelectorAll(
                'input[data-name="offercost"]:not([data-tpm-filled]),' +
                'input[data-name="money"]:not([data-tpm-filled]),' +
                'input[name="cost"]:not([data-tpm-filled])'
            );
            costInputs.forEach(costInput => {
                const container = costInput.closest('ul') || costInput.parentElement;
                const daysInput = container.querySelector('input[data-name="days"], input[name="days"]');
                if (!daysInput) return;
                costInput.value = totalCost;
                daysInput.value = leaseValue;
                const costHidden = container.querySelector(
                    'input[type="hidden"][name="offercost"], input[type="hidden"][name="money"]'
                );
                const daysHidden = container.querySelector('input[type="hidden"][name="days"]');
                if (costHidden) costHidden.value = totalCost;
                if (daysHidden) daysHidden.value = leaseValue;
                [costInput, daysInput].forEach(el => {
                    ['input', 'change', 'blur'].forEach(ev => el.dispatchEvent(new Event(ev, { bubbles: true })));
                });
                costInput.setAttribute('data-tpm-filled', 'true');
                const propertyInfoCont = document.querySelector('.property-info-cont');
                if (!propertyInfoCont) return;
                document.querySelectorAll('.tpm-market-suggestion, .tpm-calculator, .tpm-autofill-warning, .tpm-history-section, .tpm-smart-pricing').forEach(el => el.remove());
                if (typeRecords.length) {
                    const rents = typeRecords.map(v => v.rent).sort((a, b) => a - b);
                    const percentile = (arr, p) => {
                        const index = (arr.length - 1) * p;
                        const lower = Math.floor(index);
                        const upper = Math.ceil(index);
                        if (lower === upper) return arr[index];
                        return Math.round(arr[lower] + (arr[upper] - arr[lower]) * (index - lower));
                    };
                    const low = percentile(rents, 0.25);
                    const median = percentile(rents, 0.50);
                    const high = percentile(rents, 0.75);
                    const avg = Math.round(rents.reduce((a, b) => a + b, 0) / rents.length);
                    const marketDiv = document.createElement('div');
                    marketDiv.className = 'tpm-market-suggestion';
                    marketDiv.style.cssText = 'margin-bottom:8px;padding:8px;background:var(--tpm-card-bg);border-radius:4px;font-size:11px;color:var(--tpm-text-soft);';
                    marketDiv.innerHTML = `📊 Market Analysis (${typeRecords.length} records) · Avg: ${FormatCurrency(avg)} · Median: ${FormatCurrency(median)} · Range: ${FormatCurrency(low)}-${FormatCurrency(high)}`;
                    propertyInfoCont.insertAdjacentElement('beforebegin', marketDiv);
                }
                const calculatorDiv = document.createElement('div');
                calculatorDiv.className = 'tpm-calculator';
                calculatorDiv.style.cssText = 'margin-bottom:8px;background:var(--tpm-calculator-bg);border:1px solid var(--tpm-calculator-border);border-radius:6px;padding:10px 16px;font-size:13px;';
                calculatorDiv.innerHTML = `
                    <div style="display:flex; justify-content:space-between; align-items:center; width:100%;">
                        <span><span style="color:var(--tpm-text-soft);">Daily Rent:</span> <span id="tpm-calc-rent-${propertyId}" style="font-weight:600;">${FormatCurrency(rentValue)}</span></span>
                        <span style="color:var(--tpm-text-soft);">×</span>
                        <span><span style="color:var(--tpm-text-soft);">Days:</span> <span id="tpm-calc-days-${propertyId}" style="font-weight:600;">${leaseValue}</span></span>
                        <span style="color:var(--tpm-text-soft);">=</span>
                        <span><span style="color:var(--tpm-text-soft);">Total:</span> <span id="tpm-calc-total-${propertyId}" class="tpm-calculator-value" style="font-weight:600;color:var(--tpm-success);background:rgba(46,125,50,0.1);padding:4px 10px;border-radius:20px;">${FormatCurrency(totalCost)}</span></span>
                    </div>
                `;
                const lastBefore = propertyInfoCont.previousElementSibling;
                if (lastBefore && lastBefore.classList.contains('tpm-market-suggestion')) {
                    lastBefore.insertAdjacentElement('afterend', calculatorDiv);
                } else {
                    propertyInfoCont.insertAdjacentElement('beforebegin', calculatorDiv);
                }
                function updateCalculator() {
                    // const visibleForm = document.querySelector('.offerExtension-form:not([style*="display: none"]), .lease-form:not([style*="display: none"])');

                    let visibleForm = document.querySelector('.offerExtension-form:not([style*="display: none"]), .lease-form:not([style*="display: none"])');
                    if (!visibleForm) {
                    const allForms = Array.from(document.querySelectorAll('form, div[class*="form"], ul[class*="form"]')).filter(form => {
                        if (form.offsetParent === null) return false;
                            const hasCost = form.querySelector('input[data-name="offercost"], input[data-name="money"], input[name="cost"]');
                            const hasDays = form.querySelector('input[data-name="days"], input[name="days"]');
                            return hasCost && hasDays;
                        });
                        visibleForm = allForms[0];
                    }

                    if (!visibleForm) return;
                    const visibleCost = visibleForm.querySelector('input[data-name="offercost"], input[data-name="money"], input[name="cost"]');
                    const visibleDays = visibleForm.querySelector('input[data-name="days"], input[name="days"]');
                    if (!visibleCost || !visibleDays) return;
                    const days = parseInt(visibleDays.value) || 0;
                    const currentRent = parseFloat(visibleCost.value.replace(/[^0-9]/g, '')) / (days || 1) || 0;
                    const total = currentRent * days;
                    const rentSpan = document.getElementById(`tpm-calc-rent-${propertyId}`);
                    const daysSpan = document.getElementById(`tpm-calc-days-${propertyId}`);
                    const totalSpan = document.getElementById(`tpm-calc-total-${propertyId}`);
                    if (rentSpan) rentSpan.textContent = FormatCurrency(currentRent);
                    if (daysSpan) daysSpan.textContent = days;
                    if (totalSpan) totalSpan.textContent = FormatCurrency(total);
                }
                costInput.addEventListener('input', updateCalculator);
                daysInput.addEventListener('input', updateCalculator);
                updateCalculator();
                calculatorDiv._tpmUpdate = updateCalculator;
                const warnDiv = document.createElement('div');
                warnDiv.className = 'tpm-autofill-warning';
                warnDiv.style.cssText = 'margin-bottom:8px;padding:8px;background:#332600;color:#ffcc00;border-radius:4px;font-size:11px;text-align:center;';
                const hasValidRecord = propertyData && propertyData.rent > 0 && propertyData.lease > 0;
                warnDiv.textContent = hasValidRecord || hasValidRecords
                    ? `🟢 Records Discovered: ${FormatCurrency(rentValue)} per day for ${leaseValue} days totaling ${FormatCurrency(totalCost)}.`
                    : "🔴 No stored records found for this property.";
                propertyInfoCont.insertAdjacentElement('beforebegin', warnDiv);
                const propertyInfoContParent = document.querySelector('.property-info-cont');
                if (propertyInfoContParent && !window._tpmTabListenerAdded) {
                    const tabs = propertyInfoContParent.querySelector('.tabs a, .tabs button');
                    if (tabs) {
                        window._tpmTabListenerAdded = true;
                        propertyInfoContParent.addEventListener('click', (e) => {
                            const tabBtn = e.target.closest('.tabs a, .tabs button');
                            if (tabBtn) {
                                setTimeout(() => {
                                    const calculator = document.querySelector('.tpm-calculator');
                                    if (calculator && calculator._tpmUpdate) {
                                        calculator._tpmUpdate();
                                    }
                                }, 50);
                            }
                        });
                    }
                }
                const historyContainer = container.closest('div[id="market"], div[id="user"]') || propertyInfoCont;
                if (historyContainer && !historyContainer.querySelector('.tpm-history-section')) {
                    addLeaseHistory(historyContainer, propertyId);
                }
                setTimeout(() => {
                    const historyDiv = document.querySelector('.tpm-history-section');
                    if (historyDiv) {
                        propertyInfoCont.insertAdjacentElement('afterend', historyDiv);
                    }
                }, 100);
                setTimeout(() => {
                    const historyDiv = document.querySelector('.tpm-history-section');
                    const calculatorDiv = document.querySelector('.tpm-calculator');
                    if (historyDiv && calculatorDiv) {
                        const lastAfter = propertyInfoCont.nextElementSibling;
                        if (lastAfter && lastAfter.classList.contains('tpm-history-section')) {
                            lastAfter.insertAdjacentElement('afterend', calculatorDiv);
                        } else {
                            propertyInfoCont.insertAdjacentElement('afterend', calculatorDiv);
                        }
                    }
                }, 200);
                formProcessed = true;
            });
        };
        formFillTimeout = setTimeout(checkAndFillForms, 500);
        if (window.tpmLeaseObserver) {
            window.tpmLeaseObserver.disconnect();
            window.tpmLeaseObserver = null;
        }
        window.tpmLeaseObserver = new MutationObserver(checkAndFillForms);
        window.tpmLeaseObserver.observe(document.body, { childList: true, subtree: true });
    }
    let lastPropertyID = null;
    setInterval(() => {
        const id = getCurrentPropertyID();
        if (id && id !== lastPropertyID) {
            lastPropertyID = id;
            formProcessed = false;
            observeLeaseForms();
        }
    }, 800);
    const pageObserver = new MutationObserver(() => {
        const target = document.getElementById('properties-page-wrap');
        if (target && !document.getElementById('torn-property-manager-root')) initWhenReady();
    });
    pageObserver.observe(document.body, { childList: true, subtree: true });
    window.addEventListener('beforeunload', () => { tabChannel.close(); });
    tabChannel.addEventListener('message', (event) => {
        const { type, data, timestamp } = event.data;
        if (timestamp <= TAB_COORDINATION.lastUpdate) return;
        switch(type) {
            case 'PROPERTIES_UPDATED':
                localStorage.setItem('prop_cache', JSON.stringify(data.properties));
                localStorage.setItem('prop_last_call', data.timestamp);
                TAB_COORDINATION.lastUpdate = timestamp;
                if (isDebugMode()) console.log('TPM: Properties updated from another tab');
                if (document.getElementById('prop-content')?.style.display === 'block') {
                    loadProperties();
                }
                break;
            case 'LOGS_UPDATED':
                localStorage.setItem('tenant_logs_cache', JSON.stringify(data.logs));
                localStorage.setItem('tenant_logs_last_call', data.timestamp);
                TAB_COORDINATION.lastUpdate = timestamp;
                if (isDebugMode()) console.log('TPM: Lease logs updated from another tab');
                if (document.getElementById('tenantintel-content')?.style.display === 'block') {
                    renderTenantIntel();
                }
                break;
            case 'STORE_UPDATED':
                localStorage.setItem('tpm_store_v2', JSON.stringify(data.store));
                localStorage.setItem('tpm_archive', JSON.stringify(data.archive));
                tpmArchive = data.archive;
                TAB_COORDINATION.lastUpdate = timestamp;
                if (isDebugMode()) console.log('TPM: Store updated from another tab');
                if (document.getElementById('tenantintel-content')?.style.display === 'block') {
                    renderTenantIntel();
                }
                break;
        }
    });
    function initWhenReady() {
        const target = document.getElementById('properties-page-wrap');
        if (!target) return;
        if (document.getElementById('torn-property-manager-root')) return;
        target.insertAdjacentHTML('afterbegin', `
            <div id="torn-property-manager-root" style="${STYLES.container}">
                <div id="alert-summary" style="margin-bottom:8px;"></div>
                <div class="tpm-nav">
                    <button class="tpm-nav-item" id="PropList-toggle">▶️ Properties</button>
                    <div class="tpm-nav-divider"></div>
                    <button class="tpm-nav-item" id="TenantIntel-toggle">▶️ Intel</button>
                    <button class="tpm-nav-item" id="Settings-toggle">▶️ Settings</button>
                    <div class="tpm-nav-divider"></div>
                    <button class="tpm-nav-item" id="help-toggle">▶️ Help</button>
                    <div class="tpm-nav-divider"></div>
                    <button class="tpm-nav-item" id="refreshBtn">🔄 Refresh</button>
                </div>
                <div id="prop-content" style="display:none;"></div>
                <div id="tenantintel-content" style="display:none;"></div>
                <div id="settings-content" style="display:none;"></div>
                <div id="help-content" style="display:none;"></div>
            </div>
        `);
        const propBtn = document.getElementById('PropList-toggle');
        const intelBtn = document.getElementById('TenantIntel-toggle');
        const settingsBtn = document.getElementById('Settings-toggle');
        const helpBtn = document.getElementById('help-toggle');
        const refreshBtn = document.getElementById('refreshBtn');
        const propContent = document.getElementById('prop-content');
        const intelContent = document.getElementById('tenantintel-content');
        const settingsContent = document.getElementById('settings-content');
        const helpContent = document.getElementById('help-content');
        function hideAll() {
            propContent.style.display = 'none';
            intelContent.style.display = 'none';
            settingsContent.style.display = 'none';
            helpContent.style.display = 'none';
        }
        propBtn.onclick = () => {
            if (propContent.style.display === 'block') {
                hideAll();
                propBtn.textContent = '▶️ Properties';
                intelBtn.textContent = '▶️ Intel';
                settingsBtn.textContent = '▶️ Settings';
                helpBtn.textContent = '▶️ Help';
            } else {
                hideAll();
                propContent.style.display = 'block';
                propBtn.textContent = '🔽 Properties';
                intelBtn.textContent = '▶️ Intel';
                settingsBtn.textContent = '▶️ Settings';
                helpBtn.textContent = '▶️ Help';
            }
        };
        intelBtn.onclick = () => {
            if (intelContent.style.display === 'block') {
                hideAll();
                propBtn.textContent = '▶️ Properties';
                intelBtn.textContent = '▶️ Intel';
                settingsBtn.textContent = '▶️ Settings';
                helpBtn.textContent = '▶️ Help';
            } else {
                hideAll();
                intelContent.style.display = 'block';
                renderTenantIntel();
                propBtn.textContent = '▶️ Properties';
                intelBtn.textContent = '🔽 Intel';
                settingsBtn.textContent = '▶️ Settings';
                helpBtn.textContent = '▶️ Help';
            }
        };
        settingsBtn.onclick = () => {
            if (settingsContent.style.display === 'block') {
                hideAll();
                propBtn.textContent = '▶️ Properties';
                intelBtn.textContent = '▶️ Intel';
                settingsBtn.textContent = '▶️ Settings';
                helpBtn.textContent = '▶️ Help';
            } else {
                hideAll();
                settingsContent.style.display = 'block';
                propBtn.textContent = '▶️ Properties';
                intelBtn.textContent = '▶️ Intel';
                settingsBtn.textContent = '🔽 Settings';
                helpBtn.textContent = '▶️ Help';
            }
        };
        helpBtn.onclick = () => {
            if (helpContent.style.display === 'block') {
                hideAll();
                propBtn.textContent = '▶️ Properties';
                intelBtn.textContent = '▶️ Intel';
                settingsBtn.textContent = '▶️ Settings';
                helpBtn.textContent = '▶️ Help';
            } else {
                hideAll();
                helpContent.style.display = 'block';
                renderHelp();
                propBtn.textContent = '▶️ Properties';
                intelBtn.textContent = '▶️ Intel';
                settingsBtn.textContent = '▶️ Settings';
                helpBtn.textContent = '🔽 Help';
            }
        };
        refreshBtn.onclick = async () => {
            const apiKey = localStorage.getItem("tpmApiKey");
            if (!apiKey) return showNotification("No API key set", "error");
            await getAllProperties(apiKey, true);
            loadProperties();
        };
        renderHelp();
        try {
            loadProperties();
        } catch (e) {
            console.error('TPM: Initial property load failed, but continuing initialization:', e);
            if (settingsContent) {
                settingsContent.style.display = 'block';
                settingsBtn.textContent = '🔽 Settings';
                renderSettings('⚠️ Could not load properties. Please check your API key.');
            }
        }
        observeLeaseForms();
        initCollapsibleListeners();
    }
    document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape') {
            const expanded = document.querySelectorAll(
                '.tpm-card.tpm-collapsible .tpm-card-body[style="display: block;"], ' +
                'details.tpm-history-section[open], ' +
                '#prop-content[style="display: block;"], ' +
                '#tenantintel-content[style="display: block;"], ' +
                '#settings-content[style="display: block;"], ' +
                '#help-content[style="display: block;"]'
            );
            if (expanded.length > 0 && !e.target.matches('input, textarea, [contenteditable="true"]')) {
                expanded.forEach(el => {
                    if (el.tagName === 'DETAILS') {
                        el.removeAttribute('open');
                    } else {
                        el.style.display = 'none';
                        if (el.id === 'prop-content') document.getElementById('PropList-toggle').textContent = '▶️ Properties';
                        if (el.id === 'tenantintel-content') document.getElementById('TenantIntel-toggle').textContent = '▶️ Intel';
                        if (el.id === 'settings-content') document.getElementById('Settings-toggle').textContent = '▶️ Settings';
                        if (el.id === 'help-content') document.getElementById('help-toggle').textContent = '▶️ Help';
                    }
                });
                e.preventDefault();
                e.stopPropagation();
            }
        }
    });
})();