Torn Faction Command Center

Unified faction dashboard: OC 2.0 management, war target intelligence, chain awareness, member activity — all in one draggable panel with TornPDA support and Discord integration.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Advertisement:

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

Advertisement:

// ==UserScript==
// @name         Torn Faction Command Center
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  Unified faction dashboard: OC 2.0 management, war target intelligence, chain awareness, member activity — all in one draggable panel with TornPDA support and Discord integration.
// @author       DownyJR
// @match        https://www.torn.com/*
// @match        https://torn.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      api.torn.com
// @connect      discord.com
// @connect      discordapp.com
// @run-at       document-end
// @license      MIT
// ==/UserScript==

/*
 * =============================================================================
 * TERMS OF SERVICE
 * =============================================================================
 *
 * By using this script, you agree to the following terms:
 *
 * DATA STORAGE TABLE:
 * ┌─────────────────────┬─────────────────────────────────────────────────────┐
 * │ Data Storage        │ Only locally (browser localStorage)                 │
 * │ Data Sharing        │ Nobody — all data stays on your device              │
 * │ Purpose of Use      │ Competitive advantage — faction coordination &      │
 * │                     │ organized crime planning                            │
 * │ Key Storage         │ Stored locally in your browser only, never shared   │
 * │ Key Access Level    │ Limited — requires faction API access + faction info│
 * └─────────────────────┴─────────────────────────────────────────────────────┘
 *
 * DISCORD INTEGRATION (Opt-In):
 * If you choose to enable Discord webhook notifications, your faction data
 * (member names, OC slots, chain timers) will be sent to the Discord webhook
 * URL you provide. This is entirely optional and disabled by default.
 * Discord Terms of Service: https://discord.com/terms
 *
 * API USAGE:
 * This script only makes requests to api.torn.com using YOUR API key.
 * No data is sent to any other external service except your configured
 * Discord webhook (if enabled).
 *
 * TORN SCRIPT RULES COMPLIANCE:
 * This script operates within Torn City's scripting rules:
 * - Only uses data from api.torn.com or the page you are currently viewing
 * - All page interactions require manual user action
 * - No automated clicking, form submission, or page navigation
 * - API calls may be automated (permitted by Torn rules)
 *
 * =============================================================================
 */

