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