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!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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();
            }
        }
    });
})();