(function () {
    "use strict";

    // ============================ CONFIGURATION ============================
    const CONFIG = {
        version: "1.0.0",
        apiBase: "https://api.torn.com",
        refreshInterval: 60000, // API refresh every 60 seconds
        defaultPosition: { x: 20, y: 100 },
        panelWidth: 380,
        collapsed: false,
    };

    // Settings keys
    const SETTINGS = {
        apiKey: "tfcc_apiKey",
        discordWebhook: "tfcc_discordWebhook",
        panelX: "tfcc_panelX",
        panelY: "tfcc_panelY",
        collapsed: "tfcc_collapsed",
        activeTab: "tfcc_activeTab",
        enableOC: "tfcc_enableOC",
        enableWar: "tfcc_enableWar",
        enableChain: "tfcc_enableChain",
        enableMembers: "tfcc_enableMembers",
        enableDiscord: "tfcc_enableDiscord",
    };

    // ============================ STATE ============================
    let state = {
        apiKey: "",
        discordWebhook: "",
        factionData: null,
        ocData: null,
        chainData: null,
        warData: null,
        membersData: null,
        lastRefresh: null,
        isLoading: false,
        activeTab: "overview",
        collapsed: false,
        panelEl: null,
        isDragging: false,
    };

    // ============================ TORN PDA COMPATIBILITY ============================
    const isTornPDA = typeof PDA !== "undefined";

    function pdaHttpGet(url, callback) {
        if (isTornPDA && typeof PDA_httpGet === "function") {
            PDA_httpGet(url, (response) => {
                try {
                    const data = JSON.parse(response);
                    callback(data);
                } catch (e) {
                    console.error("[TFCC] PDA parse error:", e);
                    callback(null);
                }
            });
        } else {
            // Fallback to fetch
            fetch(url)
                .then((r) => r.json())
                .then((data) => callback(data))
                .catch((err) => {
                    console.error("[TFCC] Fetch error:", err);
                    callback(null);
                });
        }
    }

    // ============================ SETTINGS MANAGEMENT ============================
    function loadSettings() {
        state.apiKey = GM_getValue(SETTINGS.apiKey, "");
        state.discordWebhook = GM_getValue(SETTINGS.discordWebhook, "");
        state.activeTab = GM_getValue(SETTINGS.activeTab, "overview");
        state.collapsed = GM_getValue(SETTINGS.collapsed, false);
    }

    function saveSetting(key, value) {
        GM_setValue(key, value);
    }

    function getApiKey() {
        return state.apiKey;
    }

    // ============================ API HELPERS ============================
    function buildApiUrl(selections = {}) {
        const key = getApiKey();
        if (!key) return null;

        const defaultSelections = {
            profile: "",
            basic: "",
            crimes: "",
            attackers: "",
            faction: "basic,members,crimes,contributors",
        };

        const merged = { ...defaultSelections, ...selections };
        const parts = [];
        for (const [k, v] of Object.entries(merged)) {
            if (v) parts.push(`${k}=${v}`);
        }

        return `${CONFIG.apiBase}/v2/faction/? selections=${encodeURIComponent(merged.faction)}&key=${key}`;
    }

    function fetchFactionData(callback) {
        if (!getApiKey()) {
            callback(null);
            return;
        }

        const url = `${CONFIG.apiBase}/v2/faction/?selections=basic,members,crimes,contributors&key=${getApiKey()}`;

        pdaHttpGet(url, (data) => {
            if (data && data.error) {
                console.error("[TFCC] API Error:", data.error);
                callback(null);
                return;
            }
            state.factionData = data;
            state.lastRefresh = Date.now();
            callback(data);
        });
    }

    function fetchPlayerCrimes(callback) {
        if (!getApiKey()) {
            callback(null);
            return;
        }

        const url = `${CONFIG.apiBase}/v2/user/?selections=crimes&key=${getApiKey()}`;
        pdaHttpGet(url, (data) => {
            callback(data);
        });
    }

    // ============================ PAGE DATA READERS ============================
    // These read data from the CURRENTLY VIEWED page only — compliant with Torn rules
    function readPageChainData() {
        // Read chain data from faction chain page elements if present
        const chainTimer = document.querySelector("[class*='chainTimer'], [class*='chain__timer'], .chain-bar, #chain-hold-bar");
        const chainCount = document.querySelector("[class*='chainCount'], [class*='chain__count'], .chain-counter");
        const chainMax = document.querySelector("[class*='chainMax'], [class*='chain__max']");

        return {
            timer: chainTimer ? chainTimer.textContent.trim() : null,
            count: chainCount ? chainCount.textContent.trim() : null,
            max: chainMax ? chainMax.textContent.trim() : null,
            onPage: !!chainTimer || !!chainCount,
        };
    }

    function readPageWarData() {
        // Read war data from faction war page if present
        const warElements = document.querySelectorAll("[class*='war__'], [class*='faction-war'], .war-list, [class*='enemy__']");
        const hospitalTimers = document.querySelectorAll("[class*='hospital'], [class*='timer__']");

        return {
            onWarPage: warElements.length > 0,
            enemyCount: warElements.length,
            hospitalTimers: Array.from(hospitalTimers).map((el) => ({
                text: el.textContent.trim(),
                target: el.closest("[class*='enemy'], [class*='target'], [class*='row']")?.textContent?.slice(0, 30) || "Unknown",
            })),
        };
    }

    function detectCurrentPage() {
        const path = window.location.pathname;
        const hash = window.location.hash;
        return {
            isFaction: path.includes("/factions.php") || path.includes("/faction"),
            isWar: path.includes("/war") || path.includes("?tab=war") || document.body.textContent.includes("Ranked War"),
            isOC: path.includes("/crimes.php") || path.includes("?tab=crimes") || document.title.includes("Crimes"),
            isChain: path.includes("/chain") || document.body.textContent.includes("Chain") && path.includes("faction"),
            isBazaar: path.includes("/bazaar.php"),
            isProfile: path.includes("/profiles.php") || path.includes("/user"),
            path,
        };
    }

    // ============================ DISCORD WEBHOOK ============================
    function sendDiscordMessage(content, embeds = []) {
        if (!state.discordWebhook || !state.discordWebhook.startsWith("http")) {
            return;
        }

        const payload = {
            username: "Faction Command Center",
            avatar_url: "https://www.torn.com/images/v2/travel_agency/dest_1.jpg",
            content: content,
            embeds: embeds,
        };

        // Use GM_xmlhttpRequest for cross-origin support
        if (typeof GM_xmlhttpRequest !== "undefined") {
            GM_xmlhttpRequest({
                method: "POST",
                url: state.discordWebhook,
                headers: { "Content-Type": "application/json" },
                data: JSON.stringify(payload),
                onload: (response) => {
                    if (response.status >= 200 && response.status < 300) {
                        showNotification("Discord notification sent!", "success");
                    } else {
                        showNotification("Discord send failed", "error");
                    }
                },
                onerror: () => showNotification("Discord connection failed", "error"),
            });
        } else {
            fetch(state.discordWebhook, {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify(payload),
            }).catch(() => showNotification("Discord connection failed", "error"));
        }
    }

    // ============================ NOTIFICATIONS ============================
    function showNotification(message, type = "info") {
        const colors = {
            info: "#3498db",
            success: "#2ecc71",
            error: "#e74c3c",
            warning: "#f39c12",
        };

        const notif = document.createElement("div");
        notif.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: ${colors[type] || colors.info};
            color: white;
            padding: 12px 20px;
            border-radius: 8px;
            font-family: 'Segoe UI', Arial, sans-serif;
            font-size: 13px;
            font-weight: 600;
            z-index: 999999;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            animation: tfcc-slide-in 0.3s ease;
            max-width: 300px;
            word-wrap: break-word;
        `;
        notif.textContent = message;

        const style = document.createElement("style");
        style.textContent = `
            @keyframes tfcc-slide-in {
                from { transform: translateX(400px); opacity: 0; }
                to { transform: translateX(0); opacity: 1; }
            }
            @keyframes tfcc-slide-out {
                from { transform: translateX(0); opacity: 1; }
                to { transform: translateX(400px); opacity: 0; }
            }
        `;
        document.head.appendChild(style);

        document.body.appendChild(notif);
        setTimeout(() => {
            notif.style.animation = "tfcc-slide-out 0.3s ease forwards";
            setTimeout(() => notif.remove(), 300);
        }, 3000);
    }

    // ============================ UI: CSS ============================
    function injectStyles() {
        GM_addStyle(`
            @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');

            #tfcc-panel {
                font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
                position: fixed;
                top: 100px;
                left: 20px;
                width: 400px;
                max-height: 80vh;
                background: linear-gradient(145deg, #1a1d29 0%, #12141d 100%);
                border: 1px solid #2d3142;
                border-radius: 12px;
                box-shadow: 0 8px 32px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.03);
                z-index: 99999;
                overflow: hidden;
                transition: height 0.3s ease, opacity 0.2s ease;
                color: #c9cdd4;
                font-size: 12px;
                line-height: 1.5;
                user-select: none;
            }

            #tfcc-panel.collapsed {
                height: 48px !important;
                max-height: 48px !important;
                overflow: hidden;
            }

            #tfcc-panel.collapsed .tfcc-body,
            #tfcc-panel.collapsed .tfcc-tabs {
                display: none !important;
            }

            .tfcc-header {
                display: flex;
                align-items: center;
                justify-content: space-between;
                padding: 12px 16px;
                background: linear-gradient(90deg, #1e3a5f 0%, #2d5a87 100%);
                border-bottom: 1px solid #3d6fa0;
                cursor: grab;
            }

            .tfcc-header:active {
                cursor: grabbing;
            }

            .tfcc-header-title {
                display: flex;
                align-items: center;
                gap: 10px;
                font-weight: 700;
                font-size: 14px;
                color: #ffffff;
            }

            .tfcc-header-icon {
                width: 24px;
                height: 24px;
                background: linear-gradient(135deg, #4fc3f7 0%, #29b6f6 100%);
                border-radius: 6px;
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 14px;
            }

            .tfcc-header-controls {
                display: flex;
                gap: 6px;
            }

            .tfcc-btn-icon {
                width: 28px;
                height: 28px;
                border-radius: 6px;
                border: none;
                background: rgba(255,255,255,0.1);
                color: #fff;
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 14px;
                transition: all 0.15s ease;
            }

            .tfcc-btn-icon:hover {
                background: rgba(255,255,255,0.2);
            }

            .tfcc-tabs {
                display: flex;
                background: #161822;
                border-bottom: 1px solid #2d3142;
                overflow-x: auto;
                scrollbar-width: none;
            }

            .tfcc-tabs::-webkit-scrollbar {
                display: none;
            }

            .tfcc-tab {
                flex: 1;
                min-width: 70px;
                padding: 10px 8px;
                border: none;
                background: transparent;
                color: #7a8199;
                font-size: 11px;
                font-weight: 600;
                cursor: pointer;
                transition: all 0.15s ease;
                border-bottom: 2px solid transparent;
                white-space: nowrap;
                text-align: center;
            }

            .tfcc-tab:hover {
                color: #b0b8d4;
                background: rgba(255,255,255,0.03);
            }

            .tfcc-tab.active {
                color: #4fc3f7;
                border-bottom-color: #4fc3f7;
                background: rgba(79, 195, 247, 0.05);
            }

            .tfcc-body {
                max-height: calc(80vh - 100px);
                overflow-y: auto;
                scrollbar-width: thin;
                scrollbar-color: #2d3142 transparent;
            }

            .tfcc-body::-webkit-scrollbar {
                width: 6px;
            }

            .tfcc-body::-webkit-scrollbar-track {
                background: transparent;
            }

            .tfcc-body::-webkit-scrollbar-thumb {
                background: #2d3142;
                border-radius: 3px;
            }

            .tfcc-section {
                padding: 14px 16px;
                border-bottom: 1px solid #1e2030;
            }

            .tfcc-section:last-child {
                border-bottom: none;
            }

            .tfcc-section-title {
                font-size: 11px;
                font-weight: 700;
                text-transform: uppercase;
                letter-spacing: 0.8px;
                color: #5c7aea;
                margin-bottom: 10px;
                display: flex;
                align-items: center;
                gap: 6px;
            }

            .tfcc-row {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 6px 0;
                border-bottom: 1px solid #1a1c28;
            }

            .tfcc-row:last-child {
                border-bottom: none;
            }

            .tfcc-label {
                color: #7a8199;
                font-size: 12px;
            }

            .tfcc-value {
                color: #e2e5ec;
                font-weight: 600;
                font-size: 12px;
            }

            .tfcc-value.good { color: #2ecc71; }
            .tfcc-value.warn { color: #f39c12; }
            .tfcc-value.bad { color: #e74c3c; }
            .tfcc-value.info { color: #4fc3f7; }

            .tfcc-btn {
                padding: 8px 14px;
                border-radius: 6px;
                border: none;
                font-size: 12px;
                font-weight: 600;
                cursor: pointer;
                transition: all 0.15s ease;
                font-family: inherit;
            }

            .tfcc-btn-primary {
                background: linear-gradient(135deg, #4fc3f7 0%, #29b6f6 100%);
                color: #0a1628;
            }

            .tfcc-btn-primary:hover {
                transform: translateY(-1px);
                box-shadow: 0 4px 12px rgba(79, 195, 247, 0.3);
            }

            .tfcc-btn-secondary {
                background: #2d3142;
                color: #c9cdd4;
            }

            .tfcc-btn-secondary:hover {
                background: #3d4156;
            }

            .tfcc-btn-danger {
                background: #e74c3c22;
                color: #e74c3c;
                border: 1px solid #e74c3c44;
            }

            .tfcc-btn-danger:hover {
                background: #e74c3c33;
            }

            .tfcc-btn-success {
                background: #2ecc7122;
                color: #2ecc71;
                border: 1px solid #2ecc7144;
            }

            .tfcc-btn-success:hover {
                background: #2ecc7133;
            }

            .tfcc-input {
                width: 100%;
                padding: 8px 12px;
                background: #0f111a;
                border: 1px solid #2d3142;
                border-radius: 6px;
                color: #e2e5ec;
                font-size: 12px;
                font-family: inherit;
                box-sizing: border-box;
                transition: border-color 0.15s ease;
            }

            .tfcc-input:focus {
                outline: none;
                border-color: #4fc3f7;
            }

            .tfcc-input::placeholder {
                color: #4a4f66;
            }

            .tfcc-toggle {
                display: flex;
                align-items: center;
                gap: 10px;
                padding: 4px 0;
            }

            .tfcc-toggle-switch {
                width: 36px;
                height: 20px;
                background: #2d3142;
                border-radius: 10px;
                position: relative;
                cursor: pointer;
                transition: background 0.2s ease;
                flex-shrink: 0;
            }

            .tfcc-toggle-switch.active {
                background: #4fc3f7;
            }

            .tfcc-toggle-switch::after {
                content: '';
                position: absolute;
                width: 16px;
                height: 16px;
                background: white;
                border-radius: 50%;
                top: 2px;
                left: 2px;
                transition: transform 0.2s ease;
            }

            .tfcc-toggle-switch.active::after {
                transform: translateX(16px);
            }

            .tfcc-badge {
                display: inline-flex;
                align-items: center;
                padding: 2px 8px;
                border-radius: 4px;
                font-size: 10px;
                font-weight: 700;
                text-transform: uppercase;
            }

            .tfcc-badge-ready { background: #2ecc7122; color: #2ecc71; }
            .tfcc-badge-pending { background: #f39c1222; color: #f39c12; }
            .tfcc-badge-empty { background: #e74c3c22; color: #e74c3c; }
            .tfcc-badge-completed { background: #7f8c8d22; color: #7f8c8d; }

            .tfcc-member-list {
                max-height: 200px;
                overflow-y: auto;
            }

            .tfcc-member-item {
                display: flex;
                align-items: center;
                gap: 8px;
                padding: 6px 8px;
                border-radius: 6px;
                margin-bottom: 2px;
                transition: background 0.1s ease;
            }

            .tfcc-member-item:hover {
                background: rgba(255,255,255,0.03);
            }

            .tfcc-member-status {
                width: 8px;
                height: 8px;
                border-radius: 50%;
                flex-shrink: 0;
            }

            .tfcc-status-online { background: #2ecc71; box-shadow: 0 0 4px #2ecc7188; }
            .tfcc-status-idle { background: #f39c12; }
            .tfcc-status-offline { background: #7f8c8d; }

            .tfcc-progress-bar {
                width: 100%;
                height: 6px;
                background: #1e2030;
                border-radius: 3px;
                overflow: hidden;
                margin-top: 4px;
            }

            .tfcc-progress-fill {
                height: 100%;
                background: linear-gradient(90deg, #4fc3f7, #29b6f6);
                border-radius: 3px;
                transition: width 0.3s ease;
            }

            .tfcc-empty-state {
                text-align: center;
                padding: 30px 20px;
                color: #4a4f66;
            }

            .tfcc-empty-state-icon {
                font-size: 32px;
                margin-bottom: 10px;
                opacity: 0.5;
            }

            .tfcc-modal-overlay {
                position: fixed;
                inset: 0;
                background: rgba(0,0,0,0.6);
                backdrop-filter: blur(4px);
                z-index: 100000;
                display: flex;
                align-items: center;
                justify-content: center;
            }

            .tfcc-modal {
                background: linear-gradient(145deg, #1a1d29 0%, #12141d 100%);
                border: 1px solid #2d3142;
                border-radius: 12px;
                width: 420px;
                max-width: 90vw;
                max-height: 80vh;
                overflow-y: auto;
                box-shadow: 0 16px 48px rgba(0,0,0,0.5);
            }

            .tfcc-modal-header {
                padding: 16px 20px;
                border-bottom: 1px solid #2d3142;
                font-size: 16px;
                font-weight: 700;
                color: #ffffff;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }

            .tfcc-modal-body {
                padding: 20px;
            }

            .tfcc-modal-footer {
                padding: 16px 20px;
                border-top: 1px solid #2d3142;
                display: flex;
                justify-content: flex-end;
                gap: 10px;
            }

            .tfcc-form-group {
                margin-bottom: 16px;
            }

            .tfcc-form-label {
                display: block;
                font-size: 12px;
                font-weight: 600;
                color: #9aa0b8;
                margin-bottom: 6px;
            }

            .tfcc-form-hint {
                font-size: 11px;
                color: #4a4f66;
                margin-top: 4px;
            }

            .tfcc-oc-card {
                background: #161822;
                border: 1px solid #232636;
                border-radius: 8px;
                padding: 12px;
                margin-bottom: 8px;
            }

            .tfcc-oc-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 8px;
            }

            .tfcc-oc-title {
                font-weight: 700;
                color: #e2e5ec;
                font-size: 13px;
            }

            .tfcc-oc-slots {
                display: grid;
                grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
                gap: 6px;
                margin-top: 8px;
            }

            .tfcc-oc-slot {
                padding: 6px;
                border-radius: 4px;
                text-align: center;
                font-size: 10px;
                font-weight: 600;
            }

            .tfcc-slot-filled { background: #2ecc7111; color: #2ecc71; border: 1px solid #2ecc7133; }
            .tfcc-slot-empty { background: #e74c3c11; color: #e74c3c; border: 1px solid #e74c3c33; }
            .tfcc-slot-player { background: #4fc3f711; color: #4fc3f7; border: 1px solid #4fc3f733; }

            .tfcc-loading {
                display: flex;
                align-items: center;
                justify-content: center;
                padding: 30px;
                gap: 8px;
                color: #4a4f66;
            }

            .tfcc-spinner {
                width: 20px;
                height: 20px;
                border: 2px solid #2d3142;
                border-top-color: #4fc3f7;
                border-radius: 50%;
                animation: tfcc-spin 0.8s linear infinite;
            }

            @keyframes tfcc-spin {
                to { transform: rotate(360deg); }
            }

            .tfcc-footer {
                padding: 10px 16px;
                background: #0f111a;
                border-top: 1px solid #1e2030;
                display: flex;
                justify-content: space-between;
                align-items: center;
                font-size: 10px;
                color: #3a3f55;
            }

            .tfcc-footer a {
                color: #4a4f66;
                text-decoration: none;
            }

            .tfcc-footer a:hover {
                color: #4fc3f7;
            }

            .tfcc-chain-timer {
                font-size: 24px;
                font-weight: 700;
                font-family: 'Courier New', monospace;
                color: #4fc3f7;
                text-align: center;
                padding: 10px;
                background: #0f111a;
                border-radius: 8px;
                letter-spacing: 2px;
            }

            .tfcc-chain-timer.warning {
                color: #f39c12;
                animation: tfcc-pulse 1s ease infinite;
            }

            .tfcc-chain-timer.danger {
                color: #e74c3c;
                animation: tfcc-pulse 0.5s ease infinite;
            }

            @keyframes tfcc-pulse {
                0%, 100% { opacity: 1; }
                50% { opacity: 0.5; }
            }

            .tfcc-refresh-btn {
                animation: none;
            }

            .tfcc-refresh-btn.spinning .tfcc-spinner-icon {
                animation: tfcc-spin 1s linear infinite;
                display: inline-block;
            }
        `);
    }

    // ============================ UI: PANEL ============================
    function createPanel() {
        const panel = document.createElement("div");
        panel.id = "tfcc-panel";
        if (state.collapsed) panel.classList.add("collapsed");

        const savedX = GM_getValue(SETTINGS.panelX, CONFIG.defaultPosition.x);
        const savedY = GM_getValue(SETTINGS.panelY, CONFIG.defaultPosition.y);
        panel.style.left = savedX + "px";
        panel.style.top = savedY + "px";

        // Header
        const header = document.createElement("div");
        header.className = "tfcc-header";
        header.innerHTML = `
            <div class="tfcc-header-title">
                <div class="tfcc-header-icon">⚔</div>
                <span>Faction Command Center</span>
            </div>
            <div class="tfcc-header-controls">
                <button class="tfcc-btn-icon tfcc-refresh-btn" id="tfcc-refresh" title="Refresh Data">↻</button>
                <button class="tfcc-btn-icon" id="tfcc-settings" title="Settings">⚙</button>
                <button class="tfcc-btn-icon" id="tfcc-toggle" title="Collapse/Expand">−</button>
            </div>
        `;

        // Tabs
        const tabs = document.createElement("div");
        tabs.className = "tfcc-tabs";
        tabs.innerHTML = `
            <button class="tfcc-tab ${state.activeTab === "overview" ? "active" : ""}" data-tab="overview">Overview</button>
            <button class="tfcc-tab ${state.activeTab === "oc" ? "active" : ""}" data-tab="oc">OC 2.0</button>
            <button class="tfcc-tab ${state.activeTab === "war" ? "active" : ""}" data-tab="war">War</button>
            <button class="tfcc-tab ${state.activeTab === "chain" ? "active" : ""}" data-tab="chain">Chain</button>
            <button class="tfcc-tab ${state.activeTab === "members" ? "active" : ""}" data-tab="members">Members</button>
        `;

        // Body
        const body = document.createElement("div");
        body.className = "tfcc-body";
        body.id = "tfcc-body";

        // Footer
        const footer = document.createElement("div");
        footer.className = "tfcc-footer";
        footer.innerHTML = `
            <span>v${CONFIG.version}</span>
            <span id="tfcc-last-refresh">Not loaded</span>
        `;

        panel.appendChild(header);
        panel.appendChild(tabs);
        panel.appendChild(body);
        panel.appendChild(footer);

        document.body.appendChild(panel);
        state.panelEl = panel;

        // Event listeners
        setupDrag(header, panel);
        setupTabs(tabs);
        document.getElementById("tfcc-toggle").addEventListener("click", togglePanel);
        document.getElementById("tfcc-settings").addEventListener("click", openSettings);
        document.getElementById("tfcc-refresh").addEventListener("click", manualRefresh);

        renderTab(state.activeTab);
    }

    function setupDrag(handle, panel) {
        let isDragging = false;
        let startX, startY, startLeft, startTop;

        handle.addEventListener("mousedown", (e) => {
            if (e.target.closest(".tfcc-btn-icon")) return;
            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;
            startLeft = panel.offsetLeft;
            startTop = panel.offsetTop;
            panel.style.transition = "none";
        });

        document.addEventListener("mousemove", (e) => {
            if (!isDragging) return;
            const dx = e.clientX - startX;
            const dy = e.clientY - startY;
            panel.style.left = Math.max(0, startLeft + dx) + "px";
            panel.style.top = Math.max(0, startTop + dy) + "px";
        });

        document.addEventListener("mouseup", () => {
            if (!isDragging) return;
            isDragging = false;
            panel.style.transition = "";
            GM_setValue(SETTINGS.panelX, panel.offsetLeft);
            GM_setValue(SETTINGS.panelY, panel.offsetTop);
        });
    }

    function togglePanel() {
        state.collapsed = !state.collapsed;
        state.panelEl.classList.toggle("collapsed", state.collapsed);
        GM_setValue(SETTINGS.collapsed, state.collapsed);
        document.querySelector("#tfcc-toggle").textContent = state.collapsed ? "+" : "−";
    }

    function setupTabs(container) {
        container.addEventListener("click", (e) => {
            if (!e.target.classList.contains("tfcc-tab")) return;
            const tab = e.target.dataset.tab;
            setActiveTab(tab);
        });
    }

    function setActiveTab(tab) {
        state.activeTab = tab;
        GM_setValue(SETTINGS.activeTab, tab);
        document.querySelectorAll(".tfcc-tab").forEach((t) => t.classList.toggle("active", t.dataset.tab === tab));
        renderTab(tab);
    }

    // ============================ RENDER: TABS ============================
    function renderTab(tab) {
        const body = document.getElementById("tfcc-body");
        if (!body) return;

        switch (tab) {
            case "overview":
                renderOverview(body);
                break;
            case "oc":
                renderOC(body);
                break;
            case "war":
                renderWar(body);
                break;
            case "chain":
                renderChain(body);
                break;
            case "members":
                renderMembers(body);
                break;
            default:
                renderOverview(body);
        }
    }

    function renderOverview(container) {
        const faction = state.factionData;
        const pageInfo = detectCurrentPage();
        const chainPage = readPageChainData();

        let html = `
            <div class="tfcc-section">
                <div class="tfcc-section-title">📊 Faction Status</div>
        `;

        if (!faction || !faction.ID) {
            html += `
                <div class="tfcc-empty-state">
                    <div class="tfcc-empty-state-icon">🔑</div>
                    <div>No API key configured</div>
                    <div style="font-size: 11px; margin-top: 8px;">Click ⚙ to add your API key</div>
                </div>
            `;
        } else {
            html += `
                <div class="tfcc-row">
                    <span class="tfcc-label">Faction</span>
                    <span class="tfcc-value">${faction.name || "Unknown"}</span>
                </div>
                <div class="tfcc-row">
                    <span class="tfcc-label">ID</span>
                    <span class="tfcc-value">${faction.ID}</span>
                </div>
                <div class="tfcc-row">
                    <span class="tfcc-label">Members</span>
                    <span class="tfcc-value info">${faction.members ? Object.keys(faction.members).length : "?"}</span>
                </div>
                <div class="tfcc-row">
                    <span class="tfcc-label">Respect</span>
                    <span class="tfcc-value">${faction.respect?.toLocaleString() || "?"}</span>
                </div>
            `;

            if (faction.rank) {
                html += `
                    <div class="tfcc-row">
                        <span class="tfcc-label">Rank</span>
                        <span class="tfcc-value">${faction.rank.name || faction.rank} (${faction.rank.level || "?"})</span>
                    </div>
                `;
            }
        }

        html += `</div>`;

        // Quick stats from page
        html += `
            <div class="tfcc-section">
                <div class="tfcc-section-title">📍 Current Page</div>
                <div class="tfcc-row">
                    <span class="tfcc-label">Page Type</span>
                    <span class="tfcc-value">${getPageTypeLabel(pageInfo)}</span>
                </div>
        `;

        if (chainPage.onPage) {
            html += `
                <div class="tfcc-row">
                    <span class="tfcc-label">Chain Status</span>
                    <span class="tfcc-value ${getChainStatusClass(chainPage)}">${chainPage.count || "Active"}</span>
                </div>
            `;
        }

        html += `</div>`;

        // Quick Actions
        html += `
            <div class="tfcc-section">
                <div class="tfcc-section-title">⚡ Quick Actions</div>
                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
                    <button class="tfcc-btn tfcc-btn-primary" id="tfcc-goto-faction">Faction Page</button>
                    <button class="tfcc-btn tfcc-btn-secondary" id="tfcc-goto-oc">OC Crimes</button>
                    <button class="tfcc-btn tfcc-btn-secondary" id="tfcc-goto-war">War Room</button>
                    <button class="tfcc-btn tfcc-btn-secondary" id="tfcc-goto-chain">Chain</button>
                </div>
            </div>
        `;

        // Discord quick-send
        if (state.discordWebhook) {
            html += `
                <div class="tfcc-section">
                    <div class="tfcc-section-title">🔔 Discord</div>
                    <button class="tfcc-btn tfcc-btn-success" id="tfcc-discord-ping" style="width: 100%;">Send Faction Status to Discord</button>
                </div>
            `;
        }

        container.innerHTML = html;

        // Bind quick action buttons
        document.getElementById("tfcc-goto-faction")?.addEventListener("click", () => {
            window.location.href = "https://www.torn.com/factions.php?step=your";
        });
        document.getElementById("tfcc-goto-oc")?.addEventListener("click", () => {
            window.location.href = "https://www.torn.com/factions.php?step=your#/tab=crimes";
        });
        document.getElementById("tfcc-goto-war")?.addEventListener("click", () => {
            window.location.href = "https://www.torn.com/factions.php?step=your#/tab=war";
        });
        document.getElementById("tfcc-goto-chain")?.addEventListener("click", () => {
            window.location.href = "https://www.torn.com/factions.php?step=your#/tab=chains";
        });
        document.getElementById("tfcc-discord-ping")?.addEventListener("click", sendFactionStatusToDiscord);
    }

    function renderOC(container) {
        const faction = state.factionData;

        let html = `
            <div class="tfcc-section">
                <div class="tfcc-section-title">🎯 Organized Crimes 2.0</div>
        `;

        if (!faction || !faction.crimes) {
            html += `
                <div class="tfcc-empty-state">
                    <div class="tfcc-empty-state-icon">📋</div>
                    <div>No OC data available</div>
                    <div style="font-size: 11px; margin-top: 8px;">Configure API key and refresh</div>
                </div>
            `;
        } else {
            const crimes = faction.crimes;
            const crimeEntries = Object.entries(crimes).filter(([k]) => !isNaN(k));

            if (crimeEntries.length === 0) {
                html += `<div class="tfcc-empty-state">No active organized crimes found</div>`;
            } else {
                crimeEntries.forEach(([id, crime]) => {
                    const slots = crime.slots || [];
                    const filledSlots = slots.filter((s) => s.user_id).length;
                    const totalSlots = slots.length;
                    const isReady = filledSlots === totalSlots && crime.ready_at && crime.ready_at <= Math.floor(Date.now() / 1000);
                    const isCompleted = crime.initiated_at && crime.completed_at;
                    const isPlanning = crime.ready_at && crime.ready_at > Math.floor(Date.now() / 1000);

                    let badgeClass = "tfcc-badge-pending";
                    let badgeText = "PLANNING";
                    if (isCompleted) {
                        badgeClass = "tfcc-badge-completed";
                        badgeText = "DONE";
                    } else if (isReady) {
                        badgeClass = "tfcc-badge-ready";
                        badgeText = "READY";
                    } else if (filledSlots < totalSlots) {
                        badgeClass = "tfcc-badge-empty";
                        badgeText = `${filledSlots}/${totalSlots}`;
                    }

                    html += `
                        <div class="tfcc-oc-card">
                            <div class="tfcc-oc-header">
                                <span class="tfcc-oc-title">${crime.name || `Crime #${id}`}</span>
                                <span class="tfcc-badge ${badgeClass}">${badgeText}</span>
                            </div>
                            <div style="font-size: 11px; color: #7a8199;">
                                ${crime.difficulty ? `Difficulty: ${crime.difficulty}` : ""}
                                ${crime.ready_at ? `| Ready: ${formatTimestamp(crime.ready_at)}` : ""}
                            </div>
                            <div class="tfcc-oc-slots">
                    `;

                    if (slots.length > 0) {
                        slots.forEach((slot, idx) => {
                            if (slot.user_id) {
                                const isPlayer = slot.user_id === faction.player_id;
                                html += `<div class="tfcc-oc-slot ${isPlayer ? "tfcc-slot-player" : "tfcc-slot-filled"}">${slot.user_name || "Member"}</div>`;
                            } else {
                                html += `<div class="tfcc-oc-slot tfcc-slot-empty">Slot ${idx + 1}</div>`;
                            }
                        });
                    }

                    html += `
                            </div>
                        </div>
                    `;
                });
            }
        }

        html += `</div>`;

        // CPR Reference
        html += `
            <div class="tfcc-section">
                <div class="tfcc-section-title">📚 CPR Quick Reference</div>
                <div style="font-size: 11px; color: #7a8199; line-height: 1.8;">
                    <div><strong style="color: #e2e5ec;">Political Assassination:</strong> 100+ each stat</div>
                    <div><strong style="color: #e2e5ec;">Plane Hijacking:</strong> 80+ each stat</div>
                    <div><strong style="color: #e2e5ec;">Train Robbery:</strong> 60+ each stat</div>
                    <div><strong style="color: #e2e5ec;">Bank Robbery:</strong> 50+ each stat</div>
                    <div><strong style="color: #e2e5ec;">Armored Truck:</strong> 40+ each stat</div>
                    <div><strong style="color: #e2e5ec;">Kidnapping:</strong> 30+ each stat</div>
                    <div style="margin-top: 6px; color: #4a4f66; font-size: 10px;">
                        Note: CPR requirements may vary. Visit tornprobability.com for exact values.
                    </div>
                </div>
            </div>
        `;

        // Manual Discord notify
        if (state.discordWebhook) {
            html += `
                <div class="tfcc-section">
                    <button class="tfcc-btn tfcc-btn-success" id="tfcc-discord-oc" style="width: 100%;">Send OC Status to Discord</button>
                </div>
            `;
        }

        container.innerHTML = html;
        document.getElementById("tfcc-discord-oc")?.addEventListener("click", sendOCStatusToDiscord);
    }

    function renderWar(container) {
        const pageData = readPageWarData();
        const faction = state.factionData;

        let html = `
            <div class="tfcc-section">
                <div class="tfcc-section-title">⚔ War Intelligence</div>
        `;

        html += `
            <div class="tfcc-row">
                <span class="tfcc-label">On War Page</span>
                <span class="tfcc-value ${pageData.onWarPage ? "good" : ""}">${pageData.onWarPage ? "YES" : "NO"}</span>
            </div>
        `;

        if (pageData.onWarPage) {
            html += `
                <div class="tfcc-row">
                    <span class="tfcc-label">Visible Enemies</span>
                    <span class="tfcc-value warn">${pageData.enemyCount}</span>
                </div>
            `;

            if (pageData.hospitalTimers.length > 0) {
                html += `<div class="tfcc-section-title" style="margin-top: 12px;">🏥 Hospital Timers</div>`;
                html += `<div style="max-height: 150px; overflow-y: auto;">`;
                pageData.hospitalTimers.forEach((timer) => {
                    html += `
                        <div class="tfcc-row">
                            <span class="tfcc-label" style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;">${timer.target}</span>
                            <span class="tfcc-value info">${timer.text}</span>
                        </div>
                    `;
                });
                html += `</div>`;
            }
        } else {
            html += `
                <div class="tfcc-empty-state">
                    <div class="tfcc-empty-state-icon">🛡</div>
                    <div>Navigate to the war page to see live intelligence</div>
                </div>
            `;
        }

        html += `</div>`;

        // Ranked War Info
        if (faction && faction.ranked_wars) {
            html += `
                <div class="tfcc-section">
                    <div class="tfcc-section-title">🏆 Ranked Wars</div>
            `;
            const rwEntries = Object.entries(faction.ranked_wars);
            if (rwEntries.length === 0) {
                html += `<div style="color: #4a4f66; font-size: 11px;">No active ranked wars</div>`;
            } else {
                rwEntries.forEach(([id, rw]) => {
                    html += `
                        <div class="tfcc-oc-card">
                            <div class="tfcc-oc-title">War #${id}</div>
                            <div style="font-size: 11px; color: #7a8199; margin-top: 4px;">
                                Start: ${formatTimestamp(rw.started)} | End: ${formatTimestamp(rw.ends)}
                            </div>
                            <div style="margin-top: 6px;">
                                <div class="tfcc-progress-bar">
                                    <div class="tfcc-progress-fill" style="width: ${getWarProgress(rw)}%"></div>
                                </div>
                            </div>
                        </div>
                    `;
                });
            }
            html += `</div>`;
        }

        // Manual Discord notify
        if (state.discordWebhook && pageData.onWarPage) {
            html += `
                <div class="tfcc-section">
                    <button class="tfcc-btn tfcc-btn-success" id="tfcc-discord-war" style="width: 100%;">Send War Intel to Discord</button>
                </div>
            `;
        }

        container.innerHTML = html;
        document.getElementById("tfcc-discord-war")?.addEventListener("click", sendWarStatusToDiscord);
    }

    function renderChain(container) {
        const chainPage = readPageChainData();
        const faction = state.factionData;

        let html = `
            <div class="tfcc-section">
                <div class="tfcc-section-title">⛓ Chain Monitor</div>
        `;

        if (chainPage.onPage) {
            const isWarning = chainPage.timer && parseTime(chainPage.timer) < 30 && parseTime(chainPage.timer) > 0;
            const isDanger = chainPage.timer && parseTime(chainPage.timer) < 10;

            html += `
                <div class="tfcc-chain-timer ${isDanger ? "danger" : isWarning ? "warning" : ""}">
                    ${chainPage.timer || "--:--"}
                </div>
                <div style="text-align: center; margin-top: 8px; font-size: 13px;">
                    <span class="tfcc-value ${isWarning ? "warn" : "good"}">Chain: ${chainPage.count || "?"}${chainPage.max ? ` / ${chainPage.max}` : ""}</span>
                </div>
            `;

            if (isWarning) {
                html += `
                    <div style="background: #f39c1222; border: 1px solid #f39c1244; border-radius: 6px; padding: 8px; margin-top: 10px; text-align: center; font-size: 11px; color: #f39c12; font-weight: 600;">
                        ⚠ Chain timer running low!
                    </div>
                `;
            }

            if (isDanger) {
                html += `
                    <div style="background: #e74c3c22; border: 1px solid #e74c3c44; border-radius: 6px; padding: 8px; margin-top: 8px; text-align: center; font-size: 11px; color: #e74c3c; font-weight: 700;">
                        🚨 CRITICAL — Chain about to break!
                    </div>
                `;
            }
        } else {
            html += `
                <div class="tfcc-empty-state">
                    <div class="tfcc-empty-state-icon">⛓</div>
                    <div>Navigate to the chain page to see live data</div>
                </div>
            `;
        }

        html += `</div>`;

        // Chain history from API
        if (faction && faction.chain) {
            html += `
                <div class="tfcc-section">
                    <div class="tfcc-section-title">📈 Chain History</div>
                    <div class="tfcc-row">
                        <span class="tfcc-label">Current</span>
                        <span class="tfcc-value">${faction.chain.current || 0}</span>
                    </div>
                    <div class="tfcc-row">
                        <span class="tfcc-label">Maximum</span>
                        <span class="tfcc-value">${faction.chain.max || 0}</span>
                    </div>
                    <div class="tfcc-row">
                        <span class="tfcc-label">Cooldown</span>
                        <span class="tfcc-value">${faction.chain.cooldown || 0}s</span>
                    </div>
                </div>
            `;
        }

        // Discord notify
        if (state.discordWebhook && chainPage.onPage) {
            html += `
                <div class="tfcc-section">
                    <button class="tfcc-btn tfcc-btn-success" id="tfcc-discord-chain" style="width: 100%;">Send Chain Alert to Discord</button>
                </div>
            `;
        }

        container.innerHTML = html;
        document.getElementById("tfcc-discord-chain")?.addEventListener("click", sendChainAlertToDiscord);
    }

    function renderMembers(container) {
        const faction = state.factionData;

        let html = `
            <div class="tfcc-section">
                <div class="tfcc-section-title">👥 Faction Members</div>
        `;

        if (!faction || !faction.members) {
            html += `
                <div class="tfcc-empty-state">
                    <div class="tfcc-empty-state-icon">👤</div>
                    <div>No member data available</div>
                </div>
            `;
        } else {
            const members = Object.entries(faction.members);
            html += `<div style="font-size: 11px; color: #7a8199; margin-bottom: 10px;">${members.length} members</div>`;
            html += `<div class="tfcc-member-list">`;

            // Sort by level descending
            members.sort((a, b) => (b[1].level || 0) - (a[1].level || 0));

            members.forEach(([id, member]) => {
                let statusClass = "tfcc-status-offline";
                if (member.last_action) {
                    if (member.last_action.status === "Online") statusClass = "tfcc-status-online";
                    else if (member.last_action.status === "Idle") statusClass = "tfcc-status-idle";
                }

                html += `
                    <div class="tfcc-member-item">
                        <div class="tfcc-member-status ${statusClass}"></div>
                        <div style="flex: 1; min-width: 0;">
                            <div style="font-weight: 600; color: #e2e5ec; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
                                ${member.name || "Unknown"}
                            </div>
                            <div style="font-size: 10px; color: #4a4f66;">
                                Lvl ${member.level || "?"} | ${member.last_action?.relative || "Unknown status"}
                            </div>
                        </div>
                        <div style="text-align: right; flex-shrink: 0;">
                            <div style="font-size: 10px; color: #7a8199;">${formatNumber(member.stats?.strength)} STR</div>
                            <div style="font-size: 10px; color: #7a8199;">${formatNumber(member.stats?.speed)} SPD</div>
                        </div>
                    </div>
                `;
            });

            html += `</div>`;
        }

        html += `</div>`;

        container.innerHTML = html;
    }

    // ============================ SETTINGS MODAL ============================
    function openSettings() {
        const existing = document.getElementById("tfcc-settings-modal");
        if (existing) existing.remove();

        const overlay = document.createElement("div");
        overlay.className = "tfcc-modal-overlay";
        overlay.id = "tfcc-settings-modal";

        overlay.innerHTML = `
            <div class="tfcc-modal">
                <div class="tfcc-modal-header">
                    <span>⚙ Settings</span>
                    <button class="tfcc-btn-icon" id="tfcc-close-settings" style="color: #7a8199;">✕</button>
                </div>
                <div class="tfcc-modal-body">
                    <div class="tfcc-form-group">
                        <label class="tfcc-form-label">Torn API Key</label>
                        <input type="password" class="tfcc-input" id="tfcc-api-key" placeholder="Paste your Limited Access API key" value="${state.apiKey}">
                        <div class="tfcc-form-hint">Requires Limited access with 'faction' permissions. Key is stored only in your browser.</div>
                    </div>

                    <div class="tfcc-form-group">
                        <label class="tfcc-form-label">Discord Webhook URL (Optional)</label>
                        <input type="password" class="tfcc-input" id="tfcc-discord-url" placeholder="https://discord.com/api/webhooks/..." value="${state.discordWebhook}">
                        <div class="tfcc-form-hint">For sending faction alerts to Discord. Leave empty to disable. <a href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks" target="_blank" style="color: #4fc3f7;">How to create a webhook</a></div>
                    </div>

                    <div style="border-top: 1px solid #2d3142; margin: 16px 0; padding-top: 16px;">
                        <div class="tfcc-form-label" style="margin-bottom: 12px;">Feature Toggles</div>

                        <div class="tfcc-toggle">
                            <div class="tfcc-toggle-switch ${GM_getValue(SETTINGS.enableOC, true) ? "active" : ""}" data-setting="${SETTINGS.enableOC}"></div>
                            <span>OC 2.0 Management</span>
                        </div>
                        <div class="tfcc-toggle">
                            <div class="tfcc-toggle-switch ${GM_getValue(SETTINGS.enableWar, true) ? "active" : ""}" data-setting="${SETTINGS.enableWar}"></div>
                            <span>War Intelligence</span>
                        </div>
                        <div class="tfcc-toggle">
                            <div class="tfcc-toggle-switch ${GM_getValue(SETTINGS.enableChain, true) ? "active" : ""}" data-setting="${SETTINGS.enableChain}"></div>
                            <span>Chain Monitor</span>
                        </div>
                        <div class="tfcc-toggle">
                            <div class="tfcc-toggle-switch ${GM_getValue(SETTINGS.enableMembers, true) ? "active" : ""}" data-setting="${SETTINGS.enableMembers}"></div>
                            <span>Member Activity</span>
                        </div>
                        <div class="tfcc-toggle">
                            <div class="tfcc-toggle-switch ${GM_getValue(SETTINGS.enableDiscord, true) ? "active" : ""}" data-setting="${SETTINGS.enableDiscord}"></div>
                            <span>Discord Integration</span>
                        </div>
                    </div>

                    <div style="border-top: 1px solid #2d3142; margin-top: 16px; padding-top: 16px;">
                        <div class="tfcc-form-label">Data & Privacy</div>
                        <div style="font-size: 11px; color: #7a8199; line-height: 1.8; margin-top: 8px;">
                            <div>📦 <strong style="color: #e2e5ec;">Data Storage:</strong> Only locally in your browser</div>
                            <div>🔒 <strong style="color: #e2e5ec;">Data Sharing:</strong> Nobody — stays on your device</div>
                            <div>🎯 <strong style="color: #e2e5ec;">Purpose:</strong> Faction coordination & OC planning</div>
                            <div>🔑 <strong style="color: #e2e5ec;">API Key:</strong> Stored locally, never shared</div>
                            <div>📡 <strong style="color: #e2e5ec;">Discord:</strong> Only sends when you click a button</div>
                        </div>
                    </div>
                </div>
                <div class="tfcc-modal-footer">
                    <button class="tfcc-btn tfcc-btn-secondary" id="tfcc-cancel-settings">Cancel</button>
                    <button class="tfcc-btn tfcc-btn-primary" id="tfcc-save-settings">Save Settings</button>
                </div>
            </div>
        `;

        document.body.appendChild(overlay);

        // Toggle switches
        overlay.querySelectorAll(".tfcc-toggle-switch").forEach((sw) => {
            sw.addEventListener("click", () => {
                sw.classList.toggle("active");
            });
        });

        document.getElementById("tfcc-close-settings").addEventListener("click", () => overlay.remove());
        document.getElementById("tfcc-cancel-settings").addEventListener("click", () => overlay.remove());
        document.getElementById("tfcc-save-settings").addEventListener("click", () => {
            const newKey = document.getElementById("tfcc-api-key").value.trim();
            const newWebhook = document.getElementById("tfcc-discord-url").value.trim();

            // Save API key
            if (newKey) {
                GM_setValue(SETTINGS.apiKey, newKey);
                state.apiKey = newKey;
            }

            // Save webhook
            GM_setValue(SETTINGS.discordWebhook, newWebhook);
            state.discordWebhook = newWebhook;

            // Save toggles
            overlay.querySelectorAll(".tfcc-toggle-switch").forEach((sw) => {
                const settingKey = sw.dataset.setting;
                GM_setValue(settingKey, sw.classList.contains("active"));
            });

            overlay.remove();
            showNotification("Settings saved! Refreshing data...", "success");
            refreshAllData();
        });
    }

    // ============================ DISCORD SENDERS ============================
    function sendFactionStatusToDiscord() {
        const faction = state.factionData;
        if (!faction) {
            showNotification("No faction data to send", "error");
            return;
        }

        const memberCount = faction.members ? Object.keys(faction.members).length : "?";
        const embed = {
            title: `⚔️ ${faction.name || "Faction"} Status`,
            color: 0x4fc3f7,
            fields: [
                { name: "Members", value: String(memberCount), inline: true },
                { name: "Respect", value: faction.respect?.toLocaleString() || "?", inline: true },
                { name: "Rank", value: faction.rank?.name || "?", inline: true },
            ],
            timestamp: new Date().toISOString(),
        };

        sendDiscordMessage("📊 Faction status update:", [embed]);
    }

    function sendOCStatusToDiscord() {
        const faction = state.factionData;
        if (!faction || !faction.crimes) {
            showNotification("No OC data to send", "error");
            return;
        }

        const crimes = Object.entries(faction.crimes).filter(([k]) => !isNaN(k));
        const readyCrimes = crimes.filter(([_, c]) => {
            const slots = c.slots || [];
            const filled = slots.filter((s) => s.user_id).length;
            return filled === slots.length && c.ready_at && c.ready_at <= Math.floor(Date.now() / 1000);
        });

        const embed = {
            title: "🎯 OC 2.0 Status",
            color: readyCrimes.length > 0 ? 0x2ecc71 : 0xf39c12,
            fields: [
                { name: "Active OCs", value: String(crimes.length), inline: true },
                { name: "Ready to Start", value: String(readyCrimes.length), inline: true },
            ],
            timestamp: new Date().toISOString(),
        };

        sendDiscordMessage("🎯 OC status update:", [embed]);
    }

    function sendWarStatusToDiscord() {
        const pageData = readPageWarData();
        const embed = {
            title: "⚔️ War Intelligence",
            color: 0xe74c3c,
            fields: [
                { name: "Enemies Visible", value: String(pageData.enemyCount), inline: true },
                { name: "Page", value: window.location.pathname, inline: true },
            ],
            timestamp: new Date().toISOString(),
        };

        sendDiscordMessage("⚔️ War intel update:", [embed]);
    }

    function sendChainAlertToDiscord() {
        const chainPage = readPageChainData();
        const isWarning = chainPage.timer && parseTime(chainPage.timer) < 30;

        const embed = {
            title: isWarning ? "🚨 CHAIN ALERT" : "⛓ Chain Status",
            color: isWarning ? 0xe74c3c : 0x4fc3f7,
            fields: [
                { name: "Timer", value: chainPage.timer || "?", inline: true },
                { name: "Count", value: chainPage.count || "?", inline: true },
            ],
            timestamp: new Date().toISOString(),
        };

        sendDiscordMessage(isWarning ? "🚨 **CHAIN TIMER LOW!**" : "⛓ Chain update:", [embed]);
    }

    // ============================ DATA REFRESH ============================
    function refreshAllData() {
        if (state.isLoading) return;
        state.isLoading = true;

        const refreshBtn = document.getElementById("tfcc-refresh");
        if (refreshBtn) refreshBtn.classList.add("spinning");

        fetchFactionData(() => {
            state.isLoading = false;
            if (refreshBtn) refreshBtn.classList.remove("spinning");

            const now = new Date();
            const timeStr = now.toLocaleTimeString();
            const lastRefreshEl = document.getElementById("tfcc-last-refresh");
            if (lastRefreshEl) lastRefreshEl.textContent = "Updated: " + timeStr;

            renderTab(state.activeTab);
        });
    }

    function manualRefresh() {
        refreshAllData();
        showNotification("Refreshing faction data...", "info");
    }

    function startAutoRefresh() {
        refreshAllData();
        setInterval(() => {
            if (!state.collapsed && getApiKey()) {
                refreshAllData();
            }
        }, CONFIG.refreshInterval);
    }

    // ============================ HELPERS ============================
    function formatTimestamp(ts) {
        if (!ts) return "?";
        const date = new Date(ts * 1000);
        const now = Date.now();
        const diff = date - now;

        if (diff < 0) return "Ready now";

        const hours = Math.floor(diff / 3600000);
        const mins = Math.floor((diff % 3600000) / 60000);

        if (hours > 0) return `${hours}h ${mins}m`;
        return `${mins}m`;
    }

    function parseTime(timeStr) {
        if (!timeStr) return Infinity;
        const parts = timeStr.split(":").map(Number);
        if (parts.length === 2) return parts[0] * 60 + parts[1];
        if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2];
        return Infinity;
    }

    function getPageTypeLabel(info) {
        if (info.isFaction) return "Faction";
        if (info.isWar) return "War";
        if (info.isOC) return "Crimes";
        if (info.isChain) return "Chain";
        if (info.isBazaar) return "Bazaar";
        if (info.isProfile) return "Profile";
        return "Other";
    }

    function getChainStatusClass(chainPage) {
        if (!chainPage.count) return "";
        const count = parseInt(chainPage.count);
        if (count >= 100) return "good";
        if (count >= 10) return "warn";
        return "bad";
    }

    function getWarProgress(rw) {
        if (!rw.started || !rw.ends) return 0;
        const total = rw.ends - rw.started;
        const elapsed = Math.floor(Date.now() / 1000) - rw.started;
        return Math.min(100, Math.max(0, (elapsed / total) * 100));
    }

    function formatNumber(num) {
        if (!num) return "?";
        if (num >= 1000000000) return (num / 1000000000).toFixed(1) + "B";
        if (num >= 1000000) return (num / 1000000).toFixed(1) + "M";
        if (num >= 1000) return (num / 1000).toFixed(1) + "K";
        return String(num);
    }

    // ============================ INITIALIZATION ============================
    function init() {
        loadSettings();
        injectStyles();
        createPanel();

        if (getApiKey()) {
            startAutoRefresh();
        }

        // Register Tampermonkey menu commands
        if (typeof GM_registerMenuCommand !== "undefined") {
            GM_registerMenuCommand("⚙ TFCC Settings", openSettings);
            GM_registerMenuCommand("↻ Refresh Data", manualRefresh);
        }

        console.log(`[TFCC] Faction Command Center v${CONFIG.version} loaded${isTornPDA ? " (TornPDA mode)" : ""}`);
    }

    // Wait for page to be ready
    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", init);
    } else {
        init();
    }
})();