SheepieAIO SPVersion

Sedition AIO script

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         SheepieAIO SPVersion
// @author       SheepPrincess [2679129]
// @namespace    SneakyBeakySheepieChaining Cell
// @version      SP-v2.3.3
// @description  Sedition AIO script
// @license      MIT
// @match        https://www.torn.com/*
// @grant        unsafeWindow
// @match        https://www.torn.com/factions.php?step=your*
// @icon         https://sheepie.ca/img/SheepieWorld64.png
// @connect      tornprobability.com
// @grant        GM_info
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @connect      script.google.com
// @grant        GM_registerMenuCommand
// @connect      script.googleusercontent.com
// @connect      api.torn.com
// @require      https://www.torn.com/js/script/lib/jquery-1.8.2.js
// @connect      https://sheepie.ca/
// @grant        GM_xmlhttpRequest
// @connect      sheepie.ca
// @connect      sheepie.ca*
// @connect      https://sheepie.ca/*
// ==/UserScript==
// Forked and borrowed work from

//-Chain Timer Enhancer
//authors Omanpx [1906686] & Flav [2499383]

//- EHeasley's Egg Navigator 1.5.2
//Traverse every page in Torn in search for eggs
//author Heasleys4hemp [1468764]

//- Fast Slots 0.3
//Makes slots stop instantly. Works for every spin except first.
//author Silmaril [2665762]

//- Crime Morale 1.4.13
//Scamming helper
//author tobytorn [1617955]

//- OC Role Display - Ravage Edition 3.1.2
//Color Coding OC positions, Weight % added
//author NotIbbyz (Darrellia [2802767]) & Allenone [2033011]
//original work

//- Torn Pickpocketing Colors 0.4
//Color codes crimes based on difficulty
//author Korbrm [2931507]

//- Torn Market Filler 0.6.3
//On "Fill" click autofills market item price with lowest market price minus $1 (customizable), fills max quantity, marks checkboxes for guns.
//author Silmaril [2665762]

//- Torn: Refill Blood Bag Reminder 4.3.0
//Show blood-bag icon when ready to fill blood bags. Configurable 1-3 bags with dynamic life/cooldown thresholds.
//author ButtChew [3840391]

//- Torn: Attack Better 1.7.1
//(toggle) Move Torn "Start Fight" button on top of your weapon of choice and remove certain elements to help with load times.
//author smokey_ [2492729]

//- Torn: Fast Packs 0.4
//Removes animations that play when opening supply packs
//author Hemicopter [2780600]

//- Don't forget your Xanax 2024-06-26
//Reminds you to take your xanax when you have no drug cooldown
//author Shade

(function SheepieAIO() {
    'use strict';
    // ===== Sheepie localStorage Performance Proxy =====
    const originalGetItem = localStorage.getItem.bind(localStorage);
    const originalSetItem = localStorage.setItem.bind(localStorage);

    const SheepieStorageCache = {};

    localStorage.getItem = function(key) {
        if (key in SheepieStorageCache) {
            return SheepieStorageCache[key];
        }
        const value = originalGetItem(key);
        SheepieStorageCache[key] = value;
        return value;
    };

    localStorage.setItem = function(key, value) {
        SheepieStorageCache[key] = value;
        return originalSetItem(key, value);
    };

    // ===== Sheepie Smart Interval Controller =====
    const SheepieIntervals = [];

    function sheepieSetInterval(fn, delay) {
        const wrapped = function() {
            if (document.hidden) return; // pause when tab not visible
            fn();
        };

        const id = setInterval(wrapped, delay);
        SheepieIntervals.push(id);
        return id;
    }



    function isTornPDA() {
        return /TornPDA/i.test(navigator.userAgent);
    }
    if (isTornPDA()) {
        let sheeptall = 20;
        localStorage.setItem("sheeptall", sheeptall);
        let sheepwide = 50;
        localStorage.setItem("sheepwide", sheepwide);
        let sheeptall2 = 20;
        localStorage.setItem("sheeptall2", sheeptall2);
        let sheepwide2 = 30;
        localStorage.setItem("sheepwide2", sheepwide2);
    } else {
        let sheeptall = 30;
        localStorage.setItem("sheeptall", sheeptall);
        let sheepwide = 90;
        localStorage.setItem("sheepwide", sheepwide);
        let sheeptall2 = 20;
        localStorage.setItem("sheeptall2", sheeptall2);
        let sheepwide2 = 40;
        localStorage.setItem("sheepwide2", sheepwide2);}
    let sheepwide = Number(localStorage.getItem("sheepwide")) || 120;
    let sheeptall = Number(localStorage.getItem("sheeptall")) || 50;
    let sheepwide2 = Number(localStorage.getItem("sheepwide2")) || 80;
    let sheeptall2 = Number(localStorage.getItem("sheeptall2")) || 30;

    (function() {
        'use strict';

        function saveSidebarUsername() {
            const sidebar = document.querySelector("#sidebarroot");
            if (!sidebar) return setTimeout(saveSidebarUsername, 500); // retry in 500ms

            // Find the username link (first <a> in sidebar that is not Messages/Logout)
            const usernameLink = Array.from(sidebar.querySelectorAll("a"))
            .find(a => a.textContent.trim().length > 0 &&
                  !a.href.includes("messages") &&
                  !a.href.includes("logout"));

            if (usernameLink) {
                const username = usernameLink.textContent.trim();

                // Save to localStorage only if it hasn't been saved yet or changed
                if (localStorage.getItem("username") !== username) {
                    localStorage.setItem("username", username);
                }
            } else {
                // Retry in 500ms if username not found yet
                setTimeout(saveSidebarUsername, 500);
            }

        }

        saveSidebarUsername();


    })();

    const username = localStorage.getItem("username");
    if (username === "SheepPrincess" || username === "Baws") {
        if (localStorage.getItem("sheepcolour") === null) {
            localStorage.setItem("sheepcolour", "#FFB5C0");}
    } else if (username === "Extrovert") {
        if (localStorage.getItem("sheepcolour") === null) {
            localStorage.setItem("sheepcolour", "red");}
    } else {
        if (localStorage.getItem("sheepcolour") === null) {
            localStorage.setItem("sheepcolour", "#734180");}
        const sheepColour = localStorage.getItem("sheepcolour");
    }
    let sheepColour = localStorage.getItem("sheepcolour");

    requestIdleCallback(() => {
        (function () {
            'use strict';

            const TICKER_ID = "sheepie-news-ticker";
            const TICKER_URL = "https://sheepie.ca/code.html";
            const REFRESH_INTERVAL = 5 * 60 * 1000;
            const NEWS_CHECK_INTERVAL = 5 * 60 * 1000;

            // 🚫 Prevent duplicate tickers
            if (document.getElementById(TICKER_ID)) {
                return;
            }

            // Create ticker container
            const ticker = document.createElement("div");
            ticker.id = TICKER_ID; // 👈 important
            ticker.style.position = "relative";
            ticker.style.width = "100%";
            ticker.style.background = sheepColour;
            ticker.style.color = "black";
            ticker.style.padding = "6px 0";
            ticker.style.overflow = "hidden";
            ticker.style.fontFamily = "Arial";
            ticker.style.fontWeight = "bold";
            ticker.style.zIndex = "99999";

            const content = document.createElement("div");
            content.style.display = "inline-block";
            content.style.whiteSpace = "nowrap";
            ticker.appendChild(content);
            document.body.prepend(ticker);

            // Push Torn content down
            const tickerHeight = ticker.offsetHeight;
            document.body.style.paddingTop = 0 + "px";

            // Function to fetch news
            function fetchNews() {
                const lastCheck = parseInt(localStorage.getItem("news_timestamp") || 0);
                const now = Date.now();
                if (now - lastCheck < NEWS_CHECK_INTERVAL) {
                    // Less than 2 minutes, skip fetching
                    return;
                }

                GM_xmlhttpRequest({
                    method: "GET",
                    url: TICKER_URL + "?t=" + now,
                    onload: function (response) {
                        const html = response.responseText;
                        const parser = new DOMParser();
                        const doc = parser.parseFromString(html, "text/html");

                        const items = Array.from(doc.querySelectorAll("#ticker-items li"))
                        .map(li => li.textContent.trim())
                        .filter(line => line.length > 0);

                        // Save news and timestamp to localStorage
                        localStorage.setItem("news_items", JSON.stringify(items));
                        localStorage.setItem("news_timestamp", now);

                        // Check first line for SheepieAIO
                        if (items.length > 0 && items[0].includes("SheepieAIO")) {
                            localStorage.setItem("warmode", "true");
                        } else {
                            localStorage.setItem("warmode", "false");
                        }

                        // Update ticker
                        updateTicker(items);
                    },
                    onerror: function (err) {
                        console.error("Failed to fetch news", err);
                    }
                });
            }

            // Function to update ticker content
            function updateTicker(items) {
                if (items.length === 0) {
                    content.innerHTML = "<span style='margin-right:60px;'>No ticker items found</span>";
                    return;
                }

                // Duplicate items for seamless scrolling
                const allItems = items.concat(items);

                // Add sheep emote between each item
                content.innerHTML = allItems
                    .map((line, index) => {
                    const emote = (index < allItems.length - 1) ? " 🐑 " : "";
                    return `<span style="margin-right:60px;">${line}${emote}</span>`;
                })
                    .join("");

                // Remove previous animation
                content.style.animation = "";

                // Smooth scroll speed
                const scrollWidth = content.scrollWidth / 2; // half because duplicated
                const pixelsPerSecond = 40; // adjust speed
                const duration = scrollWidth / pixelsPerSecond;

                content.style.display = "inline-block";
                content.style.paddingLeft = "0";
                content.style.animation = `scrollLeft ${duration}s linear infinite`;
            }

            // Initial ticker load from stored news (if any)
            const storedNews = JSON.parse(localStorage.getItem("news_items") || "[]");
            if (storedNews.length > 0) updateTicker(storedNews);

            // Load ticker every REFRESH_INTERVAL (content scroll refresh)
            sheepieSetInterval(() => {
                const news = JSON.parse(localStorage.getItem("news_items") || "[]");
                if (news.length > 0) updateTicker(news);
            }, REFRESH_INTERVAL);

            // Fetch news every 2 minutes (or first time)
            fetchNews();
            sheepieSetInterval(fetchNews, NEWS_CHECK_INTERVAL);

            // Add CSS for smooth scroll and hover pause
            const style = document.createElement("style");
            style.textContent = `
        @keyframes scrollLeft {
            0% { transform: translateX(0); }
            100% { transform: translateX(-50%); }
        }
        div:hover {
            animation-play-state: paused !important;
        }
    `;
            document.head.appendChild(style);

        })();

    });

    (function () {
        'use strict';

        const STORAGE_KEY = "mySimpleToggleState";

        // Create button
        const button = document.createElement("button");
        button.style.position = "fixed";
        button.style.bottom = "40px";
        button.style.left = "50px";
        button.style.fontSize = "14px";
        button.style.borderRadius = "8px";
        button.style.border = "1px solid #888";
        button.style.cursor = "pointer";
        button.style.zIndex = "9999998";

        // Load saved state
        let isOn = localStorage.getItem(STORAGE_KEY) === "true";

        function updateButton() {
            button.textContent = isOn ? "ON" : "OFF";
            button.style.backgroundColor = isOn ? "#4CAF50" : "#ccc";
            button.style.color = isOn ? "#fff" : "#000";


        }

        // Toggle logic
        button.addEventListener("click", function () {
            isOn = !isOn;
            localStorage.setItem(STORAGE_KEY, isOn);
            updateButton();
            location.reload();
        });
        updateButton();
        document.body.appendChild(button);
    })();


    const isOn = localStorage.getItem("mySimpleToggleState") === "true";

    if (isOn) {

        sessionStorage.removeItem("floatingTimerRemaining");
        function createButtonAtPositionToDo(text, topPx, leftPx) {
            var customButton = document.createElement("button");
            customButton.innerHTML = '<h1>Timer Info</h1><p>hold middle click and drag to move timer<br><br><br>295s darkgreen<br>240s green<br>210s lightgreen<br>160s yellow<br>120s orange<br>95s red<br>90s orange<br>75s alternate red-orange<br>35s darkred </p><h2>ToDo</h2><p>bug testing - smooth out sidebar a bit thin the icons out mayhaps<br>add harambe memorial<br> see how difficult making the chain save buttons wiggle would be <br>add editable notes tab, use localstorage to store notes, call notes on open of tab, save notes on exit and button <br>Add a credits tab<br> rgb easteregg button <br> racing reminder toggle <br> xanax toggle <br> toggle dropdown menu?</p>';
            customButton.style.width = "450px";
            customButton.style.height = "800px";
            customButton.style.position = 'fixed';
            customButton.style.bottom = topPx + 'px';
            customButton.style.right = leftPx + 'px';
            customButton.style.backgroundColor = sheepColour;
            customButton.style.color = 'black';
            customButton.style.border = 'none';
            customButton.style.cursor = 'pointer';
            customButton.style.border = "1px solid purple";
            customButton.style.zIndex = '999999';
            customButton.borderRadius = '5px'
            customButton.addEventListener('click', function() {
                customButton.style.display = 'none';
            });
            document.body.appendChild(customButton);
        }



        (function () {
            'use strict';

            const STORAGE_KEY = "SheepieWatch";

            // Initialize value if it doesn't exist
            if (localStorage.getItem(STORAGE_KEY) === null) {
                localStorage.setItem(STORAGE_KEY, "false");
            }

            // Create button
            const btn = document.createElement("button");
            btn.style.position = "fixed";
            btn.style.bottom = "40px";
            btn.style.left = "55px";
            btn.style.borderRadius = "8px";
            btn.style.border = "2px solid #444";
            btn.style.cursor = "pointer";
            btn.style.zIndex = "9999";
            btn.style.width = "120px";
            btn.style.height = "50px";
            function isTornPDA() {
                return /TornPDA/i.test(navigator.userAgent);
            }

            if (isTornPDA()) {
                btn.style.bottom = '333990px';
                btn.style.left = '3339990px';}

            function updateButton() {
                const value = localStorage.getItem(STORAGE_KEY) === "true";
                btn.textContent = `SheepieWatch: ${value ? "ON 🟢" : "OFF 🔴"}`;
                btn.style.background = value ? "#b5f5c0" : "#f5b5b5";
            }

            btn.addEventListener("click", function () {
                const current = localStorage.getItem(STORAGE_KEY) === "true";
                localStorage.setItem(STORAGE_KEY, (!current).toString());
                updateButton();
                window.location.href = window.location.href;
            });

            updateButton();



            (function() {
                'use strict';

                // Create button
                const infoButton = document.createElement('button');
                infoButton.textContent = 'Scripts';
                Object.assign(infoButton.style, {
                    position: 'fixed',
                    top: '30px',
                    left: '170px',
                    zIndex: 999999,
                    color: 'black',
                    background: sheepColour,
                    color: 'black',
                    border: 'none',
                    borderRadius: '5px',
                    cursor: 'pointer',
                    width: '4%',
                    fontSize: '74%',
                });


                // Create info window
                const infoWindow = document.createElement('div');
                infoWindow.innerHTML = `
        <div id="infoHeader" style="font-weight:bold; cursor:move; margin-bottom:10px;">
            Info <span id="closeInfo" style="float:right; cursor:pointer; color:red;">✖</span>
        </div>
<div id="infoContent" style="font-size:0.9em; line-height:1.2em; max-width:600px;">
<h1>please click the lil arrow for dropdown info! </h1><h2>close the window with the lil X in the top right!! its hard to see --></h2>
    <p><strong>Stolen/forked & heavily edited by SheepPrincess [2679129]</strong> with help from evil AI ;-;<br>
    Not a real coder — just simplifying life for friends & factionmates ❤️</p>
<details>
        <summary><strong>"Dumb" Chain Timer</strong></summary>
        <ul>
            <li>"Dumb" chain timer: sets remaining chaintime on page focus loss, ignores hits when page not focused</li>
        </ul>
    </details>




    <details>
        <summary><strong>Video Player</strong></summary>
        <ul>
            <li>Embedded small basic video player in a button</li>
        </ul>
    </details>

    <details>
        <summary><strong>Chain Timer Enhancer</strong> (heavily modded, disabled when page out of focus)</summary>
        <ul>
            <li>Resizable/moveable timer with improved color coding</li>
            <li>Click to go to target attack page, middle-mouse drag to move</li>
            <li>Low chain: creates multiple "SaveChain" buttons (persist 10s, random targets, enables BetterAttack script; disabled on attack screens)</li>
        </ul>
    </details>

    <details>
        <summary><strong>EHeasley's Egg Navigator 1.5.2</strong></summary>
        <p>Traverse Torn pages searching for eggs.</p>
    </details>

    <details>
        <summary><strong>Egg Finder 1.1.0</strong></summary>
        <ul>
            <li>Notifies if there is an egg on page</li>
            <li>Centers egg & makes it float over everything</li>
            <li>Enlarges egg & clickable area ~400%</li>
            <li>Outlines hitbox in red</li>
        </ul>
    </details>

    <details>
        <summary><strong>Fast Slots 0.3</strong></summary>
        <p>Stops slots instantly (except first spin)</p>
    </details>

    <details>
        <summary><strong>Crime Morale 1.4.13</strong></summary>
        <p>Scamming helper</p>
    </details>

    <details>
        <summary><strong>OC Role Display - Ravage Edition 3.1.2</strong></summary>
        <p>Color codes OC positions & weight %</p>
    </details>

    <details>
        <summary><strong>Torn Pickpocketing Colors 0.4</strong></summary>
        <p>Color codes crimes by difficulty</p>
    </details>

    <details>
        <summary><strong>Torn Market Filler 0.6.3</strong></summary>
        <p>Autofills lowest market price - $1, max quantity, gun checkboxes</p>
    </details>

    <details>
        <summary><strong>Torn: Refill Blood Bag Reminder 4.3.0</strong></summary>
        <p>Shows icon when ready; configurable 1–3 bags</p>
    </details>

    <details>
        <summary><strong>Torn: Attack Better 1.7.1</strong></summary>
        <p>Moves "Start Fight" button atop chosen weapon; removes some elements for faster load</p>
    </details>

    <details>
        <summary><strong>Torn: Fast Packs 0.4</strong></summary>
        <p>Removes supply pack animations</p>
    </details>

    <details>
        <summary><strong>Don't forget your Xanax 2024-06-26</strong></summary>
        <p>Reminds you to take Xanax when no drug cooldown</p>
    </details>
</div>


    `;
                Object.assign(infoWindow.style, {
                    display: 'none',
                    position: 'fixed',
                    bottom: '100px',
                    left: '100px',
                    width: '800px',
                    maxWidth: '80%',
                    background: sheepColour,
                    color: 'black',
                    border: '2px solid #4CAF50',
                    borderRadius: '8px',
                    boxShadow: '0 4px 8px rgba(0,0,0,0.2)',
                    padding: '1px',
                    zIndex: 99999999,
                    cursor: 'move',
                    resize: 'both',
                    overflow: 'auto'
                });
                document.body.appendChild(infoWindow);

                const closeBtn = infoWindow.querySelector('#closeInfo');
                const header = infoWindow.querySelector('#infoHeader');

                // Toggle window
                infoButton.addEventListener('click', () => {
                    infoWindow.style.display = infoWindow.style.display === 'block' ? 'none' : 'block';
                });

                // Close button
                closeBtn.addEventListener('click', () => {
                    infoWindow.style.display = 'none';
                });








                function createButtonAtPositionShow(text, topPx, LeftPx) {
                    var customButton = document.createElement("button");
                    customButton.style.width = "120px";
                    customButton.style.height = "80px";
                    customButton.innerHTML = 'V2.3.1 ~SeditionPublic~ Ɛ>~Version~<3 Click To Show Sidebar';
                    customButton.style.position = 'absolute';
                    customButton.style.top = topPx + 'px';
                    customButton.style.left = LeftPx + 'px';
                    customButton.style.backgroundColor = sheepColour;
                    customButton.style.color = 'black';
                    customButton.style.border = "1px solid purple";
                    customButton.style.cursor = 'pointer';
                    customButton.style.zIndex = '999999';
                    function isTornPDA() {
                        return /TornPDA/i.test(navigator.userAgent);
                    }

                    if (isTornPDA()) {
                        customButton.style.position = 'absolute';
                        customButton.style.top = '333990px';
                        customButton.style.left = '3339990px';
                        customButton.style.width = '0px';
                        // PDA-specific code here
                    }
                    customButton.addEventListener('click', function() {

                        createButtonAtPosition2("Sheepie", 50, 25);
                        createButtonAtPosition("ResetSize", 80, 25);
                        createButtonAtPosition4("TargetList", 110, 25);
                        createButtonAtPosition5("FFScouter", 140, 25);
                        createButtonAtPosition6("FacDrugs", 170, 25);
                        createButtonAtPosition7("FacBoosters", 200, 25);
                        createButtonAtPosition8("FacMeds", 230, 25);
                        createButtonAtPosition9("Chain", 260, 25);
                        createButtonAtPosition3("CustomLink", 290, 25);
                        createButtonAtPosition11("Bugs", 40, 20);
                        createButtonAtPosition10("ToDo", 40, 60);
                        createButtonAtPosition12("Sheepiepage", 40, 100);
                        document.body.appendChild(cornerGif2);
                        document.body.appendChild(cornerGif2);
                        document.body.appendChild(cornerGif4);
                        customButton.style.display = 'none';
                        document.body.appendChild(btn);
                        document.body.appendChild(infoButton);
                    });
                    requestIdleCallback(() => {
                        document.body.appendChild(customButton);});
                }
                createButtonAtPositionShow("ShowSidebar", 50, 25);
                const cornerGif = document.createElement('img');
                cornerGif.src = 'https://factionimages.torn.com/85dbb882-5766-4e03-a985-71af719051bd-14432.gif';
                cornerGif.style.position = 'fixed';
                cornerGif.style.top = '10px';
                cornerGif.style.left = '10px';
                cornerGif.style.width = '150px';
                cornerGif.style.zIndex = '999999';
                cornerGif.style.border = "1px solid purple";
                cornerGif.addEventListener('click', function() {

                    function isTornPDA() {
                        return /TornPDA/i.test(navigator.userAgent);
                    }

                    if (isTornPDA()) {


                        createButtonAtPosition2("Sheepie", 50, 25);
                        createButtonAtPosition("ResetSize", 70, 25);
                        createButtonAtPosition4("TargetList", 90, 25);
                        createButtonAtPosition5("FFScouter", 110, 25);
                        createButtonAtPosition6("FacDrugs", 130, 25);
                        createButtonAtPosition7("FacBoosters", 150, 25);
                        createButtonAtPosition8("FacMeds", 170, 25);
                        createButtonAtPosition9("Chain", 190, 25);
                        createButtonAtPosition3("CustomLink", 210, 25);
                        createButtonAtPosition11("Bugs", 40, 20);
                        createButtonAtPosition10("ToDo", 40, 50);
                        createButtonAtPosition12("Sheepiepage", 40, 80);
                        document.body.appendChild(cornerGif2);
                        document.body.appendChild(cornerGif2);
                        document.body.appendChild(cornerGif4);
                        document.body.appendChild(btn);
                        document.body.appendChild(infoButton);
                    } else {

                        window.open('https://www.torn.com/factions.php?step=profile&ID=14432').focus();}
                });
                requestIdleCallback(() => {
                    document.body.appendChild(cornerGif);
                });
            })()
        })();




        if (isToggleEnabled()) {}






        (function () {
            'use strict';

            if (window.tmYTInjected) return;
            window.tmYTInjected = true;

            const STORAGE_KEY = "tmYTState";
            const VISIBILITY_KEY = "tmYTVisible";
            const POSITION_KEY = "tmYTPosition"; // original unminimized position/size
            const AUTO_SAVE_MS = 1000;

            let accumulatedTime = 0;
            let sessionStart = null;
            let autoSaveInterval = null;
            let isMinimized = false;
            let hasInteracted = false;

            /* ---------- Buttons ---------- */
            const openBtn = document.createElement("button");
            openBtn.textContent = "YT";
            openBtn.style.cssText = `
        position: fixed; bottom: 40px; left: 0px; z-index: 10000001;
        background: red; color: white; font-size: 24px; border: 1px solid purple;
        border-radius: 4px; padding: 6px 10px; cursor: pointer;
    `
    if (isTornPDA()) {
        openBtn.style.position = 'absolute';
        openBtn.style.top = '333990px';
        openBtn.style.left = '3339990px';
        openBtn.style.width = '0px';
        // PDA-specific code here
    };

            const closeBtn = document.createElement("button");
            closeBtn.textContent = "YT";
            closeBtn.style.cssText = `
        position: fixed; bottom: 40px; left: 0px; z-index: 9999999;
        background: red; color: white; font-size: 24px; border: 1px solid purple;
        border-radius: 4px; padding: 6px 10px; cursor: pointer; display: none;
        }`
    ;

            requestIdleCallback(() => {
                document.body.appendChild(openBtn);
                document.body.appendChild(closeBtn);
            });
            /* ---------- Player Wrapper ---------- */
            const wrapper = document.createElement("div");
            wrapper.style.cssText = `
        position: fixed; bottom: 60px; right: 40px; width: 420px; height: 340px;
        background: #0f0f0f; border: 1px solid #333; border-radius: 8px;
        z-index: 9999998; display: none; flex-direction: column; font-family: Arial;
        box-shadow: 0 8px 20px rgba(0,0,0,0.6); overflow: hidden; cursor: default;
    `;
            wrapper.inert = true;

            wrapper.innerHTML = `
        <div id="tm-header" style="background:#1b1b1b;padding:6px;color:#ccc;display:flex;justify-content:space-between;cursor:move">
            <span id="tm-title">YT 00:00</span>
            <button id="tm-minimize" style="background:none;border:none;color:#ccc;cursor:pointer">–</button>
        </div>
        <div id="tm-content" style="display:flex;flex-direction:column;flex:1;overflow:hidden;">
            <div style="display:flex;">
                <input id="tm-input" placeholder="Paste YouTube link"
                    style="flex:1;border:none;padding:6px;font-size:12px;outline:none;color:#ccc;background:#151515">
                <button id="tm-go" style="padding:6px 8px;font-size:12px;background:#333;color:#ccc;border:none;cursor:pointer">Go</button>
            </div>
            <iframe id="tm-frame" style="flex:1;border:none"
                allow="autoplay; encrypted-media" allowfullscreen></iframe>
            <div style="display:flex;justify-content:flex-end;background:#1b1b1b;padding:4px;gap:4px">
                <button id="tm-save" style="padding:4px 6px;font-size:12px;background:#333;color:#ccc;border:none;border-radius:4px;cursor:pointer">Save Position</button>
                <button id="tm-clear" style="padding:4px 6px;font-size:12px;background:#444;color:#ccc;border:none;border-radius:4px;cursor:pointer">Clear Saved Video</button>
            </div>
        </div>
        <div id="tm-resize"
            style="width:12px;height:12px;background:#444;position:absolute;bottom:2px;right:2px;cursor:se-resize">
        </div>
    `;
            document.body.appendChild(wrapper);

            const frame = wrapper.querySelector("#tm-frame");
            const input = wrapper.querySelector("#tm-input");
            const saveBtn = wrapper.querySelector("#tm-save");
            const goBtn = wrapper.querySelector("#tm-go");
            const clearBtn = wrapper.querySelector("#tm-clear");
            const minimizeBtn = wrapper.querySelector("#tm-minimize");
            const contentDiv = wrapper.querySelector("#tm-content");
            const header = wrapper.querySelector("#tm-header");
            const titleSpan = wrapper.querySelector("#tm-title");

            /* ---------- Storage Helpers ---------- */
            function saveState(videoId, playlistId, time) {
                localStorage.setItem(STORAGE_KEY, JSON.stringify({ videoId, playlistId, time }));
            }

            function loadState() {
                return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
            }

            function clearState() {
                localStorage.removeItem(STORAGE_KEY);
                accumulatedTime = 0;
                sessionStart = null;
            }

            function saveVisibility(visible) {
                localStorage.setItem(VISIBILITY_KEY, visible ? "1" : "0");
            }

            function loadVisibility() {
                return localStorage.getItem(VISIBILITY_KEY) === "1";
            }

            function saveWrapperPosition(pos) {
                localStorage.setItem(POSITION_KEY, JSON.stringify(pos));
            }

            function loadWrapperPosition() {
                return JSON.parse(localStorage.getItem(POSITION_KEY) || "{}");
            }

            /* ---------- Video Load ---------- */
            function extractIds(url) {
                const videoMatch = url.match(/(?:v=|youtu\.be\/)([\w-]+)/);
                const playlistMatch = url.match(/[?&]list=([\w-]+)/);
                return { videoId: videoMatch ? videoMatch[1] : null, playlistId: playlistMatch ? playlistMatch[1] : null };
            }

            function buildEmbedURL(url, startTime = 0, muted = true) {
                const { videoId, playlistId } = extractIds(url);
                const muteParam = muted ? "&mute=1" : "";
                if (playlistId && !videoId) return `https://www.youtube.com/embed/videoseries?list=${playlistId}&autoplay=1&start=${startTime}${muteParam}`;
                if (videoId) {
                    if (playlistId) return `https://www.youtube.com/embed/${videoId}?list=${playlistId}&autoplay=1&start=${startTime}${muteParam}`;
                    return `https://www.youtube.com/embed/${videoId}?autoplay=1&start=${startTime}${muteParam}`;
                }
                return null;
            }

            function loadVideo(url) {
                const state = loadState();
                const { videoId, playlistId } = extractIds(url);
                const embedURL = buildEmbedURL(url, state.time || 0, !hasInteracted);
                if (!embedURL) return;
                if (!frame.src || !frame.src.includes(videoId || playlistId)) frame.src = embedURL;
                saveState(videoId, playlistId, state.time || 0);
            }

            /* ---------- Timer ---------- */
            function formatTime(s) {
                const h = Math.floor(s / 3600);
                const m = Math.floor((s % 3600) / 60);
                const sec = s % 60;
                return `${h>0?h+":" : ""}${m.toString().padStart(2,"0")}:${sec.toString().padStart(2,"0")}`;
            }

            function updateTitleTimestamp() {
                const elapsed = sessionStart ? Math.floor((Date.now() - sessionStart)/1000) : 0;
                titleSpan.textContent = `YT ${formatTime(accumulatedTime + elapsed)}`;
            }

            function startAutoSave() {
                if (autoSaveInterval) return;
                sessionStart = Date.now();
                autoSaveInterval = setInterval(() => {
                    const elapsed = Math.floor((Date.now() - sessionStart)/1000);
                    const totalTime = accumulatedTime + elapsed;
                    const state = loadState();
                    saveState(state.videoId, state.playlistId, totalTime);
                    updateTitleTimestamp();
                }, AUTO_SAVE_MS);
            }

            function stopSessionTimer() {
                if (!sessionStart) return;
                const elapsed = Math.floor((Date.now() - sessionStart)/1000);
                accumulatedTime += elapsed;
                sessionStart = null;
                if (autoSaveInterval) {
                    clearInterval(autoSaveInterval);
                    autoSaveInterval = null;
                }
            }

            /* ---------- Restore State ---------- */
            const lastState = loadState();
            accumulatedTime = lastState.time || 0;

            let savedPos = loadWrapperPosition();
            const defaultWidth = 420;
            const defaultHeight = 340;

            wrapper.style.left = savedPos.left != null ? Math.min(savedPos.left, window.innerWidth - (savedPos.width || defaultWidth)) + "px" : "40px";
            wrapper.style.top = savedPos.top != null ? Math.min(savedPos.top, window.innerHeight - (savedPos.height || defaultHeight)) + "px" : "60px";
            wrapper.style.width = (savedPos.width || defaultWidth) + "px";
            wrapper.style.height = (savedPos.height || defaultHeight) + "px";

            if (!frame.src && (lastState.videoId || lastState.playlistId)) {
                const url = lastState.videoId ? `https://www.youtube.com/watch?v=${lastState.videoId}` : `https://www.youtube.com/playlist?list=${lastState.playlistId}`;
                loadVideo(url);
            }

            /* ---------- Show/Hide ---------- */
            function updateButtons() {
                if (wrapper.style.display === "none") {
                    openBtn.style.display = "block";
                    closeBtn.style.display = "none";
                } else {
                    openBtn.style.display = "none";
                    closeBtn.style.display = "block";
                }
            }

            function minimizePlayer() {
                isMinimized = true;
                contentDiv.style.display = "none";
                wrapper.style.height = "30px";
            }

            function restorePlayer() {
                isMinimized = false;
                contentDiv.style.display = "flex";
                wrapper.style.width = (savedPos.width || defaultWidth) + "px";
                wrapper.style.height = (savedPos.height || defaultHeight) + "px";
            }

            function showPlayer(visible) {
                wrapper.style.display = visible ? "flex" : "none";
                wrapper.inert = !visible;
                saveVisibility(visible);

                if (visible) {
                    restorePlayer();
                    startAutoSave();
                } else {
                    stopSessionTimer();
                }
                updateButtons();
            }

            openBtn.addEventListener("click", () => showPlayer(true));
            closeBtn.addEventListener("click", () => {
                showPlayer(false);
                clearState();
            });

            minimizeBtn.addEventListener("click", () => {
                if (contentDiv.style.display === "none") restorePlayer();
                else minimizePlayer();
            });

            /* ---------- Input ---------- */
            function handleInput() {
                const url = input.value.trim();
                if (!url) return;
                loadVideo(url);
                input.value = "";
                hasInteracted = true;
            }
            input.addEventListener("keypress", e => { if (e.key === "Enter") handleInput(); });
            goBtn.addEventListener("click", handleInput);

            /* ---------- Save / Clear ---------- */
            saveBtn.addEventListener("click", () => {
                const elapsed = sessionStart ? Math.floor((Date.now() - sessionStart)/1000) : 0;
                const totalTime = accumulatedTime + elapsed;
                const state = loadState();
                saveState(state.videoId, state.playlistId, totalTime);
                alert(`Position saved at ~${totalTime} seconds`);
            });
            clearBtn.addEventListener("click", () => {
                clearState();
                frame.src = "";
                alert("Saved video data cleared.");
            });

            /* ---------- Drag ---------- */
            let dragging = false, offsetX, offsetY;
            header.addEventListener("mousedown", (e) => { dragging = true; offsetX = e.clientX - wrapper.offsetLeft; offsetY = e.clientY - wrapper.offsetTop; });
            document.addEventListener("mousemove", (e) => {
                if (!dragging) return;
                wrapper.style.left = e.clientX - offsetX + "px";
                wrapper.style.top = e.clientY - offsetY + "px";
                wrapper.style.right = "auto";
                wrapper.style.bottom = "auto";
            });
            document.addEventListener("mouseup", () => {
                if (dragging) {
                    dragging = false;
                    if (!isMinimized) {
                        savedPos.left = parseInt(wrapper.style.left,10);
                        savedPos.top = parseInt(wrapper.style.top,10);
                        savedPos.width = wrapper.offsetWidth;
                        savedPos.height = wrapper.offsetHeight;
                        saveWrapperPosition(savedPos);
                    }
                }
            });

            /* ---------- Resize ---------- */
            const resize = wrapper.querySelector("#tm-resize");
            let resizing = false, startW, startH, startX, startY;
            resize.addEventListener("mousedown", (e) => { resizing = true; startW = wrapper.offsetWidth; startH = wrapper.offsetHeight; startX = e.clientX; startY = e.clientY; e.preventDefault(); });
            document.addEventListener("mousemove", (e) => {
                if (!resizing) return;
                wrapper.style.width = startW + (e.clientX - startX) + "px";
                wrapper.style.height = startH + (e.clientY - startY) + "px";
            });
            document.addEventListener("mouseup", () => {
                if (resizing) {
                    resizing = false;
                    if (!isMinimized) {
                        savedPos.width = wrapper.offsetWidth;
                        savedPos.height = wrapper.offsetHeight;
                        savedPos.left = parseInt(wrapper.style.left,10);
                        savedPos.top = parseInt(wrapper.style.top,10);
                        saveWrapperPosition(savedPos);
                    }
                }
            });

            /* ---------- INITIAL ---------- */
            showPlayer(loadVisibility());
            updateTitleTimestamp();
        })();







    }
    if (isToggleEnabled()) {

        (function () {
            'use strict';

            const bankers = [
                { id: "2493877", name: "Banker 1" },
                { id: "2202288", name: "Banker 2" },
                { id: "1316121", name: "Banker 3" },
                { id: "2112766", name: "Banker 4" },
                { id: "2161442", name: "Banker 5" },
                { id: "2124788", name: "Banker 6" },
                { id: "1322101", name: "Banker 7" },
                { id: "2261496", name: "Banker 8" },
                { id: "2535362", name: "Banker 9" },
                { id: "2649361", name: "Banker 10" }
            ];

            // Create Toggle Button
            const button = document.createElement("button");
            button.textContent = "Bankers";
            button.style.position = "fixed";
            button.style.bottom = "80px";
            button.style.left = "0px";
            if (isTornPDA()) { button.style.bottom = "40px";
                              button.style.left = "0px";}
            button.style.zIndex = "9999";
            button.style.background = sheepColour;
            button.style.color = "black";
            button.style.border = "1px solid #444";
            button.style.borderRadius = "6px";
            button.style.cursor = "pointer";
            button.style.boxShadow = "0 0 10px rgba(0,0,0,0.5)";
            button.style.fontSize = '80%';
            requestIdleCallback(() => {
                document.body.appendChild(button);
            });
            // Create Popup Panel
            const panel = document.createElement("div");
            panel.style.position = "fixed";
            panel.style.top = "50%";
            panel.style.left = "50%";
            panel.style.transform = "translate(-50%, -50%)";
            panel.style.background = sheepColour;
            panel.style.border = "2px solid #333";
            panel.style.borderRadius = "10px";
            panel.style.padding = "15px";
            panel.style.zIndex = "10000";
            panel.style.display = "none";
            panel.style.maxHeight = "80vh";
            panel.style.overflowY = "auto";
            panel.style.boxShadow = "0 0 25px rgba(0,0,0,0.8)";

            // Close Button
            const closeBtn = document.createElement("div");
            closeBtn.textContent = "✕";
            closeBtn.style.textAlign = "right";
            closeBtn.style.cursor = "pointer";
            closeBtn.style.color = "#aaa";
            closeBtn.style.marginBottom = "10px";
            closeBtn.onclick = () => panel.style.display = "none";
            panel.appendChild(closeBtn);

            // Add Banker Images
            bankers.forEach(banker => {
                const link = document.createElement("a");
                link.href = `https://www.torn.com/profiles.php?XID=${banker.id}`;

                const img = document.createElement("img");
                img.src = `/sigs/19_${banker.id}.png`;
                img.alt = banker.name;
                img.style.display = "block";
                img.style.margin = "8px auto";
                img.style.width = "100%";
                img.style.borderRadius = "6px";
                img.style.transition = "transform 0.2s ease";

                img.onmouseenter = () => img.style.transform = "scale(1.03)";
                img.onmouseleave = () => img.style.transform = "scale(1)";

                link.appendChild(img);
                panel.appendChild(link);
            });

            document.body.appendChild(panel);

            // Toggle Logic
            button.onclick = () => {
                panel.style.display = panel.style.display === "none" ? "block" : "none";
            };

        })();





        function combineToSeconds(minutes, seconds) {
            return parseInt(minutes) * 60 + parseInt(seconds);
        }
        // ==================== FLOATING DRAGGABLE TIMER ====================

        const sheeppenvar = localStorage.getItem("sheeppen");
        const minID = 3900000;
        const maxID = 3964119;

        function getRandomNumber(min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }

        if (sheeppenvar === null) {function isTornPDA() {
            return /TornPDA/i.test(navigator.userAgent);
        }

                                   if (isTornPDA()) { const sheep = 0
                                   localStorage.setItem("sheeppen", sheep);
                                                    } else {
                                                        const sheep = prompt("Please enter size from 50 to 250 (80 is default), refresh the page after entering desired sheepcounter");
                                                        localStorage.setItem("sheeppen", sheep);
                                                        window.location.reload(); }
                                  }

        // Create the floating timer element
        const floatingTimer = document.createElement('div');
        floatingTimer.id = 'sheepieFloatingTimer';
        floatingTimer.style.position = 'fixed';
        floatingTimer.style.zIndex = '9999999';
        floatingTimer.style.color = 'black';
        floatingTimer.style.cursor = 'move';
        floatingTimer.style.fontSize = `${sheeppenvar}px`;
        floatingTimer.style.padding = '8px 12px';
        floatingTimer.style.backgroundColor = '#00000000';
        floatingTimer.style.boxShadow = '0 4px 12px rgba(0,0,0,0.4)';
        floatingTimer.style.textAlign = 'center';
        floatingTimer.style.minWidth = '0px';
        floatingTimer.style.userSelect = 'none';
        floatingTimer.style.top = '40px';
        floatingTimer.style.right = '260px';
        // Convert any bottom/right positioning to top/left so dragging always works
        document.body.appendChild(floatingTimer);
        const rect = floatingTimer.getBoundingClientRect();
        floatingTimer.style.top = rect.top + 'px';
        floatingTimer.style.left = rect.left + 'px';
        floatingTimer.style.bottom = 'auto';
        floatingTimer.style.right = 'auto';
        let toggleEnabled = localStorage.getItem('sheepTimerToggle') === 'true';
        if (toggleEnabled) {floatingTimer.style.top = '3330px';
                            floatingTimer.style.right = '3330px';}
        // ==================== MAKE IT DRAGGABLE ====================
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        floatingTimer.onmousedown = function (e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        };

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            floatingTimer.style.top = (floatingTimer.offsetTop - pos2) + "px";
            floatingTimer.style.left = (floatingTimer.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }

        // Click = random attack (click still works, dragging does not trigger it)
        floatingTimer.addEventListener('click', function () {
            let randID = getRandomNumber(minID, maxID);
            let profileLink = `https://www.torn.com/loader.php?sid=attack&user2ID=${randID}`;
            window.location.href = profileLink;
        });

        // ==================== OBSERVER + COLOUR LOGIC ====================
        const element = document.querySelector('.bar-timeleft___B9RGV');
        if (element) {
            // Keep the original timer large (optional: uncomment next line to hide it)
            // element.style.display = 'none';

            const sizeRule = `
            .bar-timeleft___B9RGV {
                font-size: ${sheeppenvar}px !important;
                cursor: pointer;
            }
        `;


            const styleEl = document.createElement('style');
            styleEl.textContent = sizeRule;
            document.head.appendChild(styleEl);
            let observer = null;

            function startObserver() {
                if (observer) return;


                const config = { characterData: true, attributes: false, childList: false, subtree: true };
                observer = new MutationObserver((list) => {
                    if (!document.hasFocus()) return;
                    if (!list[0] || !list[0].target) return;

                    const timeText = list[0].target.textContent.trim();
                    floatingTimer.textContent = timeText;

                    const timeArray = timeText.split(":");
                    if (timeArray.length < 2) return;

                    const totalSeconds = combineToSeconds(timeArray[0], timeArray[1]);
                    let remainingTime = (totalSeconds) * 1000; // 5 minutes
                    sessionStorage.setItem('floatingTimerRemaining', remainingTime)
                    const SheepieWatch = localStorage.getItem("SheepieWatch") === "true";
                    let bg = "green";
                    if ([300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 269, 268, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241].includes(totalSeconds)) { bg = "darkgreen";}
                    else if ([240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, 214, 213, 212, 211].includes(totalSeconds)) bg = "green";
                    else if ([210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 195, 194, 193, 192, 191, 190, 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161].includes(totalSeconds)) bg = "lightgreen";
                    else if ([160, 159, 158, 157, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128, 127, 126, 125, 124, 123, 122, 121].includes(totalSeconds)) bg = "yellow";
                    else if ([120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96].includes(totalSeconds)) { bg = "orange";
             if (SheepieWatch) {
                 let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                 let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                 createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);}}

                    else if ([95, 94, 93, 92, 91].includes(totalSeconds)) { bg = "red";
                                                                           if (SheepieWatch) {
                                                                               let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber1);
                                                                               let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);
                                                                               let sheepnumber3 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber2, sheepnumber3);
                                                                               let sheepnumber4 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber3, sheepnumber4);
                                                                               let sheepnumber5 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber4, sheepnumber5);}}

                    else if ([90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76].includes(totalSeconds)) { bg = "orange";
                                                                                                                   if (SheepieWatch) {
                                                                                                                       let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                                                                       let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                                                                       createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);}}


                    else if ([75, 74, 73, 72, 71].includes(totalSeconds)) { bg = "red";
                                                                           if (SheepieWatch) {
                                                                               let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber1);
                                                                               let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);
                                                                               let sheepnumber3 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber2, sheepnumber3);
                                                                               let sheepnumber4 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber3, sheepnumber4);
                                                                               let sheepnumber5 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber4, sheepnumber5);}}

                    else if ([70, 69, 68, 67, 66].includes(totalSeconds)) { bg = "orange";
                                                                           if (SheepieWatch) {
                                                                               let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                               let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);}}


                    else if ([65, 64, 63, 62, 61].includes(totalSeconds)) { bg = "red";
                                                                           if (SheepieWatch) {
                                                                               let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber1);
                                                                               let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);
                                                                               let sheepnumber3 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber2, sheepnumber3);
                                                                               let sheepnumber4 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber3, sheepnumber4);
                                                                               let sheepnumber5 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber4, sheepnumber5);}}

                    else if ([60, 59, 58, 58, 57, 56].includes(totalSeconds)){ bg = "orange";
                                                                              if (SheepieWatch) {
                                                                                  let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                                  let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                                  createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);}}


                    else if ([55, 54, 53, 52, 51].includes(totalSeconds)) { bg = "red";
                                                                           if (SheepieWatch) {
                                                                               let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber1);
                                                                               let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);
                                                                               let sheepnumber3 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber2, sheepnumber3);
                                                                               let sheepnumber4 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber3, sheepnumber4);
                                                                               let sheepnumber5 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber4, sheepnumber5);}}

                    else if ([50,49,48,47,46].includes(totalSeconds)) { bg = "orange";
                                                                       if (SheepieWatch) {
                                                                           let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                           let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                           createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);}}


                    else if ([45, 44, 43, 42, 41].includes(totalSeconds)) { bg = "red";
                                                                           if (SheepieWatch) {
                                                                               let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber1);
                                                                               let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);
                                                                               let sheepnumber3 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber2, sheepnumber3);
                                                                               let sheepnumber4 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber3, sheepnumber4);
                                                                               let sheepnumber5 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber4, sheepnumber5);}}

                    else if ([40, 39, 38, 37, 36].includes(totalSeconds)) { bg = "orange";
                                                                           if (SheepieWatch) {
                                                                               let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                               let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);}}


                    else if ([35, 34, 33, 32, 31].includes(totalSeconds)) { bg = "red";
                                                                           if (SheepieWatch) {
                                                                               let sheepnumber1 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber1);
                                                                               let sheepnumber2 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber1, sheepnumber2);
                                                                               let sheepnumber3 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber2, sheepnumber3);
                                                                               let sheepnumber4 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber3, sheepnumber4);
                                                                               let sheepnumber5 = Math.floor(Math.random() * 788) + 150;
                                                                               createButtonAtPosition17("SaveChain", sheepnumber4, sheepnumber5);}}
                    else if ([30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1].includes(totalSeconds)) { bg = "darkred";}
                    else if ([310,0].includes(totalSeconds)) { bg = "darkred";
                                                              sessionStorage.removeItem("floatingTimerRemaining");}

                    floatingTimer.style.backgroundColor = bg;

                });

                observer.observe(element, config);
            }

            function stopObserver() {
                if (observer) {
                    observer.disconnect();
                    observer = null;

                }
            }

            // Page visibility handling
            document.addEventListener("visibilitychange", () => {
                if (document.visibilityState === "visible") {
                    startObserver();
                } else {
                    stopObserver();

                }
            });

            // Start if already focused
            if (document.visibilityState === "visible") {
                startObserver();
            }
        }

    }


    const cornerGif2 = document.createElement('img');
    cornerGif2.src = 'https://factionimages.torn.com/85dbb882-5766-4e03-a985-71af719051bd-14432.gif';
    cornerGif2.style.position = 'fixed';
    cornerGif2.style.top = '10px';
    cornerGif2.style.left = '10px';
    cornerGif2.style.width = '150px';
    cornerGif2.style.zIndex = '9999';
    cornerGif2.style.border = "1px solid purple";
    cornerGif2.addEventListener('click', function() {
        window.open('https://www.torn.com/factions.php?step=profile&ID=14432').focus();
    });
    requestIdleCallback(() => {
        document.body.appendChild(cornerGif2)
    });
    const cornerGif3 = document.createElement('img');
    cornerGif3.src = 'https://sheepie.ca/img/d4kWYmJ.gif';
    cornerGif3.style.position = 'absolute';
    cornerGif3.style.top = '30px';
    cornerGif3.style.right = '0px';
    cornerGif3.style.width = '150px';


    if (isTornPDA()) {
        cornerGif3.style.position = 'absolute';
        cornerGif3.style.top = '3330px';
        cornerGif3.style.right = '3330px';
        cornerGif3.style.width = '0px';
        // PDA-specific code here
    }
    cornerGif3.style.zIndex = '9999';
    cornerGif3.style.border = "1px solid purple";
    cornerGif3.addEventListener('click', function() {
        const minID = 3900000;
        const maxID = 3964119;
        let randID = getRandomNumber(minID, maxID);
        window.open(`https://www.torn.com/loader.php?sid=attack&user2ID=${randID}`).focus();
        // window.open('https://www.youtube.com/watch?v=dQw4w9WgXcQ').focus();
    });
    function isToggleEnabled() {
        return localStorage.getItem("mySimpleToggleState") === "true";
    }

    const cornerGif4 = document.createElement('img');
    cornerGif4.src = 'https://sheepie.ca/img/7v3an3v.gif';
    cornerGif4.style.position = 'absolute';
    cornerGif4.style.bottom = '20px';
    cornerGif4.style.left = '100px';
    cornerGif4.style.zIndex = '999999';
    cornerGif4.style.border = "1px solid purple";
    if (isTornPDA()) {
        cornerGif4.style.position = 'absolute';
        cornerGif4.style.top = '3330px';
        cornerGif4.style.right = '3330px';
        cornerGif4.style.width = '0px';
        // PDA-specific code here
    }
    cornerGif4.addEventListener('click', function() {
        window.open('https://www.torn.com/profiles.php?XID=2679129').focus();
    });


    requestIdleCallback(() => {
        if (isToggleEnabled()) {document.body.appendChild(cornerGif3);}
    });



    function createButtonAtPosition(text, topPx, leftPx) {
        var customButton1 = document.createElement("button");
        customButton1.innerHTML = 'ResetScript';
        customButton1.style.width = sheepwide + "px";
        customButton1.style.height = sheeptall + "px";
        customButton1.style.position = 'fixed';
        customButton1.style.top = topPx + 'px';
        customButton1.style.left = leftPx + 'px';
        customButton1.style.backgroundColor = sheepColour;
        customButton1.style.color = 'black';
        customButton1.style.border = 'none';
        customButton1.style.cursor = 'pointer';
        customButton1.style.border = "1px solid purple";
        customButton1.style.zIndex = '999999';
        customButton1.style.fontSize = (sheeptall * 0.4) + "px";
        customButton1.addEventListener('click', function() {
            localStorage.removeItem("sheeppen");
            localStorage.removeItem("sheeplink");
            localStorage.removeItem("sheepAPI");
            localStorage.removeItem("sheepcolour")
            localStorage.removeItem("floatingTimerPosition")
            localStorage.removeItem("floatingTimerSize")
            localStorage.removeItem("SheepieWatch")
            localStorage.removeItem("mySimpleToggleState")
            localStorage.removeItem("sheepattackAPI");
            window.location.reload();
        });
        document.body.appendChild(customButton1);
    }


    function createButtonAtPosition2(text, topPx, leftPx) {
        var customButton2 = document.createElement("button");
        customButton2.style.width = sheepwide + "px";
        customButton2.style.height = sheeptall + "px";
        customButton2.innerHTML = 'Send Me Sheep Plushies';
        customButton2.style.position = 'fixed';
        customButton2.style.top = topPx + 'px';
        customButton2.style.left = leftPx + 'px';
        customButton2.style.backgroundColor = '#FFD1DC';
        customButton2.style.color = 'black';
        customButton2.style.border = 'none';
        customButton2.style.cursor = 'pointer';
        customButton2.style.border = "1px solid purple";
        customButton2.style.zIndex = '999999';
        customButton2.style.fontSize = (sheeptall * 0.4) + "px";
        customButton2.addEventListener('click', function() {
            window.open('https://www.torn.com/profiles.php?XID=2679129', 'Sheepie').focus();
        });
        document.body.appendChild(customButton2);
    }


    function createButtonAtPosition3(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.style.width = sheepwide + "px";
        customButton.style.height = sheeptall + "px";
        customButton.innerHTML = 'CustomLink';
        customButton.style.position = 'fixed';
        customButton.style.top = topPx + 'px';
        customButton.style.left = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '999999';
        customButton.style.fontSize = (sheeptall * 0.4) + "px";
        customButton.addEventListener('click', function() {
            const sheeplink = localStorage.getItem("sheeplink");
            if (sheeplink === null) {
                const sheeplink = prompt("Please enter link, be SURE to start it with 'HTTPS://www.' ");
                localStorage.setItem("sheeplink", sheeplink);
                window.location.reload();
            } else {
                window.open(sheeplink).focus();
            }
        });
        document.body.appendChild(customButton);
    }

    function createButtonAtPosition4(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'TargetList';
        customButton.style.width = sheepwide + "px";
        customButton.style.height = sheeptall + "px";
        customButton.style.position = 'fixed';
        customButton.style.top = topPx + 'px';
        customButton.style.left = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '999999';
        customButton.style.fontSize = (sheeptall * 0.4) + "px";
        customButton.addEventListener('click', function() {
            window.open('https://www.torn.com/page.php?sid=list&type=targets', 'TargetList').focus();
        });
        document.body.appendChild(customButton);
    }

    function createButtonAtPosition5(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'FFScouter';
        customButton.style.width = sheepwide + "px";
        customButton.style.height = sheeptall + "px";
        customButton.style.position = 'fixed';
        customButton.style.top = topPx + 'px';
        customButton.style.left = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '999999';
        customButton.style.fontSize = (sheeptall * 0.4) + "px";
        customButton.addEventListener('click', function() {
            window.open('https://ffscouter.com/target-finder?preset=custom&liveUpdates=0&minLevel=1&maxLevel=100&minFF=2&maxFF=2.3&limit=20&inactiveOnly=1&factionless=1', 'FFSCouter').focus();
        });
        document.body.appendChild(customButton);
    }

    function createButtonAtPosition6(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'FacDrugs';
        customButton.style.width = sheepwide + "px";
        customButton.style.height = sheeptall + "px";
        customButton.style.position = 'fixed';
        customButton.style.top = topPx + 'px';
        customButton.style.left = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '999999';
        customButton.style.fontSize = (sheeptall * 0.4) + "px";
        customButton.addEventListener('click', function() {
            window.open('https://www.torn.com/factions.php?step=your&type=1#/faction-armoury=undefined&start=0&sub=drugs', 'Drugs').focus();
        });
        document.body.appendChild(customButton);
    }

    function createButtonAtPosition7(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'FacBoosters';
        customButton.style.width = sheepwide + "px";
        customButton.style.height = sheeptall + "px";
        customButton.style.position = 'fixed';
        customButton.style.top = topPx + 'px';
        customButton.style.left = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '999999';
        customButton.style.fontSize = (sheeptall * 0.4) + "px";
        customButton.addEventListener('click', function() {
            window.open('https://www.torn.com/factions.php?step=your&type=1#/faction-armoury=undefined&start=0&sub=boosters', 'Boosters').focus();
        });
        document.body.appendChild(customButton);
    }

    function createButtonAtPosition8(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'Fac Meds';
        customButton.style.width = sheepwide + "px";
        customButton.style.height = sheeptall + "px";
        customButton.style.position = 'fixed';
        customButton.style.top = topPx + 'px';
        customButton.style.left = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '999999';
        customButton.style.fontSize = (sheeptall * 0.4) + "px";
        customButton.addEventListener('click', function() {
            window.open('https://www.torn.com/factions.php?step=your&type=1#/faction-armoury=undefined&start=0&sub=medical', 'Meds').focus();
        });
        document.body.appendChild(customButton);
    }

    function createButtonAtPosition9(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'Chain';
        customButton.style.width = sheepwide + "px";
        customButton.style.height = sheeptall + "px";
        customButton.style.position = 'fixed';
        customButton.style.top = topPx + 'px';
        customButton.style.left = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '999999';
        customButton.style.fontSize = (sheeptall * 0.4) + "px";
        customButton.addEventListener('click', function() {
            window.open('https://www.torn.com/factions.php?step=your&type=1#/war/chain', 'Chain').focus();
        });
        document.body.appendChild(customButton);
    }

    function createButtonAtPosition10(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'Info';
        customButton.style.width = sheepwide2 + "px";
        customButton.style.height = sheeptall2 + "px";
        customButton.style.position = 'fixed';
        customButton.style.bottom = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '99999';
        customButton.style.fontSize = (sheeptall2 * 0.4) + "px";
        customButton.addEventListener('click', function() {
            createButtonAtPositionToDo("ToDo", 40, 25);
        });
        document.body.appendChild(customButton);
    }

    function createButtonAtPosition11(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'Bugs';
        customButton.style.width = sheepwide2 + "px";
        customButton.style.height = sheeptall2 + "px";
        customButton.style.position = 'fixed';
        customButton.style.bottom = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '99999';
        customButton.style.fontSize = (sheeptall2 * 0.4) + "px";
        customButton.addEventListener('click', function() {
            createButtonAtPositionReporting("ToDo", 40, 25);
        });
        document.body.appendChild(customButton);
    }
    function createButtonAtPositionReporting(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'please be sure to be as detailed as possible, attach images or upload them to imgur.com and link me them, you can also reachout to me over discord #Aleshkii   -click here to send me a message in torn-';
        customButton.style.width = "150px";
        customButton.style.height = "200px";
        customButton.style.position = 'fixed';
        customButton.style.bottom = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '99999';
        setTimeout(function() {
            customButton.style.display = 'none';
        }, 15000);
        customButton.addEventListener('click', function() {
            window.open('https://www.torn.com/messages.php#/p=compose&XID=2679129', 'reporting').focus();
            customButton.style.display = 'none';
        });
        document.body.appendChild(customButton);
    }

    function createButtonAtPosition12(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'URL';
        customButton.style.width = sheepwide2 + "px";
        customButton.style.height = sheeptall2 + "px";
        customButton.style.position = 'fixed';
        customButton.style.bottom = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '99999';
        customButton.style.fontSize = (sheeptall2 * 0.4) + "px";
        customButton.addEventListener('click', function() {
            window.open('https://sheepie.ca/index.html', 'SheepieAIO').focus();
        });
        document.body.appendChild(customButton);
    }


    function createButtonAtPosition17(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.innerHTML = 'SaveChain';
        customButton.style.width = "120px";
        customButton.style.height = "50px";
        customButton.style.position = 'fixed';
        customButton.style.bottom = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '9999';
        if (window.location.href === "https://www.torn.com/loader.php?sid=attack*") {customButton.style.display = 'none';}
        let sheepnum2 = Math.floor(Math.random() * 25000) + 15000;
        function getRandomColor() {
            const red = Math.floor(Math.random() * 256);
            const green = Math.floor(Math.random() * 256);
            const blue = Math.floor(Math.random() * 256);
            return `rgb(${red}, ${green}, ${blue})`;
        }
        let sheepcolour = getRandomColor();
        customButton.style.backgroundColor = sheepcolour;

        setTimeout(function() {
            customButton.style.display = 'none';
        }, sheepnum2);
        customButton.addEventListener('click', function() {
            const sheepattackAPI = localStorage.getItem("sheepattackAPI");
            if (sheepattackAPI === null) {
                const sheepAttack = ("yes");
                localStorage.setItem("sheepattackAPI", sheepAttack);}
            const minID = 3900000;
            const maxID = 3964119;
            let randID = getRandomNumber(minID, maxID);
            let profileLink = `https://www.torn.com/loader.php?sid=attack&user2ID=${randID}`;
            window.location.href = profileLink;
        });
        document.body.appendChild(customButton);
    }








    // OC Roles
    // author      tobytorn [1617955]


    (function() {
        'use strict';

        const API_URL = "https://tornprobability.com:3000/api/GetRoleWeights";
        const CACHE_KEY = "oc_role_weights_cache";
        const CACHE_TIME_KEY = "oc_role_weights_cache_time";
        const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours

        let roleWeightsAPI = null;

        /* -----------------------------
       STYLE (NO ANIMATION)
    ------------------------------*/
        const style = document.createElement('style');
        style.textContent = `
    .oc-solid-red {
        outline: 4px solid red !important;
        outline-offset: 0px !important;
    }
    .oc-has-contrib {
        position: relative !important;
        padding-bottom: 22px !important;
    }
    .oc-contrib-linewrap {
        position: absolute !important;
        left: 8px !important;
        right: 8px !important;
        bottom: 6px !important;
        display: flex !important;
        align-items: center !important;
        gap: 8px !important;
        pointer-events: none !important;
    }
    .oc-contrib-linewrap::before,
    .oc-contrib-linewrap::after {
        content: "";
        flex: 1;
        height: 1px;
        background: rgba(115,65,128,0.95);
    }
    .oc-contrib-linetext {
        font-weight: 900;
        font-size: 12px;
        color: 'sheepColour';

    }`;
        document.head.appendChild(style);

        /* -----------------------------
       YOUR ORIGINAL OC DATA
       (UNCHANGED)
    ------------------------------*/

        const defaultLevel6 = 67;
        const defaultLevel5 = 65;
        const defaultLevel4 = 65;
        const defaultLevel3 = 65;
        const defaultLevel2 = 65;
        const defaultDecline = 700;

        const ocRoles = [
            {
                OCName: "Blast From The Past",
                Positions: {
                    "MUSCLE": 70,
                    "ENGINEER": 70,
                    "BOMBER": 69,
                    "PICKLOCK #1": 69,
                    "HACKER": 69,
                    "PICKLOCK #2": 60
                }
            },{
                OCName: "Stacking the Deck",
                Positions: {
                    "HACKER": 60,
                    "IMITATOR": 64,
                    "CAT BURGLAR": 60,
                    "DRIVER": 50
                }
            },
            {
                OCName: "Ace in the Hole",
                Positions: {
                    "HACKER": 65,
                    "DRIVER": 55,
                    "MUSCLE #1": 62,
                    "IMITATOR": 65,
                    "MUSCLE #2": 63
                }
            },
            {
                OCName: "Break the Bank",
                Positions: {
                    "MUSCLE #1": 64,
                    "MUSCLE #2": 60,
                    "MUSCLE #3": 64,
                    "THIEF #1": 60,
                    "THIEF #2": 67,
                    "ROBBER": 64
                }
            },
            {
                OCName: "Bidding War",
                Positions: {
                    "ROBBER #1": 60,
                    "ROBBER #2": 69,
                    "ROBBER #3": 70,
                    "BOMBER #1": 60,
                    "BOMBER #2": 67,
                    "DRIVER": 63
                }
            },
            {
                OCName: "Clinical Precision",
                Positions: {
                    "IMITATOR": 68,
                    "CLEANER": 67,
                    "CAT BURGLAR": 67,
                    "ASSASSIN": 67
                }
            },
            {
                OCName: "Honey Trap",
                Positions: {
                    "MUSCLE #1": 67,
                    "MUSCLE #2": 67,
                    "ENFORCER": 67}
            },
            {
                OCName: "Sneaky Git Grab",
                Positions: {
                    "PICKPOCKET": 70,
                    "IMITATOR": 65,
                    "TECHIE": 65,
                    "HACKER": 66}
            },
            {
                OCName: "Leave No Trace",
                Positions: {
                    "IMITATOR": 66,
                    "NEGOTIATOR": 65,
                    "TECHIE": 65
                }
            },
            {
                OCName: "Counter Offer",
                Positions: {
                    "ROBBER": 66,
                    "ENGINEER": 65,
                    "PICKLOCK": 65,
                    "HACKER": 65,
                    "LOOTER": 65}
            },
            {
                OCName: "Stage Fright",
                Positions: {
                    "SNIPER": 70,
                    "MUSCLE #1": 67,
                    "ENFORCER": 67,
                    "MUSCLE #3": 67,
                    "LOOKOUT": 62,
                    "MUSCLE #2": 1
                }
            }, {
                OCName: "Snow Blind",
                Positions: {
                    "HUSTLER": 70,
                    "IMITATOR": 67,
                    "MUSCLE #1": 60,
                    "MUSCLE #2": 60
                }
            },
            {
                OCName: "Gaslight The Way",
                Positions: {
                    "IMITATOR #1": 64,
                    "IMITATOR #2": 66,
                    "IMITATOR #3": 70,
                    "LOOTER #1": 60,
                    "LOOTER #2": 50,
                    "LOOTER #3": 66}
            },
            {
                OCName: "Pet Project",
                Positions: `default_${defaultLevel2}`
        },
            {
                OCName: "Cash Me If You Can",
                Positions: `default_${defaultLevel2}`
        },
            {
                OCName: "Smoke and Wing Mirrors",
                Positions: {
                    "CAR THIEF": 70,
                    "IMITATOR": 65,
                    "HUSTLER #2": 65,
                    "HUSTLER #1": 62
                }
            },
            {
                OCName: "Best of the Lot",
                Positions: `default_${defaultLevel2}`
        },
            {
                OCName: "Market Forces",
                Positions: {
                    "ENFORCER": 67,
                    "NEGOTIATOR": 67,
                    "MUSCLE": 65,
                    "LOOKOUT": 65,
                    "ARSONIST": 50
                }
            },
            {
                OCName: "Guardian Ángels",
                Positions: {
                    "HUSTLER": 67,
                    "ENGINEER": 65,
                    "ENFORCER": 65
                }
            },
            {
                OCName: "No Reserve",
                Positions: {
                    "TECHIE": 68,
                    "ENGINEER": 68,
                    "CAR THIEF": 68
                }
            },
            {
                OCName: "Manifest Cruelty",
                Positions: `default_${defaultDecline}`
        }
        ];
        /* -----------------------------
       PROCESS PANEL ONCE
    ------------------------------*/

        function processScenario(panel) {
            if (panel.dataset.ocProcessed === "true") return;
            panel.dataset.ocProcessed = "true";

            const ocName = panel.querySelector('.panelTitle___aoGuV')?.innerText.trim();
            if (!ocName) return;

            const slots = panel.querySelectorAll('.wrapper___Lpz_D');

            slots.forEach(slot => {

                if (slot.dataset.ocProcessed === "true") return;
                slot.dataset.ocProcessed = "true";

                const roleElem = slot.querySelector('.title___UqFNy');
                const chanceElem = slot.querySelector('.successChance___ddHsR');
                if (!roleElem || !chanceElem) return;

                const rawRole = roleElem.innerText.trim();
                const successChance = parseInt(chanceElem.textContent.trim(), 10) || 0;
                const joinBtn = slot.querySelector("button[class^='torn-btn joinButton']");

                /* -----------------------------
               DISPLAY ROLE WEIGHT (FROM CACHE)
            ------------------------------*/
                if (roleWeightsAPI) {

                    const cleanOCName = ocName.split("\n")[0].trim();
                    const ocKey = cleanOCName.toLowerCase().replace(/[^a-z0-9\u00C0-\u017F]/gi, "");
                    const roleKey = rawRole.toLowerCase().replace(/[^a-z0-9\u00C0-\u017F]/gi, "");

                    let pct;

                    if (roleWeightsAPI) {

                        const cleanOCName = ocName.split("\n")[0].trim();
                        const ocKeyCompact = cleanOCName
                        .toLowerCase()
                        .replace(/[^a-z0-9\u00C0-\u017F]/gi, "");

                        const roleKeyCompact = rawRole
                        .toLowerCase()
                        .replace(/[^a-z0-9\u00C0-\u017F]/gi, "");

                        // find matching OC
                        const ocEntry = Object.entries(roleWeightsAPI).find(([k]) =>
                                                                            k.toLowerCase() === ocKeyCompact
                                                                           );

                        if (ocEntry) {
                            const roleEntry = Object.entries(ocEntry[1] || {}).find(([k]) =>
                                                                                    k.toLowerCase() === roleKeyCompact
                                                                                   );

                            if (roleEntry && typeof roleEntry[1] === "number") {
                                pct = Math.round(roleEntry[1]);
                            }
                        }

                        if (typeof pct === "number") {

                            slot.classList.add("oc-has-contrib");

                            if (!slot.querySelector(".oc-contrib-linewrap")) {

                                const wrap = document.createElement("div");
                                wrap.className = "oc-contrib-linewrap";

                                const text = document.createElement("span");
                                text.className = "oc-contrib-linetext";
                                text.textContent = pct + "%";

                                wrap.appendChild(text);
                                slot.appendChild(wrap);
                            }
                        }
                    }
                    if (typeof pctRaw === "number") {

                        slot.classList.add("oc-has-contrib");

                        if (!slot.querySelector(".oc-contrib-linewrap")) {

                            const wrap = document.createElement("div");
                            wrap.className = "oc-contrib-linewrap";

                            const text = document.createElement("span");
                            text.className = "oc-contrib-linetext";
                            text.textContent = Math.round(pctRaw) + "%";

                            wrap.appendChild(text);
                            slot.appendChild(wrap);
                        }
                    }
                }

                /* -----------------------------
               YOUR ORIGINAL MIN/MAX LOGIC
               (UNCHANGED)
            ------------------------------*/

                const ocData = ocRoles.find(o => o.OCName.toLowerCase() === ocName.toLowerCase());
                if (!ocData) return;

                let required = null;

                if (typeof ocData.Positions === 'string' && ocData.Positions.startsWith('default_')) {
                    required = parseInt(ocData.Positions.split('_')[1], 10);
                } else if (typeof ocData.Positions === 'object') {
                    required = ocData.Positions[rawRole];
                }

                if (required === undefined || required === null) return;

                let maxAllowed = required + 10;

                if (ocName.toLowerCase() === "stage fright" && rawRole === "MUSCLE #2") {
                    maxAllowed = 70;
                }

                const honorTexts = slot.querySelectorAll('.honor-text');
                const userName = honorTexts.length > 1 ? honorTexts[1].textContent.trim() : null;

                if (!userName) {
                    if (successChance < required) {
                        slot.style.backgroundColor = '#ff000061';
                    } else if (successChance > maxAllowed) {
                        slot.style.backgroundColor = '#F7b500';
                    } else {
                        slot.style.backgroundColor = '#21a61c61';
                    }

                    if (joinBtn) {
                        if (successChance < required || successChance > maxAllowed) {
                            joinBtn.setAttribute('disabled', '');
                        } else {
                            joinBtn.removeAttribute('disabled');
                        }
                    }
                } else if (successChance < required) {
                    slot.classList.add('oc-solid-red');
                }

            });
        }

        /* -----------------------------
       OBSERVER (LIGHTWEIGHT)
    ------------------------------*/

        let lastRun = 0;
        const MIN_INTERVAL = 1000; // adjust (150–300ms ideal)

        const observer = new MutationObserver(() => {
            const now = Date.now();

            if (now - lastRun < MIN_INTERVAL) {
                return; // too soon, skip
            }

            lastRun = now;

            document.querySelectorAll('.wrapper___U2Ap7')
                .forEach(processScenario);
        });

        observer.observe(document.body, { childList: true, subtree: true });
        /* -----------------------------
       24HR CACHE SYSTEM
    ------------------------------*/

        function loadWeights() {

            const cached = localStorage.getItem(CACHE_KEY);
            const cacheTime = localStorage.getItem(CACHE_TIME_KEY);

            if (cached && cacheTime && (Date.now() - cacheTime < CACHE_DURATION)) {
                roleWeightsAPI = JSON.parse(cached);
                return;
            }

            GM_xmlhttpRequest({
                method: "GET",
                url: API_URL,
                onload: (res) => {
                    try {
                        roleWeightsAPI = JSON.parse(res.responseText);

                        localStorage.setItem(CACHE_KEY, JSON.stringify(roleWeightsAPI));
                        localStorage.setItem(CACHE_TIME_KEY, Date.now());
;

                    } catch (e) {
                        console.warn("Failed parsing weights", e);
                    }
                }
            });
        }

        loadWeights();

    })();

    // Crime Morale
    // author      tobytorn [1617955]

    (function () {
        'use strict';

        // Avoid duplicate injection in TornPDA
        if (window.CRIME_MORALE_INJECTED) {
            return;
        }
        window.CRIME_MORALE_INJECTED = true;

        const LOCAL_STORAGE_PREFIX = 'CRIME_MORALE_';
        const STORAGE_MORALE = 'morale';
        const STYLE_ELEMENT_ID = 'CRIME-MORALE-STYLE';

        function getLocalStorage(key, defaultValue) {
            const value = window.localStorage.getItem(LOCAL_STORAGE_PREFIX + key);
            try {
                return JSON.parse(value) ?? defaultValue;
            } catch (err) {
                return defaultValue;
            }
        }

        function setLocalStorage(key, value) {
            window.localStorage.setItem(LOCAL_STORAGE_PREFIX + key, JSON.stringify(value));
        }

        const isPda = window.GM_info?.scriptHandler?.toLowerCase().includes('tornpda');
        const [getValue, setValue] =
              isPda || typeof window.GM_getValue !== 'function' || typeof window.GM_setValue !== 'function'
        ? [getLocalStorage, setLocalStorage]
        : [window.GM_getValue, window.GM_setValue];

        function addStyle(css) {
            const style =
                  document.getElementById(STYLE_ELEMENT_ID) ??
                  (function () {
                      const style = document.createElement('style');
                      style.id = STYLE_ELEMENT_ID;
                      document.head.appendChild(style);
                      return style;
                  })();
            style.appendChild(document.createTextNode(css));
        }

        function formatLifetime(seconds) {
            const hours = Math.floor(seconds / 3600);
            const text =
                  hours >= 72
            ? `${Math.floor(hours / 24)}d`
        : hours > 0
            ? `${hours}h`
        : seconds >= 0
            ? `${Math.floor(seconds / 60)}m`
        : '';
            const color = hours >= 24 ? 't-gray-c' : hours >= 12 ? 't-yellow' : hours >= 0 ? 't-red' : '';
            return { seconds, hours, text, color };
        }

        async function checkDemoralization(data) {
            const demMod = (data.DB || {}).demMod;
            if (typeof demMod !== 'number') {
                return;
            }
            const morale = 100 - demMod;
            updateMorale(morale);
            await setValue(STORAGE_MORALE, morale);
        }

        class BurglaryObserver {
            constructor() {
                this.data = getValue('burglary', {});
                this.data.favorite = this.data.favorite ?? [];
                this.properties = null;
                this.crimeOptions = null;
                this.observer = new MutationObserver((mutations) => {
                    const isAdd = mutations.some((mutation) => {
                        for (const added of mutation.addedNodes) {
                            if (added instanceof HTMLElement) {
                                return true;
                            }
                        }
                        return false;
                    });
                    if (!isAdd) {
                        return;
                    }
                    for (const element of this.crimeOptions) {
                        if (!element.classList.contains('cm-bg-seen')) {
                            element.classList.add('cm-bg-seen');
                            this._refreshCrimeOption(element);
                        }
                    }
                });
            }

            start() {
                if (this.crimeOptions) {
                    return;
                }
                this.crimeOptions = document.body.getElementsByClassName('crime-option');
                this.observer.observe($('.burglary-root')[0], { subtree: true, childList: true });
            }

            stop() {
                this.crimeOptions = null;
                this.observer.disconnect();
            }

            onNewData(data) {
                this.start();
                this.properties = data.DB?.crimesByType?.properties;
                this._refreshCrimeOptions();
            }

            _refreshCrimeOptions() {
                for (const element of this.crimeOptions) {
                    this._refreshCrimeOption(element);
                }
            }

            _refreshCrimeOption(element) {
                if (!this.properties) {
                    return;
                }
                const $element = $(element);
                const $title = $element.find('[class*=crimeOptionSection___]').first();
                $title.find('.cm-bg-lifetime').remove();
                const guessedProperty = this._guessCrimeOptionData($element);
                const property = this._checkCrimeOptionData($element, guessedProperty);
                if (!property) {
                    $element.removeAttr('data-cm-id');
                    return;
                }
                $element.attr('data-cm-id', property.subID);
                const now = Math.floor(Date.now() / 1000);
                const lifetime = formatLifetime(property.expire - now);
                if (lifetime.hours >= 0) {
                    $title.css('position', 'relative');
                    $title.append(`<div class="cm-bg-lifetime ${lifetime.color}">${lifetime.text}</div>`);
                }
                $element.find('.cm-bg-favor').remove();
                const $favor = $('<div class="cm-bg-favor"></div>');
                $favor.toggleClass('cm-bg-active', this.data.favorite.includes(property.title));
                $element.find('.crime-image').append($favor);
                $favor.on('click', () => {
                    this._toggleFavorite(property.title);
                    this._refreshCrimeOptions();
                });
            }

            _guessCrimeOptionData($crimeOption) {
                const savedId = $crimeOption.attr('data-cm-id');
                if (savedId) {
                    return this.properties.find((x) => x.subID === savedId);
                }
                const $item = $crimeOption.closest('.virtual-item');
                if ($item.prev().hasClass('lastOfGroup___YNUeQ')) {
                    return this.properties[0];
                }
                let prevId = undefined;
                $item.prevAll().each(function () {
                    prevId = $(this).find('.crime-option[data-cm-id]').attr('data-cm-id');
                    if (prevId) {
                        return false; // break the loop
                    }
                });
                const prevIndex = this.properties.findIndex((x) => prevId && x.subID === prevId);
                if (prevIndex >= 0) {
                    // Since we always scan crime options in document order,
                    // $prevItemWithId and $item should correspond to adjacent data entries.
                    return this.properties[prevIndex + 1];
                }
                if ($item.index() === 0) {
                    const $nextOptionWithId = $item.nextAll().find('.crime-option[data-cm-id]').first();
                    const nextId = $nextOptionWithId.attr('data-cm-id');
                    const nextIndex = this.properties.findIndex((x) => x.subID && x.subID === nextId);
                    const nextPos = $nextOptionWithId.closest('.virtual-item').index();
                    if (nextIndex >= 0 && nextPos >= 0) {
                        return this.properties[nextIndex - nextPos];
                    }
                }
                return undefined;
            }

            _checkCrimeOptionData($crimeOption, property) {
                if (property === undefined) {
                    return undefined;
                }
                const { title, titleType } = this._getCrimeOptionTitle($crimeOption);
                return titleType && property[titleType] === title ? property : undefined;
            }

            _getCrimeOptionTitle($crimeOption) {
                const mobileTitle = $crimeOption.find('.title___kOWyb').text();
                if (mobileTitle !== '') {
                    return { title: mobileTitle, titleType: 'mobileTitle' };
                }
                const textNode = $crimeOption.find('.crimeOptionSection___hslpu')[0]?.firstChild;
                if (textNode?.nodeType === Node.TEXT_NODE) {
                    return { title: textNode.textContent, titleType: 'title' };
                }
                return { title: null, titleType: null };
            }

            _toggleFavorite(title) {
                const index = this.data.favorite.indexOf(title);
                if (index >= 0) {
                    this.data.favorite.splice(index, 1);
                } else {
                    this.data.favorite.push(title);
                }
                setValue('burglary', this.data);
            }
        }
        const burglaryObserver = new BurglaryObserver();

        async function checkBurglary(crimeType, data) {
            if (crimeType !== '7') {
                burglaryObserver.stop();
                return;
            }
            burglaryObserver.onNewData(data);
        }

        const PP_CYCLING = 0;
        const PP_DISTRACTED = 34; // eslint-disable-line no-unused-vars
        const PP_MUSIC = 102;
        const PP_LOITERING = 136;
        const PP_PHONE = 170;
        const PP_RUNNING = 204;
        const PP_SOLICITING = 238; // eslint-disable-line no-unused-vars
        const PP_STUMBLING = 272;
        const PP_WALKING = 306;
        const PP_BEGGING = 340;

        const PP_SKINNY = 'Skinny';
        const PP_AVERAGE = 'Average';
        const PP_ATHLETIC = 'Athletic';
        const PP_MUSCULAR = 'Muscular';
        const PP_HEAVYSET = 'Heavyset';
        const PP_ANY_BUILD = [PP_SKINNY, PP_AVERAGE, PP_ATHLETIC, PP_MUSCULAR, PP_HEAVYSET];

        const PP_MARKS = {
            'Drunk Man': { level: 1, status: [PP_STUMBLING], build: PP_ANY_BUILD },
            'Drunk Woman': { level: 1, status: [PP_STUMBLING], build: PP_ANY_BUILD },
            'Homeless Person': { level: 1, status: [PP_BEGGING], build: [PP_AVERAGE] },
            Junkie: { level: 1, status: [PP_STUMBLING], build: PP_ANY_BUILD },
            'Elderly Man': { level: 1, status: [PP_WALKING], build: [PP_SKINNY, PP_AVERAGE, PP_ATHLETIC, PP_HEAVYSET] },
            'Elderly Woman': { level: 1, status: [PP_WALKING], build: [PP_SKINNY, PP_AVERAGE, PP_ATHLETIC, PP_HEAVYSET] },

            'Young Man': { level: 2, status: [PP_MUSIC], build: [PP_SKINNY, PP_AVERAGE, PP_ATHLETIC] },
            'Young Woman': { level: 2, status: [PP_PHONE], build: [PP_SKINNY, PP_AVERAGE, PP_HEAVYSET] },
            Student: { level: 2, status: [PP_PHONE], build: [PP_SKINNY, PP_AVERAGE] },
            'Classy Lady': {
                level: 2,
                status: [PP_PHONE, PP_WALKING],
                build: [PP_SKINNY, PP_HEAVYSET],
                bestBuild: [PP_HEAVYSET],
            },
            Laborer: { level: 2, status: [PP_PHONE], build: PP_ANY_BUILD },
            'Postal Worker': { level: 2, status: [PP_WALKING], build: [PP_AVERAGE] },

            'Rich Kid': {
                level: 3,
                status: [PP_WALKING, PP_PHONE],
                build: [PP_SKINNY, PP_ATHLETIC, PP_HEAVYSET],
                bestBuild: [PP_ATHLETIC],
            },
            'Sex Worker': { level: 3, status: [PP_PHONE], build: [PP_SKINNY, PP_AVERAGE], bestBuild: [PP_AVERAGE] },
            Thug: { level: 3, status: [PP_RUNNING], build: [PP_SKINNY, PP_AVERAGE, PP_ATHLETIC], bestBuild: [PP_SKINNY] },

            Businessman: {
                level: 4,
                status: [PP_PHONE],
                build: [PP_AVERAGE, PP_MUSCULAR, PP_HEAVYSET],
                bestBuild: [PP_MUSCULAR, PP_HEAVYSET],
            },
            Businesswoman: {
                level: 4,
                status: [PP_PHONE],
                build: [PP_SKINNY, PP_AVERAGE, PP_ATHLETIC],
                bestBuild: [PP_ATHLETIC],
            },
            'Gang Member': {
                level: 4,
                status: [PP_LOITERING],
                build: [PP_AVERAGE, PP_ATHLETIC, PP_MUSCULAR],
                bestBuild: [PP_AVERAGE],
            },
            Jogger: { level: 4, status: [PP_WALKING], build: [PP_ATHLETIC, PP_MUSCULAR], bestBuild: [PP_MUSCULAR] },
            Mobster: { level: 4, status: [PP_WALKING], build: [PP_SKINNY] },

            Cyclist: { level: 5, status: [PP_CYCLING], build: ['1.52 m', `5'0"`, '1.62 m', `5'4"`] },
            'Police Officer': {
                level: 6,
                status: [PP_RUNNING],
                build: PP_ANY_BUILD,
                bestBuild: [PP_SKINNY, '1.52 m', `5'0"`, '1.62 m', `5'4"`],
            },
        };
        let pickpocketingOb = null;
        let pickpocketingExitOb = null;
        let pickpocketingInterval = 0;

        async function checkPickpocketing(crimeType) {
            if (crimeType !== '5') {
                stopPickpocketing();
                return;
            }
            const $wrapper = $('.pickpocketing-root');
            if ($wrapper.length === 0) {
                if (pickpocketingInterval === 0) {
                    // This is the first fetch.
                    pickpocketingInterval = setInterval(() => {
                        const $wrapperInInterval = $('.pickpocketing-root');
                        if ($wrapperInInterval.length === 0) {
                            return;
                        }
                        clearInterval(pickpocketingInterval);
                        pickpocketingInterval = 0;
                        startPickpocketing($wrapperInInterval);
                    }, 1000);
                }
            } else {
                startPickpocketing($wrapper);
            }
        }

        function refreshPickpocketing() {
            const $wrapper = $('.pickpocketing-root');
            const now = Date.now();
            // Releasing reference to removed elements to avoid memory leak
            pickpocketingExitOb.disconnect();
            let isBelowExiting = false;
            $wrapper.find('.crime-option').each(function () {
                const $this = $(this);
                const top = Math.floor($this.position().top);
                const oldTop = parseInt($this.attr('data-cm-top'));
                if (top !== oldTop) {
                    $this.attr('data-cm-top', top.toString());
                    $this.attr('data-cm-timestamp', now.toString());
                }
                const timestamp = parseInt($this.attr('data-cm-timestamp')) || now;
                const isLocked = $this.is('[class*=locked___]');
                const isExiting = $this.is('[class*=exitActive___]');
                const isRecentlyMoved = now - timestamp <= 1000;
                $this
                    .find('[class*=commitButtonSection___]')
                    .toggleClass('cm-overlay', !isLocked && (isBelowExiting || isRecentlyMoved))
                    .toggleClass('cm-overlay-fade', !isLocked && !isBelowExiting && isRecentlyMoved);
                isBelowExiting = isBelowExiting || isExiting;

                if (!$this.is('[class*=cm-pp-level-]')) {
                    const markAndTime = $this.find('[class*=titleAndProps___] > *:first-child').text().trim().toLowerCase();
                    const iconPosStr = $this.find('[class*=timerCircle___] [class*=icon___]').css('background-position-y');
                    const iconPosMatch = iconPosStr?.match(/(-?\d+)px/);
                    const iconPos = -parseInt(iconPosMatch?.[1] ?? '');
                    const build = $this.find('[class*=physicalProps___]').text().trim().toLowerCase();
                    for (const [mark, markInfo] of Object.entries(PP_MARKS)) {
                        if (markAndTime.startsWith(mark.toLowerCase())) {
                            if (markInfo.status.includes(iconPos) && markInfo.build.some((b) => build.includes(b.toLowerCase()))) {
                                $this.addClass(`cm-pp-level-${markInfo.level}`);
                                if (markInfo.bestBuild?.some((b) => build.includes(b.toLowerCase()))) {
                                    $this.addClass(`cm-pp-best-build`);
                                }
                            }
                            break;
                        }
                    }
                }

                pickpocketingExitOb.observe(this, { attributes: true, attributeFilter: ['class'], attributeOldValue: true });
            });
        }

        function startPickpocketing($wrapper) {
            if (!pickpocketingOb) {
                pickpocketingOb = new MutationObserver(refreshPickpocketing);
                pickpocketingExitOb = new MutationObserver(function (mutations) {
                    for (const mutation of mutations) {
                        if (
                            mutation.oldValue.indexOf('exitActive___') < 0 &&
                            mutation.target.className.indexOf('exitActive___') >= 0
                        ) {
                            refreshPickpocketing();
                            return;
                        }
                    }
                });
            }
            pickpocketingOb.observe($wrapper[0], {
                childList: true,
                characterData: true,
                subtree: true,
            });
        }

        function stopPickpocketing() {
            if (!pickpocketingOb) {
                return;
            }
            pickpocketingOb.disconnect();
            pickpocketingOb = null;
            pickpocketingExitOb.disconnect();
            pickpocketingExitOb = null;
        }

        // Maximize extra exp (capitalization exp - total cost)
        class ScammingSolver {
            get BASE_ACTION_COST() {
                return this.algo === 'meritGrift' ? 0.001 : 0.02;
            }
            get FAILURE_COST_MAP() {
                return this.algo === 'merit' || this.algo === 'meritGrift'
                    ? {
                    1: 0,
                    20: 0,
                    40: 0,
                    60: 0,
                    80: 0,
                }
                : {
                    1: 1,
                    20: 1,
                    40: 1,
                    60: 0.5,
                    80: 0.33,
                };
            }
            get CONCERN_SUCCESS_RATE_MAP() {
                return {
                    'young adult': 0.55,
                    'middle-aged': 0.5,
                    senior: 0.45,
                    professional: 0.4,
                    affluent: 0.35,
                    '': 0.5,
                };
            }
            get CELL_VALUE_MAP() {
                return this.algo === 'merit'
                    ? {
                    low: 2,
                    medium: 2,
                    high: 2,
                    fail: -20,
                }
                : this.algo === 'meritGrift'
                    ? {
                    low: 0,
                    medium: 1,
                    high: 1,
                    fail: 0,
                }
                : {
                    low: 0.5,
                    medium: 1.5,
                    high: 2.5,
                    fail: -20, // The penalty should be -10. I add a bit to it for demoralization and chain bonus lost.
                };
            }
            get SAFE_CELL_SET() {
                return new Set(['neutral', 'low', 'medium', 'high', 'temptation']);
            }
            get DISPLACEMENT() {
                // prettier-ignore
                return {
                    1: {
                        strong: [[10, 19], [15, 29], [18, 35], [21, 39], [22, 42], [23, 44]],
                        soft: [[3, 7], [5, 11], [6, 13], [6, 14], [7, 15], [7, 16]],
                        back: [[-4, -2], [-6, -3], [-7, -4], [-8, -4], [-9, -4], [-9, -5]],
                    },
                    20: {
                        strong: [[8, 15], [12, 23], [15, 28], [16, 31], [18, 33], [18, 35]],
                        soft: [[3, 7], [5, 11], [6, 13], [6, 14], [7, 15], [7, 16]],
                        back: [[-4, -2], [-6, -3], [-7, -4], [-8, -4], [-9, -4], [-9, -5]],
                    },
                    40: {
                        strong: [[7, 13], [11, 20], [13, 24], [14, 27], [15, 29], [16, 30]],
                        soft: [[3, 6], [5, 9], [6, 11], [6, 12], [7, 13], [7, 14]],
                        back: [[-4, -2], [-6, -3], [-7, -4], [-8, -4], [-9, -4], [-9, -5]],
                    },
                    60: {
                        strong: [[6, 11], [9, 17], [11, 20], [12, 23], [13, 24], [14, 25]],
                        soft: [[2, 4], [3, 6], [4, 7], [4, 8], [4, 9], [5, 9]],
                        back: [[-4, -2], [-6, -3], [-7, -4], [-8, -4], [-9, -4], [-9, -5]],
                    },
                    80: {
                        strong: [[5, 9], [8, 14], [9, 17], [10, 19], [11, 20], [12, 21]],
                        soft: [[2, 3], [3, 5], [4, 6], [4, 6], [4, 7], [5, 7]],
                        back: [[-3, -2], [-5, -3], [-6, -4], [-6, -4], [-7, -4], [-7, -5]],
                    },
                };
            }
            get MERIT_MASK_MAP() {
                return {
                    temptation: 1n << 50n,
                    sensitivity: 1n << 51n,
                    hesitation: 1n << 52n,
                    concern: 1n << 53n,
                };
            }
            get MERIT_REQUIREMENT_MASK() {
                return 0xfn << 50n;
            }

            /**
     * @param {'exp' | 'merit' | 'meritGrift'} algo
     * @param {('neutral' | 'low' | 'medium' | 'high' | 'temptation' | 'sensitivity' | 'hesitation' | 'concern' | 'fail')[]} bar
     * @param {1 | 20 | 40 | 60 | 80} targetLevel
     * @param {number} round
     * @param {number} suspicion
     * @param {'young adult' | 'middle-aged' | 'senior' | 'professional' | 'affluent' | ''} mark
     */
            constructor(algo, bar, targetLevel, round, suspicion, mark) {
                this.algo = algo;
                this.bar = bar;
                this.targetLevel = targetLevel;
                this.failureCost = this.FAILURE_COST_MAP[this.targetLevel];
                this.initialRound = round;
                this.initialSuspicion = suspicion;
                this.mark = mark;

                this.driftArrayMap = new Map(); // (resolvingBitmap) => number[50]
                this.dp = new Map(); // (resolvingBitmap | round) => {value: number, action: string, multi: number}[50]

                this.resolvingMasks = new Array(50);
                for (let pip = 0; pip < 50; pip++) {
                    if (this.resolvingMasks[pip]) {
                        continue;
                    }
                    if (this.bar[pip] !== 'hesitation' && this.bar[pip] !== 'concern') {
                        this.resolvingMasks[pip] = 0n;
                        continue;
                    }
                    let mask = this.algo === 'merit' ? this.MERIT_MASK_MAP[this.bar[pip]] : 0n;
                    for (let endPip = pip; endPip < 50 && this.bar[endPip] === this.bar[pip]; endPip++) {
                        mask += 1n << BigInt(endPip);
                    }
                    for (let endPip = pip; endPip < 50 && this.bar[endPip] === this.bar[pip]; endPip++) {
                        this.resolvingMasks[endPip] = mask;
                    }
                }
            }

            /**
     * @param {number} driftBitmap 1 for temptation triggered, 2 for sensitivity triggered
     */
            solve(round, pip, resolvingBitmap, multiplierUsed, driftBitmap) {
                if (this.algo === 'merit') {
                    for (let pip = 0; pip < 50; pip++) {
                        if (this._isResolved(pip, resolvingBitmap)) {
                            resolvingBitmap |= this.MERIT_MASK_MAP[this.bar[pip]] ?? 0n;
                        }
                    }
                    resolvingBitmap |= BigInt(driftBitmap) << 50n;
                }
                const result = this._visit(round - multiplierUsed, resolvingBitmap, multiplierUsed, pip);
                return result[pip];
            }

            /**
     * @param {number} round
     * @param {bigint} resolvingBitmap
     * @param {number} minMulti
     * @param {number | undefined} singlePip
     */
            _visit(round, resolvingBitmap, minMulti, singlePip = undefined) {
                const dpKey = BigInt(round) | (resolvingBitmap << 6n);
                // Cached solutions do not respect `minMulti`.
                if (minMulti === 0) {
                    const visited = this.dp.get(dpKey);
                    if (visited) {
                        return visited;
                    }
                }
                const result = new Array(50);
                this.dp.set(dpKey, result);
                if (this._estimateSuspicion(round) >= 50) {
                    for (let pip = 0; pip < 50; pip++) {
                        result[pip] = this._getCellResult(pip, resolvingBitmap);
                    }
                    return result;
                }
                const driftArray = this._getDriftArray(resolvingBitmap);
                const [pipBegin, pipEnd] = singlePip !== undefined ? [singlePip, singlePip + 1] : [0, 50];
                for (let pip = pipBegin; pip < pipEnd; pip++) {
                    const best = this._getCellResult(pip, resolvingBitmap);
                    if (this.bar[pip] === 'fail') {
                        result[pip] = best;
                        continue;
                    }
                    if (!this._isResolved(pip, resolvingBitmap)) {
                        if (this.bar[pip] === 'hesitation') {
                            const resolvedResult = this._visit(round, resolvingBitmap | this.resolvingMasks[pip], 0);
                            result[pip] = resolvedResult[pip];
                            continue;
                        }
                        if (this.bar[pip] === 'concern') {
                            const resolvedResult = this._visit(round + 1, resolvingBitmap | this.resolvingMasks[pip], 0);
                            const unresolvedResult = this._visit(round + 1, resolvingBitmap, 0);
                            const concernSuccessRate = this.CONCERN_SUCCESS_RATE_MAP[this.mark] ?? this.CONCERN_SUCCESS_RATE_MAP[''];
                            const value =
                                  resolvedResult[pip].value * concernSuccessRate +
                                  (unresolvedResult[pip].value - this.failureCost) * (1 - concernSuccessRate) -
                                  this.BASE_ACTION_COST;
                            result[pip] = {
                                value: Math.max(0, value),
                                action: value > 0 ? 'resolve' : 'abandon',
                                multi: 0,
                            };
                            continue;
                        }
                    }
                    for (let multi = minMulti; multi <= 5; multi++) {
                        const suspicionAfterMulti = this._estimateSuspicion(round + multi);
                        const nextRoundResult = this._visit(round + multi + 1, resolvingBitmap, 0);
                        const feasibleActions = pip > 0 ? ['strong', 'soft', 'back'] : ['strong', 'soft'];
                        for (const action of feasibleActions) {
                            const displacementArray = this.DISPLACEMENT[this.targetLevel.toString()]?.[action]?.[multi];
                            if (!displacementArray) {
                                continue;
                            }
                            const [minDisplacement, maxDisplacement] = displacementArray;
                            let totalValue = 0;
                            for (let disp = minDisplacement; disp <= maxDisplacement; disp++) {
                                const landingPip = Math.max(Math.min(pip + disp, 49), 0);
                                const newPip = driftArray[landingPip];
                                if (landingPip < suspicionAfterMulti || newPip < suspicionAfterMulti) {
                                    totalValue += this.CELL_VALUE_MAP.fail;
                                } else {
                                    if (!this.SAFE_CELL_SET.has(this.bar[landingPip]) && !this._isResolved(landingPip, resolvingBitmap)) {
                                        totalValue -= this.failureCost;
                                    }
                                    totalValue -= this.BASE_ACTION_COST;
                                    const landingResult =
                                          this.algo === 'merit' && newPip !== landingPip
                                    ? this._visit(round + multi + 1, resolvingBitmap | this.MERIT_MASK_MAP[this.bar[landingPip]], 0)
                                    : nextRoundResult;
                                    totalValue += landingResult[newPip].value;
                                }
                            }
                            const avgValue = totalValue / (maxDisplacement - minDisplacement + 1) - this.BASE_ACTION_COST * multi;
                            if (avgValue > best.value) {
                                best.value = avgValue;
                                best.action = action;
                                best.multi = multi;
                            }
                        }
                    }
                    result[pip] = best;
                }
                return result;
            }

            _getDriftArray(resolvingBitmap) {
                const cached = this.driftArrayMap.get(resolvingBitmap);
                if (cached) {
                    return cached;
                }
                const driftArray = new Array(50);
                this.driftArrayMap.set(resolvingBitmap, driftArray);
                for (let pip = 0; pip < 50; pip++) {
                    let newPip = pip;
                    switch (this.bar[pip]) {
                        case 'temptation':
                            while (
                                newPip + 1 < 50 &&
                                (!this.SAFE_CELL_SET.has(this.bar[newPip]) || this.bar[newPip] === 'temptation') &&
                                !this._isResolved(newPip, resolvingBitmap)
                            ) {
                                newPip++;
                            }
                            break;
                        case 'sensitivity':
                            while (newPip > 0 && this.bar[newPip] !== 'neutral' && !this._isResolved(newPip, resolvingBitmap)) {
                                newPip--;
                            }
                            break;
                    }
                    driftArray[pip] = newPip;
                }
                return driftArray;
            }

            _getCellResult(pip, resolvingBitmap) {
                let value = this.CELL_VALUE_MAP[this.bar[pip]] ?? 0;
                if (this.algo === 'merit' && (resolvingBitmap & this.MERIT_REQUIREMENT_MASK) !== this.MERIT_REQUIREMENT_MASK) {
                    value = Math.min(value, 0);
                }
                const action = this.bar[pip] === 'fail' ? 'fail' : value > 0 ? 'capitalize' : 'abandon';
                return { value, action, multi: 0 };
            }

            _estimateSuspicion(round) {
                if (round <= this.initialRound) {
                    return this.initialSuspicion;
                }
                const predefined = [0, 0, 0, 0, 2, 5, 8, 11, 16, 23, 34, 50][round] ?? 50;
                const current = Math.floor(this.initialSuspicion * 1.5 ** (round - this.initialRound));
                return Math.max(predefined, current);
            }

            _isResolved(pip, resolvingBitmap) {
                return ((1n << BigInt(pip)) & resolvingBitmap) !== 0n;
            }
        }

        class ScammingStore {
            get TARGET_LEVEL_MAP() {
                return {
                    'delivery scam': 1,
                    'family scam': 1,
                    'prize scam': 1,
                    'charity scam': 20,
                    'tech support scam': 20,
                    'vacation scam': 40,
                    'tax scam': 40,
                    'advance-fee scam': 60,
                    'job scam': 60,
                    'romance scam': 80,
                    'investment scam': 80,
                };
            }
            get SPAM_ID_MAP() {
                return {
                    295: 'delivery',
                    293: 'family',
                    291: 'prize',
                    297: 'charity',
                    299: 'tech support',
                    301: 'vacation',
                    303: 'tax',
                    305: 'advance-fee',
                    307: 'job',
                    309: 'romance',
                    311: 'investment',
                };
            }
            constructor() {
                this.data = getValue('scamming', {});
                this.data.targets = this.data.targets ?? {};
                this.data.farms = this.data.farms ?? {};
                this.data.spams = this.data.spams ?? {};
                this.data.defaultAlgo = this.data.defaultAlgo ?? 'exp';
                this.data.algoNotice = this.data.algoNotice ?? {};
                this.unsyncedSet = new Set(Object.keys(this.data.targets));
                this.solvers = {};
                this.lastSolutions = {};
                this.cash = undefined;
            }

            update(data) {
                this._updateTargets(data.DB?.crimesByType?.targets);
                this._updateFarms(data.DB?.additionalInfo?.currentOngoing);
                this._updateSpams(data.DB?.currentUserStats?.crimesByIDAttempts, data.DB?.crimesByType?.methods);
                this.cash = data.DB?.user?.money;
                this._save();
            }

            setDefaultAlgo(algo) {
                this.data.defaultAlgo = algo;
                this._save();
            }

            changeAlgo(target) {
                target.algos.push(target.algos.shift());
                target.solution = null;
                this._solve(target);
                this._save();
            }

            setAlgoNoticeRead(algo) {
                this.data.algoNotice[algo] = true;
                this._save();
            }

            _save() {
                setValue('scamming', this.data);
            }

            _updateTargets(targets) {
                if (!targets) {
                    return;
                }
                for (const target of targets) {
                    const stored = this.data.targets[target.subID];
                    if (stored && !target.new && target.bar) {
                        stored.driftBitmap = stored.driftBitmap ?? 0; // data migration for v1.4.6
                        stored.turns = stored.turns ?? target.turns ?? 0; // data migration for v1.4.10
                        stored.mark = (target.target ?? '').toLowerCase();
                        let updated = false;
                        if (
                            stored.multiplierUsed !== target.multiplierUsed ||
                            stored.pip !== target.pip ||
                            stored.turns !== (target.turns ?? 0)
                        ) {
                            stored.multiplierUsed = target.multiplierUsed;
                            stored.pip = target.pip;
                            stored.turns = target.turns ?? 0;
                            stored.expire = target.expire;
                            updated = true;
                        }
                        if (updated && this.unsyncedSet.has(stored.id)) {
                            stored.unsynced = true; // replied on another device
                        }
                        this.unsyncedSet.delete(stored.id);
                        if (stored.bar) {
                            for (let pip = 0; pip < 50; pip++) {
                                if (target.bar[pip] === stored.bar[pip]) {
                                    continue;
                                }
                                if (target.bar[pip] === 'fail' && stored.suspicion <= pip) {
                                    stored.suspicion = pip + 1;
                                    updated = true;
                                }
                                if (target.bar[pip] === 'neutral' && (BigInt(stored.resolvingBitmap) & (1n << BigInt(pip))) === 0n) {
                                    stored.resolvingBitmap = (BigInt(stored.resolvingBitmap) | (1n << BigInt(pip))).toString();
                                    updated = true;
                                }
                            }
                            if (target.firstPip) {
                                if (stored.bar[target.firstPip] === 'temptation') {
                                    stored.driftBitmap |= 1;
                                }
                                if (stored.bar[target.firstPip] === 'sensitivity') {
                                    stored.driftBitmap |= 2;
                                }
                            }
                        }
                        if (updated) {
                            // Round is not accurate for concern and hesitation.
                            stored.round = stored.unsynced ? this._estimateRound(target) : stored.round + 1;
                        }
                        if (!stored.bar) {
                            stored.bar = target.bar;
                            updated = true;
                        }
                        if (updated || !stored.solution) {
                            this._solve(stored);
                        }
                    } else {
                        const multiplierUsed = target.multiplierUsed ?? 0;
                        const pip = target.pip ?? 0;
                        const round = multiplierUsed === 0 && pip === 0 ? 0 : Math.max(1, multiplierUsed);
                        const stored = {
                            id: target.subID,
                            email: target.email,
                            level: this.TARGET_LEVEL_MAP[target.scamMethod.toLowerCase()] ?? 999,
                            mark: '',
                            round,
                            turns: target.turns ?? 0,
                            multiplierUsed,
                            pip,
                            expire: target.expire,
                            bar: target.bar ?? null,
                            suspicion: 0,
                            resolvingBitmap: '0',
                            driftBitmap: 0,
                            algos: null,
                            solution: null,
                            unsynced: round > 0,
                        };
                        this.data.targets[target.subID] = stored;
                        this._solve(stored);
                    }
                }
                const now = Math.floor(Date.now() / 1000);
                for (const target of Object.values(this.data.targets)) {
                    if (target.expire < now) {
                        delete this.data.targets[target.id];
                    }
                }
            }

            _updateFarms(currentOngoing) {
                if (typeof currentOngoing !== 'object' || !(currentOngoing.length > 0)) {
                    return;
                }
                for (const item of currentOngoing) {
                    if (!item.type) {
                        continue;
                    }
                    this.data.farms[item.type] = { expire: item.timeEnded };
                }
            }

            _updateSpams(crimesByIDAttempts, methods) {
                if (!crimesByIDAttempts || !methods) {
                    return;
                }
                const now = Math.floor(Date.now() / 1000);
                for (const [id, count] of Object.entries(crimesByIDAttempts)) {
                    const type = this.SPAM_ID_MAP[id];
                    const method = methods.find((x) => String(x.crimeID) === id);
                    if (!type || !method) {
                        continue;
                    }
                    const stored = this.data.spams[id];
                    if (stored) {
                        if (count !== stored.count) {
                            stored.count = count;
                            stored.accurate = now - stored.ts < 3600;
                            stored.since = now;
                        }
                        stored.ts = now;
                        stored.depreciation = method.depreciation;
                    } else {
                        this.data.spams[id] = {
                            count,
                            accurate: false,
                            since: null,
                            ts: now,
                            depreciation: method.depreciation,
                        };
                    }
                }
            }

            _solve(target) {
                if (!target.bar) {
                    return;
                }
                this.lastSolutions[target.id] = target.solution;
                let solver = this.solvers[target.id];
                if (!solver || solver.algo !== target.algos?.[0] || target.suspicion > 0) {
                    if (!target.algos) {
                        target.algos = ['exp'];
                        if (this._isDecepticonFeasible(target)) {
                            target.algos.push('merit');
                        }
                        if (this._isGriftHorseFeasible(target)) {
                            target.algos.push('meritGrift');
                        }
                        const defaultIndex = target.algos.indexOf(this.data.defaultAlgo);
                        if (defaultIndex > 0) {
                            target.algos = [...target.algos.slice(defaultIndex), ...target.algos.slice(0, defaultIndex)];
                        }
                    }
                    solver = new ScammingSolver(
                        target.algos[0],
                        target.bar,
                        target.level,
                        target.round,
                        target.suspicion,
                        target.mark,
                    );
                    this.solvers[target.id] = solver;
                }
                target.solution = solver.solve(
                    target.round,
                    target.pip,
                    BigInt(target.resolvingBitmap),
                    target.multiplierUsed,
                    target.driftBitmap,
                );
            }

            _estimateRound(target) {
                // This "turns" from the server gets +2 from temptation and sensitivity (round +1 in these cases) and
                // gets +1 from hesitation (round +2 in this case).
                // The "*Attempt" fields from the server are at most 1 even with multiple attempts.
                return Math.max(
                    0,
                    (target.turns ?? 0) -
                    (target.temptationAttempt ?? 0) -
                    (target.sensitivityAttempt ?? 0) +
                    (target.hesitationAttempt ?? 0),
                );
            }

            _isDecepticonFeasible(target) {
                const cells = new Set(target.bar);
                return cells.has('temptation') && cells.has('sensitivity') && cells.has('hesitation') && cells.has('concern');
            }

            _isGriftHorseFeasible(target) {
                return target.mark === 'affluent';
            }
        }

        class ScammingObserver {
            constructor() {
                this.store = new ScammingStore();
                this.crimeOptions = null;
                this.farmIcons = null;
                this.spamOptions = null;
                this.virtualLists = null;
                this.observer = new MutationObserver((mutations) => {
                    const isAdd = mutations.some((mutation) => {
                        for (const added of mutation.addedNodes) {
                            if (added instanceof HTMLElement) {
                                return true;
                            }
                        }
                        return false;
                    });
                    if (!isAdd) {
                        return;
                    }
                    for (const element of this.crimeOptions) {
                        if (!element.classList.contains('cm-sc-seen')) {
                            element.classList.add('cm-sc-seen');
                            this._refreshCrimeOption(element);
                        }
                    }
                    for (const element of this.farmIcons) {
                        if (!element.classList.contains('cm-sc-seen')) {
                            element.classList.add('cm-sc-seen');
                            this._refreshFarm(element);
                        }
                    }
                    for (const element of this.spamOptions) {
                        if (!element.classList.contains('cm-sc-seen')) {
                            element.classList.add('cm-sc-seen');
                            this._refreshSpam(element);
                        }
                    }
                    for (const element of this.virtualLists) {
                        if (!element.classList.contains('cm-sc-seen')) {
                            element.classList.add('cm-sc-seen');
                            this._refreshSettings(element);
                        }
                    }
                });
            }

            start() {
                if (this.crimeOptions) {
                    return;
                }
                this.crimeOptions = document.body.getElementsByClassName('crime-option');
                this.farmIcons = document.body.getElementsByClassName('scraperPhisher___oy1Wn');
                this.spamOptions = document.body.getElementsByClassName('optionWithLevelRequirement___cHH35');
                this.virtualLists = document.body.getElementsByClassName('virtualList___noLef');
                this.observer.observe($('.scamming-root')[0], { subtree: true, childList: true });
            }

            stop() {
                this.crimeOptions = null;
                this.observer.disconnect();
            }

            onNewData() {
                this.start();
                for (const element of this.crimeOptions) {
                    this._refreshCrimeOption(element);
                }
                for (const element of this.farmIcons) {
                    this._refreshFarm(element);
                }
                for (const element of this.spamOptions) {
                    this._refreshSpam(element);
                }
            }

            _buildHintHtml(target, solution, lastSolution, showGriftNotice) {
                const actionText =
                      {
                          strong: 'Fast Fwd',
                          soft: 'Soft Fwd',
                          back: 'Back',
                          capitalize: '$$$',
                          abandon: 'Abandon',
                          resolve: 'Resolve',
                      }[solution.action] ?? 'N/A';
                const algo = target.algos?.[0];
                const algoText =
                      {
                          exp: 'Exp',
                          merit: 'Decep',
                          meritGrift: 'Grift',
                      }[algo] ?? 'Score';
                const score = Math.floor(solution.value * 100);
                const scoreText = `${score}${algo === 'meritGrift' ? '%' : ''}`;
                let scoreColor = '';
                if (algo === 'meritGrift') {
                    scoreColor = score < 30 ? 't-red' : score < 60 ? 't-yellow' : 't-green';
                } else {
                    scoreColor = score < 30 ? 't-red' : score < 100 ? 't-yellow' : 't-green';
                }
                const scoreDiff = lastSolution ? score - Math.floor(lastSolution.value * 100) : 0;
                const scoreDiffColor = scoreDiff > 0 ? 't-green' : 't-red';
                const scoreDiffText = scoreDiff !== 0 ? `(${scoreDiff > 0 ? '+' : ''}${scoreDiff})` : '';
                let rspText = solution.multi > target.multiplierUsed ? 'Accel' : actionText;
                let rspColor = '';
                let fullRspText = solution.multi > 0 ? `(${target.multiplierUsed}/${solution.multi} + ${actionText})` : '';
                if (target.unsynced) {
                    rspText = 'Unsynced';
                    rspColor = 't-gray-c';
                    fullRspText = fullRspText !== '' ? fullRspText : `(${actionText})`;
                }
                const $wrapper = $('<span class="cm-sc-info cm-sc-hint cm-sc-hint-content"></span>');
                if (showGriftNotice) {
                    $wrapper.append(`<span><span class="cm-sc-algo">${algoText}</span></span>`);
                    $wrapper.append('<span class="cm-sc-notice t-blue">Click to read about this strategy</span>');
                    $wrapper.children('.cm-sc-notice').on('click', () => {
                        const msg =
                              'Warning: The "Grift Horse" strategy is highly aggressive and does NOT avoid critical failures. ' +
                              'You may lose a significant amount of crime experience.\n\n' +
                              'Click OK to proceed with this risky strategy, or Cancel to choose a safer alternative.';
                        if (confirm(msg)) {
                            this.store.setAlgoNoticeRead(algo);
                            location.reload();
                        }
                    });
                } else {
                    $wrapper.append(
                        `<span><span class="cm-sc-algo">${algoText}</span>: <span class="${scoreColor}">${scoreText}</span><span class="${scoreDiffColor}">${scoreDiffText}</span></span>`,
                    );
                    $wrapper.append(
                        `<span class="cm-sc-hint-action"><span class="${rspColor}">${rspText}</span> <span class="t-gray-c">${fullRspText}</span></span>`,
                    );
                }
                $wrapper.append(`<span class="cm-sc-hint-button t-blue">Lv${target.level}</span>`);
                return $wrapper;
            }

            _refreshCrimeOption(element) {
                this._refreshTarget(element);
                this._refreshFarmButton(element);
            }

            _refreshTarget(element) {
                const $crimeOption = $(element);
                const $email = $crimeOption.find('span.email___gVRXx');
                const email = $email.text();
                const target = Object.values(this.store.data.targets).find((x) => x.email === email);
                if (!target) {
                    return;
                }
                // clear old info elements
                const hasHint = $crimeOption.find('.cm-sc-hint-content').length > 0;
                $crimeOption.find('.cm-sc-info').remove();
                $email.parent().addClass('cm-sc-info-wrapper');
                $email.parent().children().addClass('cm-sc-orig-info');
                // hint
                const solution = target.solution;
                if (solution) {
                    if (!hasHint) {
                        $email.parent().removeClass('cm-sc-hint-hidden');
                    }
                    const algo = target.algos?.[0];
                    const showGriftNotice = algo === 'meritGrift' && !this.store.data.algoNotice[algo];
                    const actionAttr = showGriftNotice
                    ? ''
                    : solution.multi > target.multiplierUsed
                    ? 'accelerate'
                    : solution.action;
                    $crimeOption.attr('data-cm-action', actionAttr);
                    $crimeOption.toggleClass('cm-sc-unsynced', !showGriftNotice && (target.unsynced ?? false));
                    const lastSolution = this.store.lastSolutions[target.id];
                    $email.parent().append(this._buildHintHtml(target, solution, lastSolution, showGriftNotice));
                    $email.parent().append(`<span class="cm-sc-info cm-sc-orig-info cm-sc-hint-button t-blue">Hint</div>`);
                    $crimeOption.find('.cm-sc-hint-button').on('click', () => {
                        $email.parent().toggleClass('cm-sc-hint-hidden');
                    });
                    if (target.algos?.length > 1) {
                        const $algo = $crimeOption.find('.cm-sc-algo');
                        $algo.addClass('t-blue');
                        $algo.addClass('cm-sc-active');
                        $algo.on('click', () => {
                            this.store.changeAlgo(target);
                            this._refreshTarget(element);
                        });
                    }
                } else {
                    $email.parent().addClass('cm-sc-hint-hidden');
                }
                // lifetime
                const now = Math.floor(Date.now() / 1000);
                const lifetime = formatLifetime(target.expire - now);
                $email.before(`<span class="cm-sc-info ${lifetime.color}">${lifetime.text}</div>`);
                // scale
                const $cells = $crimeOption.find('.cell___AfwZm');
                if ($cells.length >= 50) {
                    $cells.find('.cm-sc-scale').remove();
                    // Ignore cells after the first 50, which are faded out soon
                    for (let i = 0; i < 50; i++) {
                        const dist = i - target.pip;
                        const label = dist % 5 !== 0 || dist === 0 || dist < -5 ? '' : dist % 10 === 0 ? (dist / 10).toString() : "'";
                        let $scale = $cells.eq(i).children('.cm-sc-scale');
                        if ($scale.length === 0) {
                            $scale = $('<div class="cm-sc-scale"></div>');
                            $cells.eq(i).append($scale);
                        }
                        $scale.text(label);
                    }
                }
                // multiplier
                const $accButton = $crimeOption.find('.response-type-button').eq(3);
                $accButton.find('.cm-sc-multiplier').remove();
                if (target.multiplierUsed > 0) {
                    $accButton.append(`<div class="cm-sc-multiplier">${target.multiplierUsed}</div>`);
                }
            }

            _refreshFarmButton(element) {
                const $element = $(element);
                if ($element.find('.emailAddresses___ky_qG').length === 0) {
                    return;
                }
                $element.find('.commitButtonSection___wJfnI button').toggleClass('cm-sc-low-cash', this.store.cash < 10000);
            }

            _refreshFarm(element) {
                const $element = $(element);
                const label = $element.attr('aria-label') ?? '';
                const farm = Object.entries(this.store.data.farms).find(([type]) => label.toLowerCase().includes(type))?.[1];
                if (!farm) {
                    return;
                }
                const now = Math.floor(Date.now() / 1000);
                const lifetime = formatLifetime(farm.expire - now);
                $element.find('.cm-sc-farm-lifetime').remove();
                $element.append(`<div class="cm-sc-farm-lifetime ${lifetime.color}">${lifetime.text}</div>`);
            }

            _refreshSpam(element) {
                const $spamOption = $(element);
                if ($spamOption.closest('.dropdownList').length === 0) {
                    return;
                }
                const label = $spamOption
                .contents()
                .filter((_, x) => x.nodeType === Node.TEXT_NODE)
                .text();
                const spam = Object.entries(this.store.data.spams).find(([id]) =>
                                                                        label.toLowerCase().includes(this.store.SPAM_ID_MAP[id]),
                                                                       )?.[1];
                $spamOption.addClass('cm-sc-spam-option');
                $spamOption.find('.cm-sc-spam-elapsed').remove();
                if (!spam || !spam.since || spam.depreciation) {
                    return;
                }
                const now = Math.floor(Date.now() / 1000);
                const elapsed = formatLifetime(now - spam.since);
                if (!spam.accurate) {
                    elapsed.text = '> ' + elapsed.text;
                }
                if (elapsed.hours >= 24 * 8) {
                    elapsed.text = '> 7d';
                }
                if (elapsed.hours >= 24 && elapsed.hours < 72) {
                    elapsed.color = 't-green';
                }
                $spamOption.append(`<div class="cm-sc-spam-elapsed ${elapsed.color}">${elapsed.text}</div>`);
            }

            _refreshSettings(element) {
                const store = this.store;
                const defaultAlgo = store.data.defaultAlgo;
                const $settings = $(`<div class="cm-sc-settings">
        <span>Default Strategy:</span>
        <span class="cm-sc-algo-option t-blue" data-cm-value="exp">Exp</span>
        <span class="cm-sc-algo-option t-blue" data-cm-value="merit">Decepticon</span>
        <span class="cm-sc-algo-option t-blue" data-cm-value="meritGrift">Grift Horse</span>
      </div>`);
                $settings.children(`[data-cm-value="${defaultAlgo}"]`).addClass('cm-sc-active');
                $settings.children('.cm-sc-algo-option').on('click', function () {
                    const $this = $(this);
                    store.setDefaultAlgo($this.attr('data-cm-value'));
                    $this.siblings().removeClass('cm-sc-active');
                    $this.addClass('cm-sc-active');
                });
                $settings.insertBefore(element);
            }
        }
        const scammingObserver = new ScammingObserver();

        async function checkScamming(crimeType, data) {
            if (crimeType !== '12') {
                scammingObserver.stop();
                return;
            }
            scammingObserver.store.update(data);
            scammingObserver.onNewData();
        }

        async function onCrimeData(crimeType, data) {
            await checkDemoralization(data);
            await checkBurglary(crimeType, data);
            await checkPickpocketing(crimeType);
            await checkScamming(crimeType, data);
        }

        function interceptFetch() {
            const targetWindow = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
            const origFetch = targetWindow.fetch;
            targetWindow.fetch = async (...args) => {
                const rsp = await origFetch(...args);

                try {
                    const url = new URL(args[0], location.origin);
                    const params = new URLSearchParams(url.search);
                    const reqBody = args[1]?.body;
                    const crimeType = params.get('typeID') ?? reqBody?.get('typeID');
                    if (url.pathname === '/page.php' && params.get('sid') === 'crimesData' && crimeType) {
                        const clonedRsp = rsp.clone();
                        await onCrimeData(crimeType, await clonedRsp.json());
                    }
                } catch {
                    // ignore
                }

                return rsp;
            };
        }

        function renderMorale() {
            const interval = setInterval(async function () {
                if (!$) {
                    return; // JQuery is not loaded in TornPDA yet
                }
                const $container = $('.crimes-app-header');
                if ($container.length === 0) {
                    return;
                }
                clearInterval(interval);
                $container.append(`<span>Morale: <span id="crime-morale-value">-</span>%</span>`);
                const morale = parseInt(await getValue(STORAGE_MORALE));
                if (!isNaN(morale)) {
                    updateMorale(morale);
                }
                // Show hidden debug button on double-click
                let lastClick = 0; // dblclick event doesn't work well on mobile
                $('#crime-morale-value')
                    .parent()
                    .on('click', function () {
                    if (Date.now() - lastClick > 1000) {
                        lastClick = Date.now();
                        return;
                    }
                    const data = {
                        morale: getValue(STORAGE_MORALE),
                        burglary: getValue('burglary'),
                        scamming: getValue('scamming'),
                    };
                    const export_uri = `data:application/json;charset=utf-8,${encodeURIComponent(JSON.stringify(data))}`;
                    $(this).replaceWith(`<a download="crime-morale-debug.json" href="${export_uri}"
            class="torn-btn" style="display:inline-block;">Export Debug Data</a>`);
                });
            }, 500);
        }

        function updateMorale(morale) {
            $('#crime-morale-value').text(morale.toString());
        }

        function renderStyle() {
            addStyle(`
      .cm-bg-lifetime {
        position: absolute;
        top: 0;
        right: 0;
        padding: 2px;
        background: var(--default-bg-panel-color);
        border: 1px solid darkgray;
      }
      .cm-bg-favor {
        position: absolute;
        right: 0;
        bottom: 0;
        background: #fffc;
        height: 20px;
        width: 20px;
        font-size: 20px;
        line-height: 1;
        cursor: pointer;
        pointer-events: auto !important;
      }
      .cm-bg-favor:after {
        content: '\u2606';
        display: block;
        width: 100%;
        height: 100%;
        text-align: center;
      }
      .cm-bg-favor.cm-bg-active:after {
        content: '\u2605';
        color: orange;
      }

      :root {
        --cm-pp-level-1: #37b24d;
        --cm-pp-level-2: #95af14;
        --cm-pp-level-3: #f4cc00;
        --cm-pp-level-4: #fa9201;
        --cm-pp-level-5: #e01111;
        --cm-pp-level-6: #a016eb;
        --cm-pp-filter-level-1: brightness(0) saturate(100%) invert(61%) sepia(11%) saturate(2432%) hue-rotate(79deg) brightness(91%) contrast(96%);
        --cm-pp-filter-level-2: brightness(0) saturate(100%) invert(62%) sepia(80%) saturate(2102%) hue-rotate(32deg) brightness(99%) contrast(84%);
        --cm-pp-filter-level-3: brightness(0) saturate(100%) invert(71%) sepia(53%) saturate(1820%) hue-rotate(9deg) brightness(107%) contrast(102%);
        --cm-pp-filter-level-4: brightness(0) saturate(100%) invert(61%) sepia(62%) saturate(1582%) hue-rotate(356deg) brightness(94%) contrast(108%);
        --cm-pp-filter-level-5: brightness(0) saturate(100%) invert(12%) sepia(72%) saturate(5597%) hue-rotate(354deg) brightness(105%) contrast(101%);
        --cm-pp-filter-level-6: brightness(0) saturate(100%) invert(26%) sepia(84%) saturate(4389%) hue-rotate(271deg) brightness(86%) contrast(119%);
      }
      @keyframes cm-fade-out {
        from {
          opacity: 1;
        }
        to {
          opacity: 0;
          visibility: hidden;
        }
      }
      .cm-overlay {
        position: relative;
      }
      .cm-overlay:after {
        content: '';
        position: absolute;
        background: repeating-linear-gradient(135deg, #2223, #2223 70px, #0003 70px, #0003 80px);
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 900000;
      }
      .cm-overlay-fade:after {
        animation-name: cm-fade-out;
        animation-duration: 0.2s;
        animation-timing-function: ease-in;
        animation-fill-mode: forwards;
        animation-delay: 0.4s
      }
      .cm-pp-level-1 {
        color: var(--cm-pp-level-1);
      }
      .cm-pp-level-2 {
        color: var(--cm-pp-level-2);
      }
      .cm-pp-level-3 {
        color: var(--cm-pp-level-3);
      }
      .cm-pp-level-4 {
        color: var(--cm-pp-level-4);
      }
      .cm-pp-level-5 {
        color: var(--cm-pp-level-5);
      }
      .cm-pp-level-6 {
        color: var(--cm-pp-level-6);
      }
      .cm-pp-level-1 [class*=timerCircle___] [class*=icon___] {
        filter: var(--cm-pp-filter-level-1);
      }
      .cm-pp-level-2 [class*=timerCircle___] [class*=icon___] {
        filter: var(--cm-pp-filter-level-2);
      }
      .cm-pp-level-3 [class*=timerCircle___] [class*=icon___] {
        filter: var(--cm-pp-filter-level-3);
      }
      .cm-pp-level-4 [class*=timerCircle___] [class*=icon___] {
        filter: var(--cm-pp-filter-level-4);
      }
      .cm-pp-level-5 [class*=timerCircle___] [class*=icon___] {
        filter: var(--cm-pp-filter-level-5);
      }
      .cm-pp-level-6 [class*=timerCircle___] [class*=icon___] {
        filter: var(--cm-pp-filter-level-6);
      }
      .cm-pp-level-1 [class*=timerCircle___] .CircularProgressbar-path {
        stroke: var(--cm-pp-level-1) !important;
      }
      .cm-pp-level-2 [class*=timerCircle___] .CircularProgressbar-path {
        stroke: var(--cm-pp-level-2) !important;
      }
      .cm-pp-level-3 [class*=timerCircle___] .CircularProgressbar-path {
        stroke: var(--cm-pp-level-3) !important;
      }
      .cm-pp-level-4 [class*=timerCircle___] .CircularProgressbar-path {
        stroke: var(--cm-pp-level-4) !important;
      }
      .cm-pp-level-5 [class*=timerCircle___] .CircularProgressbar-path {
        stroke: var(--cm-pp-level-5) !important;
      }
      .cm-pp-level-6 [class*=timerCircle___] .CircularProgressbar-path {
        stroke: var(--cm-pp-level-6) !important;
      }
      .cm-pp-level-1 [class*=commitButton___] {
        border: 2px solid var(--cm-pp-level-1);
      }
      .cm-pp-level-2 [class*=commitButton___] {
        border: 2px solid var(--cm-pp-level-2);
      }
      .cm-pp-level-3 [class*=commitButton___] {
        border: 2px solid var(--cm-pp-level-3);
      }
      .cm-pp-level-4 [class*=commitButton___] {
        border: 2px solid var(--cm-pp-level-4);
      }
      .cm-pp-level-5 [class*=commitButton___] {
        border: 2px solid var(--cm-pp-level-5);
      }
      .cm-pp-best-build:not(.crime-option-locked) [class*=physicalProps___]:before {
        content: '\u2713 ';
        font-weight: bold;
        color: var(--cm-pp-level-2);
      }

      .cm-sc-info {
        transform: translateY(1px);
      }
      .cm-sc-notice,
      .cm-sc-hint-button {
        cursor: pointer;
      }
      .cm-sc-info-wrapper.cm-sc-hint-hidden > .cm-sc-hint,
      .cm-sc-info-wrapper:not(.cm-sc-hint-hidden) > .cm-sc-orig-info {
        display: none;
      }
      .cm-sc-hint-content {
        display: flex;
        justify-content: space-between;
        flex-grow: 1;
        gap: 5px;
        white-space: nowrap;
        overflow: hidden;
      }
      .cm-sc-notice,
      .cm-sc-hint-action {
        flex-shrink: 1;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .cm-sc-seen[data-cm-action=strong] .response-type-button:nth-child(1):after,
      .cm-sc-seen[data-cm-action=soft] .response-type-button:nth-child(2):after,
      .cm-sc-seen[data-cm-action=back] .response-type-button:nth-child(3):after,
      .cm-sc-seen[data-cm-action=accelerate] .response-type-button:nth-child(4):after,
      .cm-sc-seen[data-cm-action=capitalize] .response-type-button:nth-child(5):after {
        content: '\u2713';
        color: var(--crimes-green-color);
        position: absolute;
        top: 0;
        right: 0;
        font-size: 12px;
        font-weight: bolder;
        line-height: 1;
        z-index: 999;
      }
      .cm-sc-seen.cm-sc-unsynced[data-cm-action=strong] .response-type-button:nth-child(1):after,
      .cm-sc-seen.cm-sc-unsynced[data-cm-action=soft] .response-type-button:nth-child(2):after,
      .cm-sc-seen.cm-sc-unsynced[data-cm-action=back] .response-type-button:nth-child(3):after,
      .cm-sc-seen.cm-sc-unsynced[data-cm-action=accelerate] .response-type-button:nth-child(4):after,
      .cm-sc-seen.cm-sc-unsynced[data-cm-action=capitalize] .response-type-button:nth-child(5):after {
        content: '?';
      }
      .cm-sc-seen[data-cm-action=abandon] .response-type-button:after {
        content: '\u2715';
        color: var(--crimes-stats-criticalFails-color);
        position: absolute;
        top: 0;
        right: 0;
        font-size: 12px;
        font-weight: bolder;
        line-height: 1;
        z-index: 999;
      }
      .cm-sc-scale {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: calc(100% + 10px);
        line-height: 1;
        font-size: 8px;
        display: flex;
        align-items: flex-end;
        justify-content: center;
      }
      .cm-sc-multiplier {
        position: absolute;
        bottom: 0;
        right: 0;
        color: var(--crimes-baseText-color);
        text-align: right;
        font-size: 10px;
        line-height: 1;
      }
      .cm-sc-farm-lifetime {
        padding-top: 2px;
        text-align: center;
      }
      .cm-sc-spam-option .levelLabel___LNbg8,
      .cm-sc-spam-option .separator___C2skk {
        display: none;
      }
      .cm-sc-spam-elapsed {
        position: absolute;
        right: -5px;
      }
      .cm-sc-settings {
        height: 40px;
        width: 100%;
        background: var(--default-bg-panel-color););
        border-bottom: 1px solid var(--crimes-crimeOption-borderBottomColor);
        padding-left: 10px;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        gap: 20px;
      }
      .cm-sc-algo-option {
        cursor: pointer;
        line-height: 1.5;
        border-top: 2px solid #0000;
        border-bottom: 2px solid #0000;
      }
      .cm-sc-algo-option.cm-sc-active {
        border-bottom-color: var(--default-blue-color);
      }
      .cm-sc-algo.cm-sc-active {
        cursor: pointer;
      }
      .cm-sc-algo.cm-sc-active:before {
        content: '\u21bb ';
      }
      .cm-sc-low-cash:after {
        content: 'Low Cash';
        color: var(--default-red-color);
        position: absolute;
        width: 100%;
        left: 0;
        top: calc(100% - 4px);
        line-height: 1;
        font-size: 12px;
      }
    `);
        }

        interceptFetch();
        renderMorale();

        if (document.readyState === 'loading') {
            document.addEventListener('readystatechange', () => {
                if (document.readyState === 'interactive') {
                    renderStyle();
                }
            });
        } else {
            renderStyle();
        }
    })();



    // Fast Slots
    // author Silmaril [2665762]


    (function() {
        'use strict';

        const originalAjax = $.ajax;

        $.ajax = function (options) {
            if (options.data != null && options.data.sid == 'slotsData' && options.data.step == 'play') {
                const originalSuccess = options.success;
                options.success = function (data, textStatus, jqXHR) {
                    if (data.error) delete data.error;
                    if (data.errorMsg) delete data.errorMsg;
                    data.barrelsAnimationSpeed = 0;
                    if (originalSuccess) {
                        originalSuccess(data, textStatus, jqXHR);
                    }
                };
            }

            return originalAjax(options);
        }

        function enableBetButtons() {
            document.querySelectorAll(".slots-btn-list .betbtn").forEach(btn => {
                btn.classList.remove("disabled");
            });
        }

        function disableBetButtons() {
            document.querySelectorAll(".slots-btn-list .betbtn").forEach(btn => {
                btn.classList.add("disabled");
            });
        }

        function watchBarrelsSpinAndStop(delay = 60) {
            const barrels = document.querySelectorAll("#barrel0, #barrel1, #barrel2");
            let timers = new Map();
            let stopped = new Map();

            barrels.forEach(barrel => stopped.set(barrel, true));

            barrels.forEach(barrel => {
                const observer = new MutationObserver(() => {
                    disableBetButtons();
                    stopped.set(barrel, false);
                    clearTimeout(timers.get(barrel));
                    timers.set(barrel, setTimeout(() => {
                        stopped.set(barrel, true);
                        if ([...stopped.values()].every(Boolean)) {
                            enableBetButtons();
                        }
                    }, delay));
                });

                observer.observe(barrel, {
                    attributes: true,
                    attributeFilter: ["style"]
                });
            });
        }

        var o = setInterval(() => {
            if($('#barrels').length == 1){
                clearInterval(o)
                watchBarrelsSpinAndStop();
            }
        }, 100);
    })();

    // Pickpocketing colors
    // author Korbrm [2931507]

    (function() {
        'use strict';


        const categoryColorMap = {
            "Safe": "#37b24d",
            "Moderately Unsafe": "#74b816",
            "Unsafe": "#f59f00",
            "Risky": "#f76707",
            "Dangerous": "#f03e3e",
            "Very Dangerous": "#7048e8",
        };

        var sideColorMap = {
            "Safe": "#37b24d",
            "Moderately Unsafe": "#74b816",
            "Unsafe": "#f59f00",
            "Risky": "#f76707",
            "Dangerous": "#f03e3e",
            "Very Dangerous": "#7048e8",
        }

        const tier1 = {
            "Safe": "#37b24d",
            "Moderately Unsafe": "#f76707",
            "Unsafe": "#f03e3e",
            "Risky": "#f03e3e",
            "Dangerous": "#f03e3e",
            "Very Dangerous": "#7048e8",
        }
        const tier2 = {
            "Safe": "#37b24d",
            "Moderately Unsafe": "#37b24d",
            "Unsafe": "#f76707",
            "Risky": "#f03e3e",
            "Dangerous": "#f03e3e",
            "Very Dangerous": "#7048e8",
        }
        const tier3 = {
            "Safe": "#37b24d",
            "Moderately Unsafe": "#37b24d",
            "Unsafe": "#37b24d",
            "Risky": "#f76707",
            "Dangerous": "#f03e3e",
            "Very Dangerous": "#7048e8",
        }
        const tier4 = {
            "Safe": "#37b24d",
            "Moderately Unsafe": "#37b24d",
            "Unsafe": "#37b24d",
            "Risky": "#37b24d",
            "Dangerous": "#f76707",
            "Very Dangerous": "#7048e8",
        }
        const tier5 = {
            "Safe": "#37b24d",
            "Moderately Unsafe": "#37b24d",
            "Unsafe": "#37b24d",
            "Risky": "#37b24d",
            "Dangerous": "#37b24d",
            "Very Dangerous": "#7048e8",
        }

        const markGroups = {
            "Safe": ["Drunk man", "Drunk woman", "Homeless person", "Junkie", "Elderly man", "Elderly woman"],
            "Moderately Unsafe": ["Classy lady", "Laborer", "Postal worker", "Young man", "Young woman", "Student"],
            "Unsafe": ["Rich kid", "Sex worker", "Thug"],
            "Risky": ["Jogger", "Businessman", "Businesswoman", "Gang member", "Mobster"],
            "Dangerous": ["Cyclist"],
            "Very Dangerous": ["Police officer"],
        };

        function updateDivColors() {
            var spanElement = document.querySelector('.value___FdkAT.copyTrigger___fsdzI');

            const url = window.location.href;
            if (!url.includes("#/pickpocketing")){
                return;
            }

            if (spanElement) {
                var pickpocketSkill = spanElement.textContent;
            }
            if (pickpocketSkill < 10) { sideColorMap = tier1; } else
                if (pickpocketSkill < 35) { sideColorMap = tier2; } else
                    if (pickpocketSkill < 65) { sideColorMap = tier3; } else
                        if (pickpocketSkill < 80) { sideColorMap = tier4; } else
                        { sideColorMap = tier5; }


            const divElements = document.querySelectorAll('.titleAndProps___DdeVu:not(.processed)');
            divElements.forEach(divElement => {
                const divContent = divElement.querySelector('div').textContent.trim();
                const additionalData = divElement.querySelector('button.physicalPropsButton___xWW45');

                if (additionalData) {
                    const additionalText = additionalData.textContent.trim();
                    const text = divContent + ' ' + additionalText;

                    for (const category in markGroups) {
                        if (markGroups[category].some(group => text.includes(group))) {
                            divElement.querySelector('div').style.color = categoryColorMap[category];
                            if (window.innerWidth > 386) {
                                divElement.querySelector('div').textContent = `${divContent} (${category})`;
                            }

                            divElement.classList.add('processed');
                            let parentElement = divElement;
                            for (let i = 0; i < 3; i++) {
                                parentElement = parentElement.parentElement;
                            }
                            if (!parentElement.classList.contains('processed')) {
                                parentElement.style.borderLeft = `3px solid ${sideColorMap[category]}`;
                                parentElement.classList.add('processed');
                            }
                        }
                    }
                }
            });
        }

        updateDivColors();
        setInterval(updateDivColors, 2000);
    })();


    // name Torn Market Filler
    // author Silmaril [2665762]

    (async function() {
        'use strict';

        const itemUrl = "https://api.torn.com/torn/{itemId}?selections=items&key={apiKey}&comment=MarketFiller";
        const marketUrl = "https://api.torn.com/v2/market/{itemId}?selections=itemMarket&key={apiKey}&comment=MarketFiller";
        const marketUrlV2 = "https://api.torn.com/v2/market?id={itemId}&selections=itemMarket&key={apiKey}&comment=MarketFiller";
        let showPricesPopup = localStorage.getItem("silmaril-torn-market-filler-show-prices-popup") ?? '1';
        showPricesPopup = Boolean(parseInt(showPricesPopup));
        let priceDeltaRaw = localStorage.getItem("silmaril-torn-market-filler-price-delta") ?? localStorage.getItem("silmaril-torn-bazaar-filler-price-delta") ?? '-1[0]';
        let apiKey = localStorage.getItem("sheepAPI") ?? '###PDA-APIKEY###';
        let togglePricesPopupMenuId, setPriceDeltaMenuId, setApiKeyMenuId;

        try {
            togglePricesPopupMenuId = GM_registerMenuCommand(`Toggle Prices Popup (${showPricesPopup ? 'ON' : 'OFF'})`, togglePricesPopupVisibility);
            setPriceDeltaMenuId = GM_registerMenuCommand(`Set Price Delta: ${priceDeltaRaw}`, setPriceDelta);
            setApiKeyMenuId = GM_registerMenuCommand(`Set Api Key: ${apiKey}`, function() { checkApiKey(false); });
        } catch (error) {
            console.warn('[TornMarketFiller] Tampermonkey not detected!');
        }

        let GM_addStyle = function (s) {
            let style = document.createElement("style");
            style.type = "text/css";
            style.innerHTML = s;
            document.head.appendChild(style);
        };
        GM_addStyle(`#item-market-root [class^=addListingWrapper___] [class^=panels___] [class^=priceInputWrapper___]>.input-money-group>.input-money,#item-market-root [class^=viewListingWrapper___] [class^=priceInputWrapper___]>.input-money-group>.input-money{font-size:smaller!important;border-bottom-left-radius:0!important;border-top-left-radius:0!important}.silmaril-market-filler-popup{background:var(--tooltip-bg-color);padding:12px 18px;border-radius:8px;border:1px solid #888;box-shadow:0 4px 18px 0 #0009;color:var(--info-msg-font-color);z-index:99999;position:absolute;font-size:1em!important;line-height:1.5;pointer-events:auto}.silmaril-market-filler-popup-close{position:absolute;top:4px;right:7px;font-size:1em;color:#aaa;cursor:pointer}.silmaril-market-filler-popup-draggable{user-select:none;cursor:move}.silmaril-torn-market-filler-popup-price{cursor:pointer}`);

        const pages = { "AddItems": 10, "ViewItems": 20, "Other": 0};
        let recentFilledInput = null;
        let popupOffsetX = localStorage.getItem("silmaril-torn-market-filler-popup-offset-x") ?? 0, popupOffsetY = 0, isDragging = false;
        const marketTaxFactor = 1 - getCurrentMarketTax();
        let currentPage = pages.Other;
        let holdTimer;
        const LOADING_THE_PRICES = 'Loading the prices...';
        const isMobileView = window.innerWidth <= 784;
        const observerTarget = document.querySelector("#item-market-root");
        const observerConfig = { attributes: false, childList: true, characterData: false, subtree: true };
        const observer = new MutationObserver(function(mutations) {
            mutations.forEach(mutationRaw => {
                let mutation = mutationRaw.target;
                currentPage = getCurrentPage();
                if (currentPage == pages.AddItems){
                    if (mutation.id && mutation.id.startsWith('headlessui-tabs-panel-')) {
                        mutation.querySelectorAll('[class*=itemRowWrapper___]:not(.silmaril-market-filler-processed) > [class*=itemRow___]:not([class*=grayedOut___]) [class^=priceInputWrapper___]').forEach(x => AddFillButton(x));
                    }
                    if (String(mutation.className).indexOf('priceInputWrapper___') > -1){
                        AddFillButton(mutation);
                    }
                } else if (currentPage == pages.ViewItems){
                    if (mutation.className && mutation.className.startsWith('viewListingWrapper___')) {
                        mutation.querySelectorAll('[class*=itemRowWrapper___]:not(.silmaril-market-filler-processed) > [class*=itemRow___]:not([class*=grayedOut___]) [class^=priceInputWrapper___]').forEach(x => AddFillButton(x));
                    }
                }
            });
        });
        observer.observe(observerTarget, observerConfig);
        addCustomFillPopup();

        function AddFillButton(itemPriceElement){
            if (itemPriceElement.querySelector('.silmaril-market-filler-button') != null){
                return;
            }
            const wrapperParent = findParentByCondition(itemPriceElement, (el) => String(el.className).indexOf('itemRowWrapper___') > -1);
            wrapperParent.classList.add('silmaril-market-filler-processed');
            let itemIdString = wrapperParent.querySelector('[class^=itemRow___] [type=button][class^=viewInfoButton___]').getAttribute('aria-controls');
            let itemImage = wrapperParent.querySelector('[class*=viewInfoButton] img');
            let itemId = currentPage == pages.AddItems ? getItemIdFromString(itemIdString) : getItemIdFromImage(itemImage);
            const span = document.createElement('span');
            span.className = 'silmaril-market-filler-button input-money-symbol';
            span.style.position = "relative";
            span.setAttribute('data-action-flag', 'fill');
            span.addEventListener('click', async function(e) { await handleFillClick(e, itemId) });
            span.addEventListener('mousedown', startHold);
            span.addEventListener('touchstart', startHold);
            span.addEventListener('mouseup', cancelHold);
            span.addEventListener('mouseleave', cancelHold);
            span.addEventListener('touchend', cancelHold);
            span.addEventListener('touchcancel', cancelHold);
            const input = document.createElement('input');
            input.type = 'button';
            input.className = 'wai-btn';
            span.appendChild(input);
            itemPriceElement.querySelector('.input-money-group').prepend(span);
        }

        async function GetPrices(itemId){
            let requestUrl = priceDeltaRaw.indexOf('[market]') != -1 ? itemUrl : marketUrlV2;
            requestUrl = requestUrl
                .replace("{itemId}", itemId)
                .replace("{apiKey}", apiKey);
            return fetch(requestUrl)
                .then(response => response.json())
                .then(data => {
                if (data.error != null){
                    switch (data.error.code){
                        case 2:
                            apiKey = null;
                            localStorage.setItem("silmaril-torn-bazaar-filler-apikey", null);
                            console.error("[TornMarketFiller] Incorrect Api Key:", data);
                            return {"price": 'Wrong API key!', "amount": 0};
                        case 9:
                            console.warn("[TornMarketFiller] The API is temporarily disabled, please try again later");
                            return {"price": 'API is OFF!', "amount": 0};
                        default:
                            console.error("[TornMarketFiller] Error:", data.error.error);
                            return {"price": data.error.error, "amount": 0};
                    }
                }
                if (priceDeltaRaw.indexOf('[market]') != -1){
                    return {"price": data.items[itemId].market_value, "amount": 1};
                } else {
                    if (data.itemmarket.listings[0].price == null){
                        console.warn("[TornMarketFiller] The API is temporarily disabled, please try again later");
                        return {"price": 'API is OFF!', "amount": 0};
                    }
                    // temporary hotfix to avoid wrong prices
                    if (data.itemmarket.item.id != itemId){
                        return {"price": 'API is BROKEN!', "amount": 0};
                    }
                    return data.itemmarket.listings;
                }
            })
                .catch(error => {
                console.error("[TornMarketFiller] Error fetching data:", error);
                return 'Failed!';
            });
        }

        function GetPrice(prices){
            if (prices == null){
                return 'No prices loaded';
            }
            if (prices.amount == 0){
                return prices.price;
            }
            if (priceDeltaRaw.indexOf('[market]') != -1) {
                prices = Array(prices);
                let priceDelta = priceDeltaRaw.indexOf('[') == -1 ? priceDeltaRaw : priceDeltaRaw.substring(0, priceDeltaRaw.indexOf('['));
                return Math.round(performOperation(prices[0].price, priceDelta));
            } else if (priceDeltaRaw.indexOf('[median]') != -1) {
                let priceDelta = priceDeltaRaw.indexOf('[') == -1 ? priceDeltaRaw : priceDeltaRaw.substring(0, priceDeltaRaw.indexOf('['));
                return Math.round(performOperation(getMedianPrice(prices), priceDelta));
            } else {
                let marketSlotOffset = priceDeltaRaw.indexOf('[') == -1 ? 0 : parseInt(priceDeltaRaw.substring(priceDeltaRaw.indexOf('[') + 1, priceDeltaRaw.indexOf(']')));
                let priceDeltaWithoutMarketOffset = priceDeltaRaw.indexOf('[') == -1 ? priceDeltaRaw : priceDeltaRaw.substring(0, priceDeltaRaw.indexOf('['));
                return Math.round(performOperation(prices[Math.min(marketSlotOffset, prices.length - 1)].price, priceDeltaWithoutMarketOffset));
            }
        }

        async function handleFillClick(event, itemId){
            let target = event.currentTarget || event.target;
            let priceInputs = target.parentNode.querySelectorAll('input.input-money');
            recentFilledInput = priceInputs;
            const popup = document.querySelector('.silmaril-market-filler-popup');
            if (popup) {
                const rect = target.getBoundingClientRect();
                if (popupOffsetX == 0){
                    popupOffsetX = window.scrollX + rect.left - 300;
                    localStorage.setItem("silmaril-torn-market-filler-popup-offset-x", popupOffsetX);
                }
                popupOffsetY = window.scrollY + rect.top + 4;

                let left = popupOffsetX;
                let top = popupOffsetY;

                popup.style.display = showPricesPopup ? 'block' : 'none';
                popup.style.visibility = 'hidden';
                popup.style.left = `${left}px`;
                popup.style.top = `${top}px`;
                popup.querySelector('.silmaril-market-filler-popup-body').innerHTML = LOADING_THE_PRICES;

                const popupRect = popup.getBoundingClientRect();
                const viewportHeight = window.innerHeight;
                const viewportWidth = window.innerWidth;

                if (popupRect.right > viewportWidth) {
                    left = Math.max(0, viewportWidth - popupRect.width - 10 + scrollX);
                }
                if (popupRect.left < 0) {
                    left = 10 + scrollX;
                }
                if (popupRect.bottom > viewportHeight) {
                    top = Math.max(0, viewportHeight - popupRect.height - 10 + scrollY);
                }
                if (popupRect.top < 0) {
                    top = 10 + scrollY;
                }

                popup.style.left = `${left}px`;
                popup.style.top = `${top}px`;
                popup.style.visibility = 'visible';
            }

            let action = target.getAttribute('data-action-flag');
            let prices = await GetPrices(itemId);
            const breakdown = GetPricesBreakdown(prices);

            // Thanks to Rosti [2840742] for the help with the prices popup component
            showCustomFillPopup(target, breakdown);

            let price = action == 'fill' ? GetPrice(prices) : '';
            switchActionFlag(target);
            let parentRow = findParentByCondition(target, (el) => String(el.className).indexOf('info___') > -1);
            let quantityInputs = parentRow.querySelectorAll('[class^=amountInputWrapper___] .input-money-group > .input-money');
            if (quantityInputs.length > 0){
                if (quantityInputs[0].value.length === 0 || parseInt(quantityInputs[0].value) < 1){
                    quantityInputs[0].value = action == 'fill' ? Number.MAX_SAFE_INTEGER : 0;
                    quantityInputs[1].value = action == 'fill' ? Number.MAX_SAFE_INTEGER : 0;
                } else {
                    quantityInputs[0].value = action == 'clear' ? '' : quantityInputs[0].value;
                    quantityInputs[1].value = action == 'clear' ? '' : quantityInputs[1].value;
                }
                quantityInputs[0].dispatchEvent(new Event("input", {bubbles: true}));
            } else {
                let checkbox = parentRow.querySelector('[class^=checkboxWrapper___] > [class^=checkboxContainer___] [type=checkbox]');
                if (checkbox && ((action == 'fill' && !checkbox.checked) || (action == 'clear' && checkbox.checked))){
                    checkbox.click();
                }
            }
            priceInputs.forEach(x => {x.value = price});
            priceInputs[0].dispatchEvent(new Event("input", {bubbles: true}));
        }

        function hideAllFillPopups() {
            document.querySelector('.silmaril-market-filler-popup').style.display = 'none';
        }

        function showCustomFillPopup(targetElem, contentHTML) {
            const popup = document.querySelector('.silmaril-market-filler-popup');
            popup.querySelector('.silmaril-market-filler-popup-body').innerHTML = contentHTML;
            popup.querySelectorAll('.silmaril-torn-market-filler-popup-price').forEach(row => {
                row.addEventListener('click', (e) => {
                    recentFilledInput.forEach(x => {x.value = parseInt(e.target.getAttribute('data-price')) - 1});
                    recentFilledInput[0].dispatchEvent(new Event("input", {bubbles: true}));
                });
            });
        }

        function addCustomFillPopup() {
            const popup = document.createElement('div');
            popup.className = 'silmaril-market-filler-popup';
            popup.style.display = 'none';
            popup.style.left = popupOffsetX + 'px';
            popup.style.top = '0px';
            popup.innerHTML = '<div class="silmaril-market-filler-popup-close" title="Close">&times;</div><b class="silmaril-market-filler-popup-draggable">Drag from here</b><br><div class="silmaril-market-filler-popup-body"></div>';
            popup.querySelector('.silmaril-market-filler-popup-close').onclick = function(){ popup.style.display = 'none'; };
            document.body.appendChild(popup);

            const dragHandle = popup.querySelector('.silmaril-market-filler-popup-draggable');
            dragHandle.addEventListener("mousedown", (e) => {
                isDragging = true;
                popupOffsetX = e.clientX - popup.offsetLeft;
                popupOffsetY = e.clientY - popup.offsetTop;
            });

            document.addEventListener("mousemove", (e) => {
                if (isDragging) {
                    popup.style.left = (e.clientX - popupOffsetX) + "px";
                    popup.style.top = (e.clientY - popupOffsetY) + "px";
                }
            });

            document.addEventListener("mouseup", (e) => {
                if (isDragging) {
                    popupOffsetX = e.clientX - popupOffsetX;
                    popupOffsetY = e.clientY - popupOffsetY;
                    localStorage.setItem("silmaril-torn-market-filler-popup-offset-x", popupOffsetX);
                }
                isDragging = false;
            });

            // Touch events (mobile)
            dragHandle.addEventListener("touchstart", (e) => {
                isDragging = true;
                const touch = e.touches[0];
                popupOffsetX = touch.clientX - popup.offsetLeft;
                popupOffsetY = touch.clientY - popup.offsetTop;
                e.preventDefault();
            }, { passive: false });

            document.addEventListener("touchmove", (e) => {
                if (isDragging) {
                    const touch = e.touches[0];
                    popup.style.left = (touch.clientX - popupOffsetX) + "px";
                    popup.style.top = (touch.clientY - popupOffsetY) + "px";
                }
            }, { passive: false });

            document.addEventListener("touchend", () => {
                if (isDragging) {
                    popupOffsetX = popup.offsetLeft;
                    popupOffsetY = popup.offsetTop;
                    localStorage.setItem("silmaril-torn-market-filler-popup-offset-x", popupOffsetX);
                }
                isDragging = false;
            });
        }

        function getItemIdFromString(string){
            const match = string.match(/-(\d+)-/);
            if (match) {
                const number = match[1];
                return number;
            } else {
                console.error("[TornMarketFiller] ItemId not found!");
                return -1;
            }
        }

        function getItemIdFromImage(image){
            let numberPattern = /\/(\d+)\//;
            let match = image.src.match(numberPattern);
            if (match) {
                return parseInt(match[1], 10);
            } else {
                console.error("[TornMarketFiller] ItemId not found!");
                return -1;
            }
        }

        function switchActionFlag(target){
            switch (target.getAttribute('data-action-flag')){
                case 'fill':
                    target.setAttribute('data-action-flag', 'clear');
                    break;
                case 'clear':
                default:
                    target.setAttribute('data-action-flag', 'fill');
                    break;
            }
        }

        function findParentByCondition(element, conditionFn){
            let currentElement = element;
            while (currentElement !== null) {
                if (conditionFn(currentElement)) {
                    return currentElement;
                }
                currentElement = currentElement.parentElement;
            }
            return null;
        }

        function setPriceDelta() {
            let userInput = prompt('Enter price delta formula (default: -1[0]):', priceDeltaRaw);
            if (userInput !== null) {
                priceDeltaRaw = userInput;
                localStorage.setItem("silmaril-torn-market-filler-price-delta", userInput);
            } else {
                console.error("[TornMarketFiller] User cancelled the Price Delta input.");
            }
        }

        function GetPricesBreakdown(prices){
            if (prices == null) return "No prices loaded";
            if (prices[0] === undefined){
                prices = Array(prices);
            }
            const sb = new StringBuilder();
            for (let i = 0; i < Math.min(prices.length, 5); i++){
                if(typeof prices[i] !== "object" || prices[i].amount === undefined || prices[i].price === undefined) continue;
                sb.append(`<span class="silmaril-torn-market-filler-popup-price" data-price=${prices[i].price}>${prices[i].amount} x ${formatNumberWithCommas(prices[i].price)} (${formatNumberWithCommas(Math.round(prices[i].price * marketTaxFactor))})</span>`);
                if (i < Math.min(prices.length, 5)-1){
                    sb.append('<br>');
                }
            }
            return sb.toString();
        }

        function performOperation(number, operation) {
            const match = operation.match(/^([-+]?)(\d+(?:\.\d+)?)(%)?$/);
            if (!match) {
                throw new Error('Invalid operation string');
            }
            const [, operator, operand, isPercentage] = match;
            const operandValue = parseFloat(operand);
            const adjustedOperand = isPercentage ? (number * operandValue) / 100 : operandValue;
            switch (operator) {
                case '':
                case '+':
                    return number + adjustedOperand;
                case '-':
                    return number - adjustedOperand;
                default:
                    throw new Error('Invalid operator');
            }
        }

        function formatNumberWithCommas(number) {
            return new Intl.NumberFormat('en-US').format(number);
        }

        function checkApiKey(checkExisting = true) {
            if (!checkExisting || apiKey === null || apiKey.indexOf('PDA-APIKEY') > -1 || apiKey.length != 16){
                let userInput = prompt("Please enter a PUBLIC Api Key, it will be used to get current bazaar prices:", apiKey ?? '');
                if (userInput !== null && userInput.length == 16) {
                    apiKey = userInput;
                    localStorage.setItem("silmaril-torn-bazaar-filler-apikey", userInput);
                } else {
                    console.error("[TornMarketFiller] User cancelled the Api Key input.");
                }
            }
        }

        function askForPricesPopupFlag() {
            let dsf = null;
            let userInput = prompt("Please choose to show or hide the lowest 5 prices popup, enter 1 to SHOW or 0 to HIDE:", showPricesPopup ? '1' : '0');
            if (userInput !== null && userInput.length == 1) {
                if (userInput != '1' && userInput != '0'){
                    console.error("[TornMarketFiller] User entered invalid value for the Prices Popup input.");
                    return;
                }
                showPricesPopup = Boolean(parseInt(userInput));
                localStorage.setItem('silmaril-torn-market-filler-show-prices-popup', showPricesPopup ? '1' : '0');
            } else {
                console.error("[TornMarketFiller] User cancelled the Prices Popup input.");
            }
        }

        function togglePricesPopupVisibility() {
            showPricesPopup = !showPricesPopup;
            localStorage.setItem('silmaril-torn-market-filler-show-prices-popup', showPricesPopup ? '1' : '0');
        }

        function getMedianPrice(items) {
            const prices = items.flatMap(item => Array(item.amount).fill(item.price));
            prices.sort((a, b) => a - b);
            const mid = Math.floor(prices.length / 2);
            if (prices.length % 2 === 0) {
                return (prices[mid - 1] + prices[mid]) / 2;
            } else {
                return prices[mid];
            }
        }

        function getCurrentPage(){
            if (window.location.href.indexOf('#/addListing') > -1){
                return pages.AddItems;
            } else if (window.location.href.indexOf('#/viewListing') > -1){
                return pages.ViewItems;
            } else {
                return pages.Other;
            }
        }

        function getCurrentMarketTax() {
            return 0.05;
        }

        // function getTornToday() {
        //     const now = document.querySelector('span.server-date-time').textContent.split(' ');
        //     return now[now.length - 1];
        // }

        function parseDate(str) {
            const [dd, mm, yy] = str.split('/').map(Number);
            const fullYear = yy < 50 ? 2000 + yy : 1900 + yy;
            return new Date(fullYear, mm - 1, dd);
        }

        const startHold = () => {
            holdTimer = setTimeout(() => {
                askForPricesPopupFlag();
                setPriceDelta();
                checkApiKey(false);
            }, 2000);
        };

        const cancelHold = () => {
            clearTimeout(holdTimer);
        };

        class StringBuilder {
            constructor() {
                this.parts = [];
            }
            append(str) {
                this.parts.push(str);
                return this;
            }
            toString() {
                return this.parts.join('');
            }
        }
    })();



    const sheepAPI = localStorage.getItem("sheepAPI");
    if (sheepAPI === null) {
        if (window.location.href === "https://www.torn.com/preferences.php#tab=api") {createButtonAtPositionAPI("API", 0, 550);} else {
            createButtonAtPositionAPI2("API", 0, 550);}}


    function createButtonAtPositionAPI(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.style.width = "100px";
        customButton.style.height = "50px";
        customButton.innerHTML = 'Click Here To Enter Public API';
        customButton.style.position = 'fixed';
        customButton.style.top = topPx + 'px';
        customButton.style.left = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '99999999999999';
        customButton.addEventListener('click', function() {
            let sheepAPI = localStorage.getItem("sheepAPI");

            if (sheepAPI === null) {
                sheepAPI = prompt("Please enter your Public API key");

                // Check if input is 16 characters and only letters/numbers
                if (/^[a-zA-Z0-9]{16}$/.test(sheepAPI)) {
                    localStorage.setItem("sheepAPI", sheepAPI);
                    window.location.reload();
                } else {
                    alert("Invalid API key. It must be exactly 16 letters and numbers.");
                    // Optionally, you can prompt again
                }
            }
        });
        document.body.appendChild(customButton);
    }
    function createButtonAtPositionAPI2(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.style.width = "100px";
        customButton.style.height = "50px";
        customButton.innerHTML = 'Click Here To Get Public API key';
        customButton.style.position = 'fixed';
        customButton.style.top = topPx + 'px';
        customButton.style.left = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '99999999999999';
        customButton.addEventListener('click', function() {
            const sheepAPI = localStorage.getItem("sheepAPI");
            if (sheepAPI === null) {
                window.open('https://www.torn.com/preferences.php#tab=api').focus();
            } else {
            }
        });
        document.body.appendChild(customButton);
    }



    // name Torn Market Filler
    // author Silmaril [2665762]

    (async function() {
        'use strict';

        const itemUrl = "https://api.torn.com/torn/{itemId}?selections=items&key={apiKey}&comment=MarketFiller";
        const marketUrl = "https://api.torn.com/v2/market/{itemId}?selections=itemMarket&key={apiKey}&comment=MarketFiller";
        const marketUrlV2 = "https://api.torn.com/v2/market?id={itemId}&selections=itemMarket&key={apiKey}&comment=MarketFiller";
        let showPricesPopup = localStorage.getItem("silmaril-torn-market-filler-show-prices-popup") ?? '1';
        showPricesPopup = Boolean(parseInt(showPricesPopup));
        let priceDeltaRaw = localStorage.getItem("silmaril-torn-market-filler-price-delta") ?? localStorage.getItem("silmaril-torn-bazaar-filler-price-delta") ?? '-1[0]';
        let apiKey = localStorage.getItem("sheepAPI") ?? '###PDA-APIKEY###';
        let togglePricesPopupMenuId, setPriceDeltaMenuId, setApiKeyMenuId;

        try {
            togglePricesPopupMenuId = GM_registerMenuCommand(`Toggle Prices Popup (${showPricesPopup ? 'ON' : 'OFF'})`, togglePricesPopupVisibility);
            setPriceDeltaMenuId = GM_registerMenuCommand(`Set Price Delta: ${priceDeltaRaw}`, setPriceDelta);
            setApiKeyMenuId = GM_registerMenuCommand(`Set Api Key: ${apiKey}`, function() { checkApiKey(false); });
        } catch (error) {
            console.warn('[TornMarketFiller] Tampermonkey not detected!');
        }

        let GM_addStyle = function (s) {
            let style = document.createElement("style");
            style.type = "text/css";
            style.innerHTML = s;
            document.head.appendChild(style);
        };
        GM_addStyle(`#item-market-root [class^=addListingWrapper___] [class^=panels___] [class^=priceInputWrapper___]>.input-money-group>.input-money,#item-market-root [class^=viewListingWrapper___] [class^=priceInputWrapper___]>.input-money-group>.input-money{font-size:smaller!important;border-bottom-left-radius:0!important;border-top-left-radius:0!important}.silmaril-market-filler-popup{background:var(--tooltip-bg-color);padding:12px 18px;border-radius:8px;border:1px solid #888;box-shadow:0 4px 18px 0 #0009;color:var(--info-msg-font-color);z-index:99999;position:absolute;font-size:1em!important;line-height:1.5;pointer-events:auto}.silmaril-market-filler-popup-close{position:absolute;top:4px;right:7px;font-size:1em;color:#aaa;cursor:pointer}.silmaril-market-filler-popup-draggable{user-select:none;cursor:move}.silmaril-torn-market-filler-popup-price{cursor:pointer}`);

        const pages = { "AddItems": 10, "ViewItems": 20, "Other": 0};
        let recentFilledInput = null;
        let popupOffsetX = localStorage.getItem("silmaril-torn-market-filler-popup-offset-x") ?? 0, popupOffsetY = 0, isDragging = false;
        const marketTaxFactor = 1 - getCurrentMarketTax();
        let currentPage = pages.Other;
        let holdTimer;
        const LOADING_THE_PRICES = 'Loading the prices...';
        const isMobileView = window.innerWidth <= 784;
        const observerTarget = document.querySelector("#item-market-root");
        const observerConfig = { attributes: false, childList: true, characterData: false, subtree: true };
        const observer = new MutationObserver(function(mutations) {
            mutations.forEach(mutationRaw => {
                let mutation = mutationRaw.target;
                currentPage = getCurrentPage();
                if (currentPage == pages.AddItems){
                    if (mutation.id && mutation.id.startsWith('headlessui-tabs-panel-')) {
                        mutation.querySelectorAll('[class*=itemRowWrapper___]:not(.silmaril-market-filler-processed) > [class*=itemRow___]:not([class*=grayedOut___]) [class^=priceInputWrapper___]').forEach(x => AddFillButton(x));
                    }
                    if (String(mutation.className).indexOf('priceInputWrapper___') > -1){
                        AddFillButton(mutation);
                    }
                } else if (currentPage == pages.ViewItems){
                    if (mutation.className && mutation.className.startsWith('viewListingWrapper___')) {
                        mutation.querySelectorAll('[class*=itemRowWrapper___]:not(.silmaril-market-filler-processed) > [class*=itemRow___]:not([class*=grayedOut___]) [class^=priceInputWrapper___]').forEach(x => AddFillButton(x));
                    }
                }
            });
        });
        observer.observe(observerTarget, observerConfig);
        addCustomFillPopup();

        function AddFillButton(itemPriceElement){
            if (itemPriceElement.querySelector('.silmaril-market-filler-button') != null){
                return;
            }
            const wrapperParent = findParentByCondition(itemPriceElement, (el) => String(el.className).indexOf('itemRowWrapper___') > -1);
            wrapperParent.classList.add('silmaril-market-filler-processed');
            let itemIdString = wrapperParent.querySelector('[class^=itemRow___] [type=button][class^=viewInfoButton___]').getAttribute('aria-controls');
            let itemImage = wrapperParent.querySelector('[class*=viewInfoButton] img');
            let itemId = currentPage == pages.AddItems ? getItemIdFromString(itemIdString) : getItemIdFromImage(itemImage);
            const span = document.createElement('span');
            span.className = 'silmaril-market-filler-button input-money-symbol';
            span.style.position = "relative";
            span.setAttribute('data-action-flag', 'fill');
            span.addEventListener('click', async function(e) { await handleFillClick(e, itemId) });
            span.addEventListener('mousedown', startHold);
            span.addEventListener('touchstart', startHold);
            span.addEventListener('mouseup', cancelHold);
            span.addEventListener('mouseleave', cancelHold);
            span.addEventListener('touchend', cancelHold);
            span.addEventListener('touchcancel', cancelHold);
            const input = document.createElement('input');
            input.type = 'button';
            input.className = 'wai-btn';
            span.appendChild(input);
            itemPriceElement.querySelector('.input-money-group').prepend(span);
        }

        async function GetPrices(itemId){
            let requestUrl = priceDeltaRaw.indexOf('[market]') != -1 ? itemUrl : marketUrlV2;
            requestUrl = requestUrl
                .replace("{itemId}", itemId)
                .replace("{apiKey}", apiKey);
            return fetch(requestUrl)
                .then(response => response.json())
                .then(data => {
                if (data.error != null){
                    switch (data.error.code){
                        case 2:
                            apiKey = null;
                            localStorage.setItem("silmaril-torn-bazaar-filler-apikey", null);
                            console.error("[TornMarketFiller] Incorrect Api Key:", data);
                            return {"price": 'Wrong API key!', "amount": 0};
                        case 9:
                            console.warn("[TornMarketFiller] The API is temporarily disabled, please try again later");
                            return {"price": 'API is OFF!', "amount": 0};
                        default:
                            console.error("[TornMarketFiller] Error:", data.error.error);
                            return {"price": data.error.error, "amount": 0};
                    }
                }
                if (priceDeltaRaw.indexOf('[market]') != -1){
                    return {"price": data.items[itemId].market_value, "amount": 1};
                } else {
                    if (data.itemmarket.listings[0].price == null){
                        console.warn("[TornMarketFiller] The API is temporarily disabled, please try again later");
                        return {"price": 'API is OFF!', "amount": 0};
                    }
                    // temporary hotfix to avoid wrong prices
                    if (data.itemmarket.item.id != itemId){
                        return {"price": 'API is BROKEN!', "amount": 0};
                    }
                    return data.itemmarket.listings;
                }
            })
                .catch(error => {
                console.error("[TornMarketFiller] Error fetching data:", error);
                return 'Failed!';
            });
        }

        function GetPrice(prices){
            if (prices == null){
                return 'No prices loaded';
            }
            if (prices.amount == 0){
                return prices.price;
            }
            if (priceDeltaRaw.indexOf('[market]') != -1) {
                prices = Array(prices);
                let priceDelta = priceDeltaRaw.indexOf('[') == -1 ? priceDeltaRaw : priceDeltaRaw.substring(0, priceDeltaRaw.indexOf('['));
                return Math.round(performOperation(prices[0].price, priceDelta));
            } else if (priceDeltaRaw.indexOf('[median]') != -1) {
                let priceDelta = priceDeltaRaw.indexOf('[') == -1 ? priceDeltaRaw : priceDeltaRaw.substring(0, priceDeltaRaw.indexOf('['));
                return Math.round(performOperation(getMedianPrice(prices), priceDelta));
            } else {
                let marketSlotOffset = priceDeltaRaw.indexOf('[') == -1 ? 0 : parseInt(priceDeltaRaw.substring(priceDeltaRaw.indexOf('[') + 1, priceDeltaRaw.indexOf(']')));
                let priceDeltaWithoutMarketOffset = priceDeltaRaw.indexOf('[') == -1 ? priceDeltaRaw : priceDeltaRaw.substring(0, priceDeltaRaw.indexOf('['));
                return Math.round(performOperation(prices[Math.min(marketSlotOffset, prices.length - 1)].price, priceDeltaWithoutMarketOffset));
            }
        }

        async function handleFillClick(event, itemId){
            let target = event.currentTarget || event.target;
            let priceInputs = target.parentNode.querySelectorAll('input.input-money');
            recentFilledInput = priceInputs;
            const popup = document.querySelector('.silmaril-market-filler-popup');
            if (popup) {
                const rect = target.getBoundingClientRect();
                if (popupOffsetX == 0){
                    popupOffsetX = window.scrollX + rect.left - 300;
                    localStorage.setItem("silmaril-torn-market-filler-popup-offset-x", popupOffsetX);
                }
                popupOffsetY = window.scrollY + rect.top + 4;

                let left = popupOffsetX;
                let top = popupOffsetY;

                popup.style.display = showPricesPopup ? 'block' : 'none';
                popup.style.visibility = 'hidden';
                popup.style.left = `${left}px`;
                popup.style.top = `${top}px`;
                popup.querySelector('.silmaril-market-filler-popup-body').innerHTML = LOADING_THE_PRICES;

                const popupRect = popup.getBoundingClientRect();
                const viewportHeight = window.innerHeight;
                const viewportWidth = window.innerWidth;

                if (popupRect.right > viewportWidth) {
                    left = Math.max(0, viewportWidth - popupRect.width - 10 + scrollX);
                }
                if (popupRect.left < 0) {
                    left = 10 + scrollX;
                }
                if (popupRect.bottom > viewportHeight) {
                    top = Math.max(0, viewportHeight - popupRect.height - 10 + scrollY);
                }
                if (popupRect.top < 0) {
                    top = 10 + scrollY;
                }

                popup.style.left = `${left}px`;
                popup.style.top = `${top}px`;
                popup.style.visibility = 'visible';
            }

            let action = target.getAttribute('data-action-flag');
            let prices = await GetPrices(itemId);
            const breakdown = GetPricesBreakdown(prices);

            // Thanks to Rosti [2840742] for the help with the prices popup component
            showCustomFillPopup(target, breakdown);

            let price = action == 'fill' ? GetPrice(prices) : '';
            switchActionFlag(target);
            let parentRow = findParentByCondition(target, (el) => String(el.className).indexOf('info___') > -1);
            let quantityInputs = parentRow.querySelectorAll('[class^=amountInputWrapper___] .input-money-group > .input-money');
            if (quantityInputs.length > 0){
                if (quantityInputs[0].value.length === 0 || parseInt(quantityInputs[0].value) < 1){
                    quantityInputs[0].value = action == 'fill' ? Number.MAX_SAFE_INTEGER : 0;
                    quantityInputs[1].value = action == 'fill' ? Number.MAX_SAFE_INTEGER : 0;
                } else {
                    quantityInputs[0].value = action == 'clear' ? '' : quantityInputs[0].value;
                    quantityInputs[1].value = action == 'clear' ? '' : quantityInputs[1].value;
                }
                quantityInputs[0].dispatchEvent(new Event("input", {bubbles: true}));
            } else {
                let checkbox = parentRow.querySelector('[class^=checkboxWrapper___] > [class^=checkboxContainer___] [type=checkbox]');
                if (checkbox && ((action == 'fill' && !checkbox.checked) || (action == 'clear' && checkbox.checked))){
                    checkbox.click();
                }
            }
            priceInputs.forEach(x => {x.value = price});
            priceInputs[0].dispatchEvent(new Event("input", {bubbles: true}));
        }

        function hideAllFillPopups() {
            document.querySelector('.silmaril-market-filler-popup').style.display = 'none';
        }

        function showCustomFillPopup(targetElem, contentHTML) {
            const popup = document.querySelector('.silmaril-market-filler-popup');
            popup.querySelector('.silmaril-market-filler-popup-body').innerHTML = contentHTML;
            popup.querySelectorAll('.silmaril-torn-market-filler-popup-price').forEach(row => {
                row.addEventListener('click', (e) => {
                    recentFilledInput.forEach(x => {x.value = parseInt(e.target.getAttribute('data-price')) - 1});
                    recentFilledInput[0].dispatchEvent(new Event("input", {bubbles: true}));
                });
            });
        }

        function addCustomFillPopup() {
            const popup = document.createElement('div');
            popup.className = 'silmaril-market-filler-popup';
            popup.style.display = 'none';
            popup.style.left = popupOffsetX + 'px';
            popup.style.top = '0px';
            popup.innerHTML = '<div class="silmaril-market-filler-popup-close" title="Close">&times;</div><b class="silmaril-market-filler-popup-draggable">Drag from here</b><br><div class="silmaril-market-filler-popup-body"></div>';
            popup.querySelector('.silmaril-market-filler-popup-close').onclick = function(){ popup.style.display = 'none'; };
            document.body.appendChild(popup);

            const dragHandle = popup.querySelector('.silmaril-market-filler-popup-draggable');
            dragHandle.addEventListener("mousedown", (e) => {
                isDragging = true;
                popupOffsetX = e.clientX - popup.offsetLeft;
                popupOffsetY = e.clientY - popup.offsetTop;
            });

            document.addEventListener("mousemove", (e) => {
                if (isDragging) {
                    popup.style.left = (e.clientX - popupOffsetX) + "px";
                    popup.style.top = (e.clientY - popupOffsetY) + "px";
                }
            });

            document.addEventListener("mouseup", (e) => {
                if (isDragging) {
                    popupOffsetX = e.clientX - popupOffsetX;
                    popupOffsetY = e.clientY - popupOffsetY;
                    localStorage.setItem("silmaril-torn-market-filler-popup-offset-x", popupOffsetX);
                }
                isDragging = false;
            });

            // Touch events (mobile)
            dragHandle.addEventListener("touchstart", (e) => {
                isDragging = true;
                const touch = e.touches[0];
                popupOffsetX = touch.clientX - popup.offsetLeft;
                popupOffsetY = touch.clientY - popup.offsetTop;
                e.preventDefault();
            }, { passive: false });

            document.addEventListener("touchmove", (e) => {
                if (isDragging) {
                    const touch = e.touches[0];
                    popup.style.left = (touch.clientX - popupOffsetX) + "px";
                    popup.style.top = (touch.clientY - popupOffsetY) + "px";
                }
            }, { passive: false });

            document.addEventListener("touchend", () => {
                if (isDragging) {
                    popupOffsetX = popup.offsetLeft;
                    popupOffsetY = popup.offsetTop;
                    localStorage.setItem("silmaril-torn-market-filler-popup-offset-x", popupOffsetX);
                }
                isDragging = false;
            });
        }

        function getItemIdFromString(string){
            const match = string.match(/-(\d+)-/);
            if (match) {
                const number = match[1];
                return number;
            } else {
                console.error("[TornMarketFiller] ItemId not found!");
                return -1;
            }
        }

        function getItemIdFromImage(image){
            let numberPattern = /\/(\d+)\//;
            let match = image.src.match(numberPattern);
            if (match) {
                return parseInt(match[1], 10);
            } else {
                console.error("[TornMarketFiller] ItemId not found!");
                return -1;
            }
        }

        function switchActionFlag(target){
            switch (target.getAttribute('data-action-flag')){
                case 'fill':
                    target.setAttribute('data-action-flag', 'clear');
                    break;
                case 'clear':
                default:
                    target.setAttribute('data-action-flag', 'fill');
                    break;
            }
        }

        function findParentByCondition(element, conditionFn){
            let currentElement = element;
            while (currentElement !== null) {
                if (conditionFn(currentElement)) {
                    return currentElement;
                }
                currentElement = currentElement.parentElement;
            }
            return null;
        }

        function setPriceDelta() {
            let userInput = prompt('Enter price delta formula (default: -1[0]):', priceDeltaRaw);
            if (userInput !== null) {
                priceDeltaRaw = userInput;
                localStorage.setItem("silmaril-torn-market-filler-price-delta", userInput);
            } else {
                console.error("[TornMarketFiller] User cancelled the Price Delta input.");
            }
        }

        function GetPricesBreakdown(prices){
            if (prices == null) return "No prices loaded";
            if (prices[0] === undefined){
                prices = Array(prices);
            }
            const sb = new StringBuilder();
            for (let i = 0; i < Math.min(prices.length, 5); i++){
                if(typeof prices[i] !== "object" || prices[i].amount === undefined || prices[i].price === undefined) continue;
                sb.append(`<span class="silmaril-torn-market-filler-popup-price" data-price=${prices[i].price}>${prices[i].amount} x ${formatNumberWithCommas(prices[i].price)} (${formatNumberWithCommas(Math.round(prices[i].price * marketTaxFactor))})</span>`);
                if (i < Math.min(prices.length, 5)-1){
                    sb.append('<br>');
                }
            }
            return sb.toString();
        }

        function performOperation(number, operation) {
            const match = operation.match(/^([-+]?)(\d+(?:\.\d+)?)(%)?$/);
            if (!match) {
                throw new Error('Invalid operation string');
            }
            const [, operator, operand, isPercentage] = match;
            const operandValue = parseFloat(operand);
            const adjustedOperand = isPercentage ? (number * operandValue) / 100 : operandValue;
            switch (operator) {
                case '':
                case '+':
                    return number + adjustedOperand;
                case '-':
                    return number - adjustedOperand;
                default:
                    throw new Error('Invalid operator');
            }
        }

        function formatNumberWithCommas(number) {
            return new Intl.NumberFormat('en-US').format(number);
        }

        function checkApiKey(checkExisting = true) {
            if (!checkExisting || apiKey === null || apiKey.indexOf('PDA-APIKEY') > -1 || apiKey.length != 16){
                let userInput = prompt("Please enter a PUBLIC Api Key, it will be used to get current bazaar prices:", apiKey ?? '');
                if (userInput !== null && userInput.length == 16) {
                    apiKey = userInput;
                    localStorage.setItem("silmaril-torn-bazaar-filler-apikey", userInput);
                } else {
                    console.error("[TornMarketFiller] User cancelled the Api Key input.");
                }
            }
        }

        function askForPricesPopupFlag() {
            let dsf = null;
            let userInput = prompt("Please choose to show or hide the lowest 5 prices popup, enter 1 to SHOW or 0 to HIDE:", showPricesPopup ? '1' : '0');
            if (userInput !== null && userInput.length == 1) {
                if (userInput != '1' && userInput != '0'){
                    console.error("[TornMarketFiller] User entered invalid value for the Prices Popup input.");
                    return;
                }
                showPricesPopup = Boolean(parseInt(userInput));
                localStorage.setItem('silmaril-torn-market-filler-show-prices-popup', showPricesPopup ? '1' : '0');
            } else {
                console.error("[TornMarketFiller] User cancelled the Prices Popup input.");
            }
        }

        function togglePricesPopupVisibility() {
            showPricesPopup = !showPricesPopup;
            localStorage.setItem('silmaril-torn-market-filler-show-prices-popup', showPricesPopup ? '1' : '0');
        }

        function getMedianPrice(items) {
            const prices = items.flatMap(item => Array(item.amount).fill(item.price));
            prices.sort((a, b) => a - b);
            const mid = Math.floor(prices.length / 2);
            if (prices.length % 2 === 0) {
                return (prices[mid - 1] + prices[mid]) / 2;
            } else {
                return prices[mid];
            }
        }

        function getCurrentPage(){
            if (window.location.href.indexOf('#/addListing') > -1){
                return pages.AddItems;
            } else if (window.location.href.indexOf('#/viewListing') > -1){
                return pages.ViewItems;
            } else {
                return pages.Other;
            }
        }

        function getCurrentMarketTax() {
            return 0.05;
        }

        // function getTornToday() {
        //     const now = document.querySelector('span.server-date-time').textContent.split(' ');
        //     return now[now.length - 1];
        // }

        function parseDate(str) {
            const [dd, mm, yy] = str.split('/').map(Number);
            const fullYear = yy < 50 ? 2000 + yy : 1900 + yy;
            return new Date(fullYear, mm - 1, dd);
        }

        const startHold = () => {
            holdTimer = setTimeout(() => {
                askForPricesPopupFlag();
                setPriceDelta();
                checkApiKey(false);
            }, 2000);
        };

        const cancelHold = () => {
            clearTimeout(holdTimer);
        };

        class StringBuilder {
            constructor() {
                this.parts = [];
            }
            append(str) {
                this.parts.push(str);
                return this;
            }
            toString() {
                return this.parts.join('');
            }
        }
    })();






    // name Torn: Refill Blood Bag Reminder
    // author ButtChew [3840391]

    (function () {
        'use strict';

        const CONFIG = {
            // Icon settings
            fullLifeIconId: 'tm-full-life-bloodbag',
            bloodBagPng: 'https://i.postimg.cc/mkZ1T68H/blood-bag-2.png',

            // Destination URLs
            destinations: {
                factionArmoury: 'https://www.torn.com/factions.php?step=your&type=1#/tab=armoury&start=0&sub=medical',
                personalInventory: 'https://www.torn.com/item.php#medical-items',
            },

            // Blood bag mechanics
            lifePerBag: 30,           // Each bag uses 30% life
            cooldownPerBagMs: 60 * 60 * 1000,  // Each bag adds 1 hour cooldown

            // Status icons selector (for inserting our icon)
            statusIconsSelector: 'ul[class*="status-icons"]',

            // Poll interval
            pollMs: 2000,
        };


        (function () {
            'use strict';

            const BANNER_ID = "sheepieBloodBagBanner";
            const LINK = "https://www.torn.com/factions.php?step=your&type=1#/tab=armoury&start=0&sub=medical";

            function checkForBloodBag() {
                const icon = document.querySelector('img[src*="blood-bag-2.png"]');
                if (!icon) return;

                if (document.getElementById(BANNER_ID)) return;

                const banner = document.createElement("div");
                banner.id = BANNER_ID;
                banner.textContent = "please fill blood bags";

                banner.style.position = "fixed";
                banner.style.top = "150000000000000px";
                banner.style.left = "333333333px";
                if (localStorage.getItem("warmode") === "true") {
                    // do warmode stuff
                    banner.style.top = "15px";
                    banner.style.left = "50%";
                }
                banner.style.transform = "translateX(-50%)";
                banner.style.background = "#8b0000";
                banner.style.color = "white";
                banner.style.padding = "10px 20px";
                banner.style.fontSize = "16px";
                banner.style.fontWeight = "bold";
                banner.style.borderRadius = "8px";
                banner.style.boxShadow = "0 4px 12px rgba(0,0,0,0.4)";
                banner.style.zIndex = "999999";
                banner.style.cursor = "pointer";
                banner.style.transition = "opacity 0.5s ease";

                banner.addEventListener("click", function () {
                    window.location.href = LINK;
                });

                document.body.appendChild(banner);

                setTimeout(() => {
                    banner.style.opacity = "0";
                    setTimeout(() => banner.remove(), 500);
                }, 3000);
            }

            // Because Torn loads dynamically, poll briefly
            const interval = setInterval(() => {
                checkForBloodBag();
            }, 2500);

            // Stop checking after 10 seconds
            setTimeout(() => clearInterval(interval), 10000);

        })();

        // ===== GM_* COMPATIBILITY (TornPDA support) =====
        const safeGM = {
            getValue: (key, defaultVal) => {
                try {
                    return typeof GM_getValue === 'function' ? GM_getValue(key, defaultVal) : defaultVal;
                } catch { return defaultVal; }
            },
            setValue: (key, val) => {
                try {
                    if (typeof GM_setValue === 'function') GM_setValue(key, val);
                } catch { /* ignore */ }
            },
            registerMenuCommand: (name, fn) => {
                try {
                    if (typeof GM_registerMenuCommand === 'function') GM_registerMenuCommand(name, fn);
                } catch { /* ignore */ }
            }
        };

        // ===== SESSIONSTORAGE DATA EXTRACTION =====
        function getSidebarData() {
            try {
                const key = Object.keys(sessionStorage).find(k => /sidebarData\d+/.test(k));
                if (!key) return null;
                return JSON.parse(sessionStorage.getItem(key));
            } catch {
                return null;
            }
        }

        function getLifeFromStorage() {
            const data = getSidebarData();
            if (!data) return null;

            // Life data is at data.bars.life with amount/max properties
            const life = data?.bars?.life;
            if (life && typeof life.amount === 'number' && typeof life.max === 'number') {
                const pct = life.max > 0 ? Math.round((life.amount / life.max) * 100) : 0;
                return { current: life.amount, max: life.max, pct };
            }

            return null;
        }

        function hmsToMs(hms) {
            if (!hms) return 0;
            const parts = hms.split(':').map(Number);
            if (parts.length === 3) {
                const [h, m, s] = parts;
                return ((h * 60 + m) * 60 + s) * 1000;
            }
            return 0;
        }

        function getMedicalCooldownInfo() {
            const data = getSidebarData();
            if (!data) return null;

            const med = data?.statusIcons?.icons?.medical_cooldown;
            if (!med) return null;

            const nowSec = Date.now() / 1000;

            const remainingMs = Math.max(0, (med.timerExpiresAt - nowSec) * 1000);
            const maxMs = hmsToMs(med.factionUpgrade);

            return {
                remainingMs,
                maxMs,
                freeMs: Math.max(0, maxMs - remainingMs)
            };
        }

        // ===== SETTINGS =====
        function getDestinationURL() {
            const destination = safeGM.getValue('bloodBagDestination', 'factionArmoury');
            return CONFIG.destinations[destination] || CONFIG.destinations.factionArmoury;
        }

        function getBagsToFill() {
            const bags = safeGM.getValue('bloodBagCount', 3);
            return Math.max(1, Math.min(3, bags)); // Clamp to 1-3
        }

        function getThresholds() {
            const bags = getBagsToFill();
            return {
                lifePercent: bags * CONFIG.lifePerBag, // 30%, 60%, or 90%
                cooldownBufferMs: (bags - 1) * CONFIG.cooldownPerBagMs // 0, 1hr, or 2hr buffer
            };
        }

        function getOpenInNewTab() {
            return safeGM.getValue('bloodBagNewTab', true); // Default: open in new tab
        }

        function openSettingsModal() {
            // Remove existing modal if present
            const existingModal = document.getElementById('bloodbag-settings-modal');
            if (existingModal) existingModal.remove();

            const currentDestination = safeGM.getValue('bloodBagDestination', 'factionArmoury');
            const currentBags = getBagsToFill();
            const currentNewTab = getOpenInNewTab();
            const thresholds = getThresholds();

            const settingsModal = document.createElement('div');
            settingsModal.id = 'bloodbag-settings-modal';
            settingsModal.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.8);
                z-index: 100000;
                display: flex;
                align-items: center;
                justify-content: center;
            `;

            const getCooldownText = (bags) => {
                if (bags === 1) return 'any room available';
                return `${bags - 1}hr buffer available`;
            };

            settingsModal.innerHTML = `
                <div style="
                    background: #2e2e2e;
                    border-radius: 10px;
                    width: 450px;
                    max-width: 90%;
                    box-shadow: 0 4px 20px rgba(0,0,0,0.5);
                ">
                    <div style="
                        background: linear-gradient(to bottom, #1a1a1a, #2a2a2a);
                        padding: 15px 20px;
                        border-radius: 10px 10px 0 0;
                        display: flex;
                        justify-content: space-between;
                        align-items: center;
                    ">
                        <h2 style="margin: 0; color: #fff; font-size: 18px;">Blood Bag Settings</h2>
                        <button id="close-bloodbag-settings" style="
                            background: none;
                            border: none;
                            color: #aaa;
                            font-size: 24px;
                            cursor: pointer;
                            padding: 0;
                            width: 30px;
                            height: 30px;
                        ">x</button>
                    </div>
                    <div style="padding: 20px; color: #ccc;">
                        <div style="margin-bottom: 20px;">
                            <label style="display: block; margin-bottom: 5px; color: #aaa; font-size: 14px;">
                                Bags to Fill:
                            </label>
                            <select id="bloodbag-count" style="
                                width: 100%;
                                padding: 10px;
                                background: #1a1a1a;
                                border: 1px solid #444;
                                border-radius: 5px;
                                color: #fff;
                                font-size: 14px;
                                box-sizing: border-box;
                            ">
                                <option value="1" ${currentBags === 1 ? 'selected' : ''}>1 bag (requires >30% life)</option>
                                <option value="2" ${currentBags === 2 ? 'selected' : ''}>2 bags (requires >60% life)</option>
                                <option value="3" ${currentBags === 3 ? 'selected' : ''}>3 bags (requires >90% life)</option>
                            </select>
                            <p style="font-size: 12px; color: #888; margin-top: 5px;">
                                How many blood bags do you want to fill at once?<br>
                                Each bag uses 30% life and adds 1hr medical cooldown.
                            </p>
                        </div>

                        <div style="margin-bottom: 20px;">
                            <label style="display: block; margin-bottom: 5px; color: #aaa; font-size: 14px;">
                                Destination Page:
                            </label>
                            <select id="bloodbag-destination" style="
                                width: 100%;
                                padding: 10px;
                                background: #1a1a1a;
                                border: 1px solid #444;
                                border-radius: 5px;
                                color: #fff;
                                font-size: 14px;
                                box-sizing: border-box;
                            ">
                                <option value="factionArmoury" ${currentDestination === 'factionArmoury' ? 'selected' : ''}>Faction Armoury (Medical)</option>
                                <option value="personalInventory" ${currentDestination === 'personalInventory' ? 'selected' : ''}>Personal Inventory (Medical)</option>
                            </select>
                            <p style="font-size: 12px; color: #888; margin-top: 5px;">
                                Where clicking the blood bag icon takes you.<br>
                                <em>Tip: Long-press the icon to open this settings panel.</em>
                            </p>
                        </div>

                        <div style="margin-bottom: 20px;">
                            <label style="display: flex; align-items: center; cursor: pointer; color: #ccc; font-size: 14px;">
                                <input type="checkbox" id="bloodbag-newtab" ${currentNewTab ? 'checked' : ''} style="
                                    width: 18px;
                                    height: 18px;
                                    margin-right: 10px;
                                    cursor: pointer;
                                ">
                                Open in new tab
                            </label>
                            <p style="font-size: 12px; color: #888; margin-top: 5px; margin-left: 28px;">
                                When disabled, clicking the icon navigates in the same tab.
                            </p>
                        </div>

                        <div id="trigger-conditions" style="
                            background: rgba(255,255,255,0.05);
                            padding: 12px;
                            border-radius: 5px;
                            margin-bottom: 20px;
                            font-size: 12px;
                            color: #aaa;
                        ">
                            <strong style="color: #ccc;">Current Trigger Conditions:</strong><br>
                            - Life above ${thresholds.lifePercent}%<br>
                            - Medical cooldown: ${getCooldownText(currentBags)}
                        </div>

                        <div style="text-align: right;">
                            <button id="cancel-bloodbag-settings" style="
                                background: linear-gradient(to bottom, #555, #777);
                                border: none;
                                color: white;
                                padding: 10px 20px;
                                border-radius: 5px;
                                cursor: pointer;
                                margin-right: 10px;
                                font-size: 14px;
                            ">Cancel</button>
                            <button id="save-bloodbag-settings" style="
                                background: linear-gradient(to bottom, #799427, #a3c248);
                                border: none;
                                color: white;
                                padding: 10px 20px;
                                border-radius: 5px;
                                cursor: pointer;
                                font-size: 14px;
                            ">Save</button>
                        </div>
                    </div>
                </div>
            `;

            // Update trigger conditions when bags selection changes
            const updateTriggerDisplay = () => {
                const bags = parseInt(document.getElementById('bloodbag-count').value, 10);
                const lifeReq = bags * CONFIG.lifePerBag;
                const conditionsDiv = document.getElementById('trigger-conditions');
                if (conditionsDiv) {
                    conditionsDiv.innerHTML = `
                        <strong style="color: #ccc;">Current Trigger Conditions:</strong><br>
                        - Life above ${lifeReq}%<br>
                        - Medical cooldown: ${getCooldownText(bags)}
                    `;
                }
            };

            document.body.appendChild(settingsModal);

            // Event listeners
            document.getElementById('bloodbag-count').addEventListener('change', updateTriggerDisplay);
            document.getElementById('close-bloodbag-settings').addEventListener('click', () => settingsModal.remove());
            document.getElementById('cancel-bloodbag-settings').addEventListener('click', () => settingsModal.remove());
            document.getElementById('save-bloodbag-settings').addEventListener('click', () => {
                const bags = parseInt(document.getElementById('bloodbag-count').value, 10);
                const destination = document.getElementById('bloodbag-destination').value;
                const newTab = document.getElementById('bloodbag-newtab').checked;
                safeGM.setValue('bloodBagCount', bags);
                safeGM.setValue('bloodBagDestination', destination);
                safeGM.setValue('bloodBagNewTab', newTab);
                settingsModal.remove();
                updateIcon();
            });
            settingsModal.addEventListener('click', (e) => {
                if (e.target === settingsModal) settingsModal.remove();
            });
        }

        // Register settings menu command
        safeGM.registerMenuCommand('Blood Bag Settings', openSettingsModal);

        // ===== ICON MANAGEMENT =====
        function updateIcon() {
            const statusUl = document.querySelector(CONFIG.statusIconsSelector);
            if (!statusUl) return;

            const existing = document.getElementById(CONFIG.fullLifeIconId);
            const life = getLifeFromStorage();
            const med = getMedicalCooldownInfo();
            const thresholds = getThresholds();

            // Check conditions
            // Life must be above threshold (30%, 60%, or 90% based on bags setting)
            const lifeOk = life && life.pct > thresholds.lifePercent;

            // Cooldown check: current < max - buffer
            // For 1 bag: just need current < max (any room)
            // For 2 bags: need current < max - 1hr
            // For 3 bags: need current < max - 2hr
            let cooldownOk = true;
            if (med && med.maxMs > 0) {
                // We have medical cooldown info
                // Check if: remainingMs < maxMs - bufferMs
                // Which means: we have enough room for all bags
                cooldownOk = med.remainingMs < (med.maxMs - thresholds.cooldownBufferMs);
            }
            // If no medical cooldown icon exists, cooldownOk stays true (no cooldown = can use)

            const shouldShow = lifeOk && cooldownOk;

            if (shouldShow) {
                // Build tooltip text
                let label = `Life: ${formatNum(life.current)} / ${formatNum(life.max)} (${life.pct}%)`;
                if (med && med.maxMs > 0) {
                    const remainHrs = Math.floor(med.remainingMs / 3600000);
                    const remainMin = Math.floor((med.remainingMs % 3600000) / 60000);
                    const maxHrs = Math.floor(med.maxMs / 3600000);
                    label += ` - CD: ${remainHrs}h${remainMin}m / ${maxHrs}h`;
                } else {
                    label += ` - No medical cooldown`;
                }

                if (existing) {
                    updateIconTooltip(existing, label);
                    return;
                }

                const li = buildBloodBagIcon(label);
                statusUl.appendChild(li);
            } else if (existing) {
                existing.remove();
            }
        }

        function buildBloodBagIcon(tooltipText) {
            const li = document.createElement('li');
            li.id = CONFIG.fullLifeIconId;
            li.style.background = 'none';
            li.style.animation = 'tmPulse 900ms ease-out 1';

            const a = document.createElement('a');
            a.href = getDestinationURL();
            if (getOpenInNewTab()) {
                a.target = '_blank';
                a.rel = 'noopener noreferrer';
            }
            a.setAttribute('aria-label', tooltipText);
            a.tabIndex = 0;
            a.setAttribute('data-is-tooltip-opened', 'false');

            const img = document.createElement('img');
            img.src = CONFIG.bloodBagPng;
            img.alt = 'Blood Bag';
            img.width = 17;
            img.height = 17;
            img.style.display = 'block';

            a.appendChild(img);
            li.appendChild(a);

            // Long-press to open settings
            setupLongPress(a, 500, openSettingsModal);

            // Native-style tooltip
            enableNativeLikeTooltip(a);

            // Add pulse animation style if not present
            if (!document.getElementById('tm-pulse-style')) {
                const style = document.createElement('style');
                style.id = 'tm-pulse-style';
                style.textContent = `
                    @keyframes tmPulse {
                        0%   { transform: scale(0.9); }
                        60%  { transform: scale(1.1); }
                        100% { transform: scale(1.0); }
                    }
                `;
                document.head.appendChild(style);
            }

            return li;
        }

        function setupLongPress(element, duration, callback) {
            let timer = null;
            let didLongPress = false;

            function startPress() {
                didLongPress = false;
                timer = setTimeout(() => {
                    didLongPress = true;
                    callback();
                }, duration);
            }

            function cancelPress() {
                clearTimeout(timer);
                timer = null;
            }

            function endPress(e) {
                clearTimeout(timer);
                if (didLongPress) {
                    e.preventDefault();
                    e.stopPropagation();
                    didLongPress = false;
                }
            }

            // Touch events (mobile/TornPDA)
            element.addEventListener('touchstart', startPress, { passive: true });
            element.addEventListener('touchend', endPress);
            element.addEventListener('touchmove', cancelPress, { passive: true });
            element.addEventListener('touchcancel', cancelPress);

            // Mouse events (desktop)
            element.addEventListener('mousedown', startPress);
            element.addEventListener('mouseup', endPress);
            element.addEventListener('mouseleave', cancelPress);

            // Prevent click if long-press occurred
            element.addEventListener('click', (e) => {
                if (didLongPress) {
                    e.preventDefault();
                    e.stopPropagation();
                    didLongPress = false;
                }
            });
        }

        function updateIconTooltip(li, text) {
            const a = li.querySelector('a');
            if (!a) return;
            a.href = getDestinationURL();
            if (getOpenInNewTab()) {
                a.target = '_blank';
                a.rel = 'noopener noreferrer';
            } else {
                a.removeAttribute('target');
                a.removeAttribute('rel');
            }
            a.setAttribute('aria-label', text);
            if (typeof a.__tmUpdateTipText === 'function') a.__tmUpdateTipText(text);
        }

        // ===== TOOLTIP =====
        function enableNativeLikeTooltip(anchor) {
            let tipEl = null;
            let hideTimer = null;

            const CLS = {
                tip: 'tooltip___aWICR tooltipCustomClass___gbI4V',
                arrowWrap: 'arrow___yUDKb top___klE_Y',
                arrowIcon: 'arrowIcon___KHyjw',
            };

            function buildTooltip(text) {
                const el = document.createElement('div');
                el.className = CLS.tip;
                el.setAttribute('role', 'tooltip');
                el.setAttribute('tabindex', '-1');
                el.style.position = 'absolute';
                el.style.transitionProperty = 'opacity';
                el.style.transitionDuration = '200ms';
                el.style.opacity = '0';

                const [title, subtitle] = parseTwoLines(text);
                const b = document.createElement('b');
                b.textContent = title;
                el.appendChild(b);

                if (subtitle) {
                    const div = document.createElement('div');
                    div.textContent = subtitle;
                    el.appendChild(div);
                }

                const arrowWrap = document.createElement('div');
                arrowWrap.className = CLS.arrowWrap;
                const arrowIcon = document.createElement('div');
                arrowIcon.className = CLS.arrowIcon;
                arrowWrap.appendChild(arrowIcon);
                el.appendChild(arrowWrap);

                return el;
            }

            function setText(text) {
                if (!tipEl) return;
                const [title, subtitle] = parseTwoLines(text);
                const b = tipEl.querySelector('b');
                if (b) b.textContent = title;
                let sub = b?.nextElementSibling;
                if (subtitle) {
                    if (!sub || sub.tagName !== 'DIV') {
                        sub = document.createElement('div');
                        b.after(sub);
                    }
                    sub.textContent = subtitle;
                } else if (sub) {
                    sub.remove();
                }
            }

            function parseTwoLines(text) {
                const parts = text.split(' - ');
                if (parts.length >= 2) {
                    return [parts[0].trim(), parts[1].trim()];
                }
                return [text.trim(), ''];
            }

            function positionTooltip() {
                if (!tipEl) return;

                const r = anchor.getBoundingClientRect();
                const ew = tipEl.offsetWidth;
                const eh = tipEl.offsetHeight;

                let left = Math.round(r.left + (r.width - ew) / 2);
                let top = Math.round(r.top - eh - 14);

                left = Math.max(8, Math.min(left, window.innerWidth - ew - 8));
                if (top < 8) {
                    top = Math.round(r.bottom + 10);
                }

                tipEl.style.left = `${left}px`;
                tipEl.style.top = `${top}px`;

                const arrow = tipEl.querySelector(`.${CLS.arrowWrap.split(' ')[0]}`);
                if (arrow) {
                    const iconCenter = r.left + r.width / 2;
                    const arrowLeft = Math.round(iconCenter - left - 6 + 14);
                    arrow.style.left = `${arrowLeft}px`;
                }
            }

            function showTip() {
                clearTimeout(hideTimer);
                const text = anchor.getAttribute('aria-label');
                if (!text) return;

                if (!tipEl) {
                    tipEl = buildTooltip(text);
                    document.body.appendChild(tipEl);
                    anchor.__tmTipEl = tipEl;
                } else {
                    setText(text);
                }

                anchor.setAttribute('data-is-tooltip-opened', 'true');

                tipEl.style.opacity = '0';
                tipEl.style.left = '-9999px';
                tipEl.style.top = '-9999px';
                requestAnimationFrame(() => {
                    positionTooltip();
                    requestAnimationFrame(() => {
                        if (tipEl) tipEl.style.opacity = '1';
                    });
                });
            }

            function hideTip(immediate = false) {
                if (!tipEl) return;
                anchor.setAttribute('data-is-tooltip-opened', 'false');

                if (immediate) {
                    tipEl.remove();
                    anchor.__tmTipEl = null;
                    tipEl = null;
                    return;
                }
                tipEl.style.opacity = '0';
                hideTimer = setTimeout(() => {
                    tipEl?.remove();
                    anchor.__tmTipEl = null;
                    tipEl = null;
                }, 210);
            }

            anchor.__tmUpdateTipText = (text) => setText(text);

            anchor.addEventListener('mouseenter', showTip);
            anchor.addEventListener('mouseleave', () => hideTip(false));
            anchor.addEventListener('focus', showTip);
            anchor.addEventListener('blur', () => hideTip(true));
            window.addEventListener('scroll', () => hideTip(true), { passive: true });
        }

        // ===== CSS GUARDS =====
        function ensureStyles() {
            if (document.getElementById('tm-bloodbag-styles')) return;
            const s = document.createElement('style');
            s.id = 'tm-bloodbag-styles';
            s.textContent = `
                #${CONFIG.fullLifeIconId},
                #${CONFIG.fullLifeIconId} a,
                #${CONFIG.fullLifeIconId} img {
                    background: none !important;
                    background-image: none !important;
                    -webkit-mask: none !important;
                    mask: none !important;
                    box-shadow: none !important;
                    border: none !important;
                }
                #${CONFIG.fullLifeIconId}::before,
                #${CONFIG.fullLifeIconId}::after,
                #${CONFIG.fullLifeIconId} a::before,
                #${CONFIG.fullLifeIconId} a::after {
                    content: none !important;
                }
                ul[class*="status-icons"] {
                    height: auto !important;
                    overflow: visible !important;
                }
            `;
            document.head.appendChild(s);
        }

        // ===== UTILITIES =====
        function formatNum(n) {
            try {
                return n.toLocaleString();
            } catch {
                return String(n);
            }
        }

        // ===== INITIALIZATION =====
        // Declare before anything can call scheduleCheck (fixes TDZ crash)
        let checkScheduled = false;

        function scheduleCheck() {
            if (checkScheduled) return;
            checkScheduled = true;
            requestAnimationFrame(() => {
                checkScheduled = false;
                updateIcon();
            });
        }

        // One-time CSS injection (run immediately like v3.4)
        ensureStyles();

        // Observe DOM changes (SPA) and poll (like v3.4)
        const mo = new MutationObserver(() => {
            scheduleCheck();
        });
        mo.observe(document.documentElement, { childList: true, subtree: true });
        setInterval(scheduleCheck, CONFIG.pollMs);

        // Initial check
        scheduleCheck();


    })();

    (function () {
        'use strict';

        const now = new Date();
        const year = now.getFullYear();

        // Months are 0-based in JS (April = 3)
        const aprilFirst = new Date(year, 3, 1, 0, 0, 0);
        const aprilNinth = new Date(year, 3, 9, 23, 59, 59);

        if (now >= aprilFirst && now <= aprilNinth) {
            // ✅ EXECUTE YOUR CODE HERE
            console.log("April 1–9 detected, running Easter scripts!");
            // name Heasley's Egg Navigator
            // author Heasleys4hemp [1468764]
            'use strict';
            var ButtonFloat = parseInt(localStorage.getItem('eeh-float')) || 0;
            var ButtonFloatPos = parseInt(localStorage.getItem('eeh-float-pos')) || 0; //0 = bottom-left ; 1 = top-left; 2 = bottom-right; 3 = top-right
            var linkIndex = localStorage.getItem('eeh-index') || 0;
            var eeh_pressTimer, eeh_anim_pressTimer;
            var eeh_reset_time = 9800;
            var eeh_fade_in = 200;
            var eeh_is_disabled = false;
            var eeh_holding = false;



            if (typeof GM == 'undefined') {
                window.GM = {};
            }

            if (typeof GM.addStyle == "undefined") { //Add GM.addStyle for browsers that do not support it (e.g. TornPDA, Firefox+Greasemonkey)
                GM.addStyle = function (aCss) {
                    'use strict';
                    let head = document.getElementsByTagName('head')[0];
                    if (head) {
                        let style = document.createElement('style');
                        style.setAttribute('type', 'text/css');
                        style.textContent = aCss;
                        head.appendChild(style);
                        return style;
                    }
                    return null;
                };
            }

            if (typeof GM.registerMenuCommand != "undefined") {
                GM.registerMenuCommand('Toggle Floating Button', toggleFloatButton,
                                       {
                    autoClose: false
                }
                                      );

                GM.registerMenuCommand('Toggle Float Position', toggleFloatPosition,
                                       {
                    autoClose: false
                }
                                      );
            }

            const obs_ops = {attributes: false, childList: true, characterData: false, subtree:true};

            const easteregg_svg = `<svg xmlns="http://www.w3.org/2000/svg" fill="#AFC372" stroke="transparent" stroke-width="0" width="13" height="17" viewBox="0 0 14 18"><path d="M1.68,16a5.6,5.6,0,0,0,.43.41A5.72,5.72,0,0,0,3,17a4.73,4.73,0,0,0,.74.39,5.08,5.08,0,0,0,.8.3,5.35,5.35,0,0,0,.69.17,8.62,8.62,0,0,0,.87.11h.84a8.46,8.46,0,0,0,.88-.11l.69-.17a7.14,7.14,0,0,0,.81-.31q.38-.18.72-.39a6.57,6.57,0,0,0,.9-.67,5.14,5.14,0,0,0,.41-.4A6.3,6.3,0,0,0,13,11.67a8.86,8.86,0,0,0-.09-1.21c0-.31-.1-.64-.17-1s-.2-.85-.33-1.29-.3-.93-.48-1.39-.33-.81-.51-1.2c-.1-.2-.19-.39-.29-.58L11,4.72c-.18-.33-.4-.69-.64-1s-.4-.55-.62-.82A4.41,4.41,0,0,0,6.5,1,4.41,4.41,0,0,0,3.29,2.86a9.15,9.15,0,0,0-.61.82c-.24.34-.44.68-.62,1L1.87,5l-.33.66c-.16.36-.32.72-.46,1.09S.74,7.7.61,8.16a13.14,13.14,0,0,0-.34,1.3,10,10,0,0,0-.18,1A8.47,8.47,0,0,0,0,11.67a6.29,6.29,0,0,0,.89,3.25A6.63,6.63,0,0,0,1.68,16ZM1.27,14.8a.7.7,0,0,1,.4.38,1.4,1.4,0,0,1,.09.29A6.38,6.38,0,0,1,1.27,14.8Zm1,1.15c.17-.14.46,0,.66.32a1.41,1.41,0,0,1,.14.31A5.55,5.55,0,0,1,2.22,16Zm1.41,1a.44.44,0,0,1,.2-.39c.22-.11.52.1.67.46a1.28,1.28,0,0,1,.09.32A6.22,6.22,0,0,1,3.63,16.94Zm1.58.55a.47.47,0,0,1,.27-.4c.22-.06.46.16.57.51A7.4,7.4,0,0,1,5.21,17.49ZM7,17.6c.11-.35.35-.57.57-.51a.49.49,0,0,1,.27.39A5.66,5.66,0,0,1,7,17.6Zm1.46-.28A1.18,1.18,0,0,1,8.52,17c.16-.36.46-.57.67-.46a.43.43,0,0,1,.2.38A7.27,7.27,0,0,1,8.44,17.32ZM10,16.56a.84.84,0,0,1,.13-.29c.19-.31.47-.44.65-.33A7.57,7.57,0,0,1,10,16.56Zm1.26-1.14a.75.75,0,0,1,.08-.24.72.72,0,0,1,.36-.37A6.76,6.76,0,0,1,11.28,15.42Zm1.06-6q.11.51.18,1a.73.73,0,0,1-.37-.4A.44.44,0,0,1,12.34,9.45ZM10.49,4.67l.3.54c.11.2.21.41.31.63a.85.85,0,0,1-.65-.4C10.24,5.12,10.26,4.78,10.49,4.67Zm-.41,2.2c-.25.09-.58-.12-.74-.46s-.09-.68.16-.76a.69.69,0,0,1,.74.46C10.4,6.45,10.33,6.79,10.08,6.87ZM7.22,1.49a3.3,3.3,0,0,1,1,.51.5.5,0,0,1-.14.59.68.68,0,0,1-.86-.28A.61.61,0,0,1,7.22,1.49Zm-2.39.45a3.34,3.34,0,0,1,1-.46.6.6,0,0,1,0,.83A.66.66,0,0,1,5,2.59.53.53,0,0,1,4.83,1.94ZM3.58,3.12a4.75,4.75,0,0,0,2.91.93A4.7,4.7,0,0,0,9.42,3.1c.24.3.47.62.68.92A4.5,4.5,0,0,1,6.49,5.39,4.46,4.46,0,0,1,2.9,4,9.35,9.35,0,0,1,3.58,3.12ZM7.93,7.54c-.29,0-.57-.25-.64-.64a.59.59,0,0,1,.38-.76c.29,0,.57.25.64.63S8.21,7.5,7.93,7.54Zm-2-.64c-.07.39-.36.67-.65.64s-.45-.38-.38-.77.36-.67.64-.63A.6.6,0,0,1,5.9,6.9Zm-3-.79a.69.69,0,0,1,.74-.46c.25.08.32.42.16.76s-.49.55-.74.46S2.78,6.45,2.94,6.11Zm-.73-.9c.08-.16.18-.33.28-.51.17.14.17.45,0,.74a.89.89,0,0,1-.57.39C2,5.62,2.1,5.41,2.21,5.21ZM1.38,7.08A7.89,7.89,0,0,0,6.52,8.7a7.91,7.91,0,0,0,5.11-1.6c.19.5.36,1,.5,1.52-1,1.2-3.11,2-5.61,2S1.83,9.8.88,8.58C1,8.09,1.19,7.58,1.38,7.08ZM11.55,11.5A.59.59,0,0,1,11,11a.46.46,0,0,1,.4-.57.59.59,0,0,1,.56.52A.47.47,0,0,1,11.55,11.5Zm-1.68.85a.6.6,0,0,1-.59-.5.45.45,0,0,1,.36-.59.62.62,0,0,1,.59.51A.45.45,0,0,1,9.87,12.35Zm-1.77,0a.56.56,0,0,1-.53.57.57.57,0,0,1-.51-.6.52.52,0,1,1,1,0Zm-2,0a.56.56,0,0,1-.5.6.59.59,0,0,1,0-1.17A.55.55,0,0,1,6.06,12.27Zm-2.21-.42a.61.61,0,0,1-.59.5.45.45,0,0,1-.36-.58.6.6,0,0,1,.59-.51A.46.46,0,0,1,3.85,11.85ZM2.13,11a.58.58,0,0,1-.56.52.46.46,0,0,1-.39-.57.59.59,0,0,1,.56-.52A.46.46,0,0,1,2.13,11ZM.65,9.48A.46.46,0,0,1,.78,10a.69.69,0,0,1-.29.36C.53,10.11.59,9.8.65,9.48ZM.38,11.67a4.84,4.84,0,0,1,0-.53c.74,1.68,3.19,3,6.1,3s5.33-1.32,6.09-3c0,.17,0,.35,0,.51a5.86,5.86,0,0,1-.39,2.11C11.21,15.09,9,16,6.51,16S1.75,15.06.75,13.73A5.84,5.84,0,0,1,.38,11.67Z"></path></svg>`;
            const EVERY_LINK = ["", "index.php","forums.php#/p=threads&f=67&t=16326854&b=0&a=0","city.php","jobs.php","gym.php","properties.php","page.php?sid=education",
                                "crimes.php","loader.php?sid=missions","newspaper.php","jailview.php","hospitalview.php",
                                "casino.php","page.php?sid=hof","factions.php","competition.php","page.php?sid=list&type=friends",
                                "page.php?sid=list&type=enemies", "page.php?sid=list&type=targets","messages.php","page.php?sid=events","awards.php","points.php","rules.php",
                                "staff.php","credits.php","citystats.php","committee.php","bank.php","donator.php","item.php",
                                "page.php?sid=stocks","fans.php","museum.php","loader.php?sid=racing","church.php",
                                "dump.php","loan.php","page.php?sid=travel","amarket.php","bigalgunshop.php","shops.php?step=bitsnbobs",
                                "shops.php?step=cyberforce","shops.php?step=docks","shops.php?step=jewelry",
                                "shops.php?step=nikeh","shops.php?step=pawnshop","shops.php?step=pharmacy","pmarket.php",
                                "shops.php?step=postoffice","shops.php?step=super","shops.php?step=candy",
                                "shops.php?step=clothes","shops.php?step=recyclingcenter","shops.php?step=printstore","page.php?sid=ItemMarket","estateagents.php","bazaar.php?userId=1",
                                "calendar.php","token_shop.php","freebies.php","bringafriend.php","comics.php","archives.php","joblist.php",
                                "newspaper_class.php","personals.php","newspaper.php#/archive",
                                "profiles.php?XID=1",
                                "bounties.php","usersonline.php","joblist.php?step=search#!p=corpinfo&ID=79286","page.php?sid=log","page.php?sid=ammo","playerreport.php",
                                "loader.php?sid=itemsMods","displaycase.php","trade.php",
                                "crimes.php?step=criminalrecords","page.php?sid=factionWarfare#/dirty-bombs",
                                "index.php?page=fortune","page.php?sid=bunker","church.php?step=proposals",
                                "messageinc.php","preferences.php","messageinc2.php#!p=main","page.php?sid=gallery&XID=1","personalstats.php?ID=1",
                                "properties.php?step=rentalmarket","properties.php?step=sellingmarket","forums.php",
                                "page.php?sid=slots",
                                "page.php?sid=roulette","page.php?sid=highlow","page.php?sid=keno","page.php?sid=craps",
                                "page.php?sid=bookie","page.php?sid=lottery","page.php?sid=blackjack",
                                "page.php?sid=holdem","page.php?sid=russianRoulette","page.php?sid=spinTheWheel",
                                "page.php?sid=spinTheWheelLastSpins","page.php?sid=slotsStats",
                                "page.php?sid=slotsLastRolls","page.php?sid=rouletteStatistics","page.php?sid=rouletteLastSpins",
                                "page.php?sid=highlowStats","page.php?sid=highlowLastGames",
                                "page.php?sid=kenoStatistics","page.php?sid=kenoLastGames","page.php?sid=crapsStats",
                                "page.php?sid=crapsLastRolls","page.php?sid=bookie#/stats/","page.php?sid=lotteryTicketsBought",
                                "page.php?sid=lotteryPreviousWinners","page.php?sid=blackjackStatistics",
                                "page.php?sid=blackjackLastGames","page.php?sid=holdemStats",
                                "loader.php?sid=viewRussianRouletteLastGames","loader.php?sid=viewRussianRouletteStats",
                                "messageinc2.php#!p=viewall","bazaar.php#/add",
                                "bazaar.php#/personalize","factions.php?step=your#/tab=crimes",
                                "factions.php?step=your#/tab=rank","page.php?sid=events#onlySaved=true",
                                "factions.php?step=your#/tab=controls","factions.php?step=your#/tab=info","messages.php#/p=ignorelist",
                                "messages.php#/p=outbox","factions.php?step=your#/tab=upgrades",
                                "messages.php#/p=saved","messages.php#/p=compose","displaycase.php#add","displaycase.php#manage",
                                "factions.php?step=your#/tab=armoury","bazaar.php#/manage","companies.php",
                                "itemuseparcel.php","index.php?page=rehab","index.php?page=people","christmas_town.php",
                                "christmas_town.php#/mymaps","christmas_town.php#/parametereditor","christmas_town.php#/npceditor",
                                "page.php?sid=UserList","index.php?page=hunting","old_forums.php","donatordone.php","revive.php","pc.php",
                                "loader.php?sid=attackLog","loader.php?sid=attack&user2ID=1","loader.php?sid=crimes","loader.php?sid=crimes#/searchforcash",
                                "loader.php?sid=crimes#/bootlegging","loader.php?sid=crimes#/graffiti","loader.php?sid=crimes#/shoplifting",
                                "loader.php?sid=crimes#/pickpocketing","loader.php?sid=crimes#/cardskimming","loader.php?sid=crimes#/burglary","loader.php?sid=crimes#/hustling",
                                "loader.php?sid=crimes#/disposal","loader.php?sid=crimes#/cracking","loader.php?sid=crimes#/forgery","loader.php?sid=crimes#/scamming",
                                "/war.php?step=rankreport&rankID=69","/war.php?step=warreport&warID=420","/war.php?step=raidreport&raidID=69",
                                "/war.php?step=chainreport&chainID=69420", "page.php?sid=keepsakes",
                                "page.php?sid=crimes2","authenticate.php"];

            const eeeh_options_observer = new MutationObserver(function(mutations) {
                const url = window.location.href;
                if (url.includes("forums.php")) {
                    if (url.includes("f=67&t=16326854") && $('li.parent-post[data-id="23383506"]').length) {
                        if (!document.getElementsByClassName("eeh-options").length) {
                            insertOptions();
                        }
                        eeeh_options_observer.disconnect();
                    }
                } else {
                    eeeh_options_observer.disconnect();
                }
            });

            const eeeh_observer = new MutationObserver(function(mutations) {
                if (document.getElementById("eggTraverse")) {
                    eeeh_observer.disconnect();
                    return;
                }

                if (ButtonFloat) {
                    //insert floating button
                    if (document.getElementsByTagName('body')[0]) {
                        insertFloat();
                        eeeh_observer.disconnect();
                        return;
                    }
                } else {
                    // Insert into sidebar
                    if (document.querySelector('#sidebar > div:first-of-type')) {
                        insertNormal(); // Insert normal sidebar version
                        eeeh_observer.disconnect();
                        return;
                    }
                }
            });


            window.addEventListener(
                "hashchange",
                () => {
                    hashChanged();
                },
                false,
            );

            eeeh_observer.observe(document, obs_ops);
            eeeh_options_observer.observe(document, obs_ops);

            function hashChanged() {
                const url = window.location.href;
                if (url.includes("forums.php")) {
                    eeeh_options_observer.observe(document, obs_ops);
                }
                if (eeh_is_disabled) {
                    setTimeout(() => {
                        eeh_is_disabled = false;
                    }, "1000");
                }
            }

            function getEggLabel(eggButtonType) {
                let eggLabel = `Egg Navigator (${linkIndex})`;
                if (eggButtonType == "float") {
                    eggLabel = `#${linkIndex}`;
                }
                return eggLabel;
            }

            function setEggTraverseClickEvent(eggButtonType) {
                var eggTraverse = $('#eggTraverse');
                var egg_icon = eggTraverse.find('.eeh-icon');
                eggTraverse.on('mousedown touchstart', function(e) {
                    eeh_anim_pressTimer = window.setTimeout(function() {
                        eeh_holding = true;
                        egg_icon.fadeOut(eeh_reset_time);

                        eeh_pressTimer = window.setTimeout(function() {
                            if (eeh_holding) {
                                linkIndex = 0;
                                egg_icon.fadeIn(eeh_fade_in);
                                localStorage.setItem("eeh-index", linkIndex);
                                eggTraverse.attr('href', EVERY_LINK[0]);
                                eggTraverse.find('.eeh-name').text(getEggLabel(eggButtonType));
                            }
                        }, eeh_reset_time);

                    }, eeh_fade_in);
                }).on('mouseup touchend mouseleave', function(e){
                    clearTimeout(eeh_anim_pressTimer);
                    if (eeh_holding) {
                        clearTimeout(eeh_pressTimer);
                        eeh_holding = false;
                        egg_icon.stop(true, true).fadeIn(eeh_fade_in);
                    }
                }).contextmenu(function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    return false;
                }).on('click', function(e) {
                    if (eeh_holding) {
                        eeh_holding = false;
                        egg_icon.stop(true, true).fadeIn(eeh_fade_in);
                    }
                    if (window.event.ctrlKey) {
                        //ctrl was held down during the click
                        incrementEggTraverse(eggButtonType);
                    } else {
                        //normal click
                        if (!eeh_is_disabled) {
                            eeh_is_disabled = true;
                            incrementEggTraverse(eggButtonType);
                        } else {
                            e.preventDefault();
                        }
                    }
                });
            }

            function incrementEggTraverse(eggButtonType) {
                var eggTraverse = $('#eggTraverse');
                linkIndex++;
                if (linkIndex >= EVERY_LINK.length) linkIndex = 0;
                localStorage.setItem("eeh-index", linkIndex);
                eggTraverse.attr('href', EVERY_LINK[linkIndex]);
                eggTraverse.find('.eeh-name').text(getEggLabel(eggButtonType));
            }

            function insertNormal() {
                if (!document.getElementById("eggTraverse")) {
                    let href = EVERY_LINK[linkIndex];

                    let easterspans = `<div class="eeh-link">
                                   <a href="${href}" id="eggTraverse">
                                       <span class="eeh-icon">${easteregg_svg}</span>
                                       <span class="eeh-name">Egg Navigator (${linkIndex})</span>
                                   </a>
                               </div>`;

                    const sidebar = document.getElementById('sidebar');
                    if (sidebar.firstChild) {
                        // Insert the easterspans HTML string after the first child element of sidebar
                        $('#sidebar > *').first().after(easterspans);
                        setEggTraverseClickEvent("sidebar");
                    }
                    insertStyle();
                }
            }

            function insertFloat() {
                if (!document.getElementById("eggTraverse")) {
                    let href = EVERY_LINK[linkIndex];
                    const eeh_float = `<a href="${href}" id="eggTraverse" class="eeh-float">
                                   <span class="eeh-icon">${easteregg_svg}</span>
                                   <span class="eeh-name"> #${linkIndex}</span>
                               </a>`;

                    $('body').append(eeh_float);

                    setFloatPosition();
                    setEggTraverseClickEvent("float");
                    insertStyle();
                }
            }

            function insertOptions() {
                if (!document.getElementsByClassName("eeh-options").length) {
                    const post = $('li.parent-post[data-id="23383506"]').find('div.post-container div.post');
                    let enabled_float = ButtonFloat ? "enabled" : "disabled";
                    let enabledClass_float = ButtonFloat ? "eeh-green" : "eeh-red";

                    let enabled_float_pos;

                    switch(ButtonFloatPos) {
                        case 0:
                            enabled_float_pos = "bottom left";
                            break;
                        case 1:
                            enabled_float_pos = "top left";
                            break;
                        case 2:
                            enabled_float_pos = "bottom right";
                            break;
                        case 3:
                            enabled_float_pos = "top right";
                            break;
                    }

                    post.before(`
                <div class="eeh-options"><button id="eeh-float-toggle">Toggle floating button</button>
                    <p>Floating button: <span id="eeh-float-toggle-label" class="${enabledClass_float}">${enabled_float}</span></p>
                </div>
                <div class="eeh-options"><button id="eeh-float-pos-toggle">Toggle float position</button>
                    <p>Float position: <span id="eeh-float-pos-toggle-label">${enabled_float_pos}</span></p>
                </div>
            `);

                    $('#eeh-float-toggle').click(function() {
                        let label = $('#eeh-float-toggle-label');
                        if (toggleFloatButton()) {
                            label.text("enabled");
                        } else {
                            label.text("disabled");
                        }
                        label.toggleClass('eeh-green eeh-red');
                    });

                    $('#eeh-float-pos-toggle').click(function() {
                        let label = $('#eeh-float-pos-toggle-label');
                        switch(toggleFloatPosition()) {
                            case 0:
                                label.text("bottom left");
                                break;
                            case 1:
                                label.text("top left");
                                break;
                            case 2:
                                label.text("bottom right");
                                break;
                            case 3:
                                label.text("top right");
                                break;
                            default:
                                label.text("Float button is not enabled");
                        }

                    });
                }
            }

            function insertStyle() {
                GM.addStyle(`
    .eeh-link {
      background-color: var(--default-bg-panel-color);
      cursor: pointer;
      overflow: hidden;
      vertical-align: top;
      border-bottom-right-radius: 5px;
      border-top-right-radius: 5px;
      margin-top: 2px;
      height: 23px;
      margin-bottom: 2px;
    }

    .eeh-link:hover {
      background-color: var(--default-bg-panel-active-color);
    }

    .eeh-link a {
      display: flex;
      -ms-align-items: center;
      align-items: center;
      color: var(--default-color);
      text-decoration: none;
      height: 100%;
    }

    .eeh-link a .eeh-icon {
      float: left;
      width: 34px;
      height: 23px;
      display: flex;
      -ms-align-items: center;
      align-items: center;
      justify-content: center;
      margin-left: 0;
    }

    .eeh-link a .eeh-icon {
      stroke: transparent;
      stroke-width: 0;
    }

    .eeh-link a .eeh-name {
      line-height: 22px;
      padding-top: 1px;
      overflow: hidden;
      max-width: 134px;
    }

    .eeh-float.eeh-float-right .eeh-icon {
      order: 1;
    }

    .eeh-float.eeh-float-left .eeh-icon {
      order: 2;
    }

    .eeh-float.eeh-float-right .eeh-name {
      margin-left: 5px;
      order: 2;
    }

    .eeh-float.eeh-float-left .eeh-name {
      margin-right: 5px;
      order: 1;
    }

    .eeh-float .eeh-icon svg {
      width: 20px !important;
      height: 26px !important;
    }

    #eggTraverse.eeh-float {
        z-index: 999999;
        height: 40px;
        width: 80px;
        cursor: pointer;
        padding: 10px 15px 10px 15px;
        box-sizing: border-box;
        border: 1px solid var(--default-panel-divider-outer-side-color);
        position: fixed;
        box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
        display: flex;
        align-items: center;
        text-shadow: var(--default-tabs-text-shadow);
        background: var(--info-msg-bg-gradient);
        box-shadow: var(--default-tabs-box-shadow);
        border-radius: 5px;
        overflow: hidden;
        font-size: 15px;
        font-weight: 700;
        line-height: 18px;
        font-family: arial;
        color: var(--default-color);
        text-decoration: none;
    }

    #eggTraverse.eeh-float.eeh-float-top {
        top: 80px;
    }

    #eggTraverse.eeh-float.eeh-float-bottom {
        bottom: 80px;
    }

    #eggTraverse.eeh-float.eeh-float-left {
        left: -10px;
        padding-right: 5px;
        justify-content: right;
    }

    #eggTraverse.eeh-float.eeh-float-right {
        right: -10px;
        padding-left: 5px;
        justify-content: left;
    }

    [class*='topSection_'] .eeh-icon-svg-wrap {
        position: absolute;
        -ms-transform: translate(-120%, 10%);
        transform: translate(-120%, 10%);
    }

    .content-wrapper > #easterrandom .eeh-icon-svg-wrap {
        position: absolute;
        -ms-transform: translate(-140%, 10%);
        transform: translate(-140%, 10%);
    }

    .eeh-options {
        margin: 20px;
        margin-left: 0px;
    }

    .eeh-options p {
        margin-top: 5px;
        margin-left: 2px;
        font-size: 15px;
        font-weight: 700;
        line-height: 18px;
        font-family: arial;
    }

    .eeh-options button {
        background: transparent linear-gradient(180deg ,#CCCCCC 0%,#999999 60%,#666666 100%) 0 0 no-repeat;
        border-radius: 5px;
        font-family: Arial,sans-serif;
        font-size: 14px;
        font-weight: 700;
        text-align: center;
        letter-spacing: 0;
        color: #333;
        text-shadow: 0 1px 0 #ffffff66;
        text-decoration: none;
        text-transform: uppercase;
        margin: 0;
        border: none;
        outline: none;
        overflow: visible;
        box-sizing: border-box;
        line-height: 16px;
        padding: 4px 8px;
        height: auto;
        white-space: nowrap;
        cursor: pointer;
        margin-right: 5px;
    }

    .eeh-green {
        color: var(--user-status-green-color);
    }

    .eeh-red {
        color: var(--user-status-red-color);
    }


    @media screen and (max-width: 1000px) {
        html:not(.html-manual-desktop) [class*='topSection_'] #easterrandom span.eeh-text, .content-wrapper > #easterrandom span.eeh-text {
            display: none;
        }

        [class*='topSection_'] .eeh-icon-svg-wrap {
            -ms-transform: translate(-140%, -110%);
            transform: translate(-140%, -110%);
        }

        html:not(.html-manual-desktop) #eggTraverse.eeh-float.eeh-float-top {
            top: 170px !important;
        }
    }

    /* SVG Colors */
    .eeh-link svg, .eeh-icon-svg svg {
      filter: drop-shadow(0px 0.7px 0.1px #fff);
      width: 13px !important;
      height: 17px !important;
    }
    .eeh-icon-svg svg path {
      fill: #AFC372 !important;
    }
    body.dark-mode .eeh-icon svg, body.dark-mode .eeh-icon-svg svg {
      filter: drop-shadow(0px 0px 1.3px #000);
    }

    /* Torn Edits */
    .members-cont>.member-item>a[href="profiles.php?XID=1468764"]>.member>.member-header {
        color: #E0CE00 !important;
    }

    .members-cont>.member-item>a[href="profiles.php?XID=1468764"]>.member>.member-cont>span::after {
        content: "👑  " url("https://profileimages.torn.com/ad324318-744c-c686-1468764.gif?v=1940629196397");
    }
    `);
            }

            function killButton() {
                let eeh_button = document.getElementById("eggTraverse");
                if (eeh_button) {
                    let parent = eeh_button.closest(`.eeh-link`);
                    if (parent) {
                        parent.remove();
                    } else {
                        eeh_button.remove();
                    }
                }
            }

            function toggleFloatButton() {
                killButton();
                if (ButtonFloat) {
                    ButtonFloat = 0;
                    insertNormal();
                } else {
                    ButtonFloat = 1;
                    insertFloat();
                }
                localStorage.setItem("eeh-float", ButtonFloat);
                return ButtonFloat;
            }

            function toggleFloatPosition() {
                let float_button = document.querySelector("#eggTraverse.eeh-float");
                if (!float_button) return;

                ButtonFloatPos++;
                if (ButtonFloatPos >= 4) ButtonFloatPos = 0; //cycle back to 0=bottom-left
                setFloatPosition();
                return ButtonFloatPos;
            }

            function setFloatPosition() {
                let float_button = document.querySelector("#eggTraverse.eeh-float");
                if (!float_button) return;

                float_button.classList.remove("eeh-float-bottom", "eeh-float-top", "eeh-float-left", "eeh-float-right");

                switch(ButtonFloatPos) {
                    case 0:
                        float_button.classList.add("eeh-float-bottom", "eeh-float-left");
                        break;
                    case 1:
                        float_button.classList.add("eeh-float-top", "eeh-float-left");
                        break;
                    case 2:
                        float_button.classList.add("eeh-float-bottom", "eeh-float-right");
                        break;
                    case 3:
                        float_button.classList.add("eeh-float-top", "eeh-float-right");
                        break;
                    default:
                        float_button.classList.add("eeh-float-bottom", "eeh-float-left");
                }

                localStorage.setItem("eeh-float-pos", ButtonFloatPos);
            }
        } else {
            // optional: do nothing or log
            console.log("Outside April 1–9, easter scripts not running.");
        }
    })();

    // name Fast Packs
    // author Hemicopter [2780600]

    (function() {
        'use strict';


        //Removes all images, leaving only the result text and buttons.
        //This should prevent the "Use Another" button moving most of the time
        let removeVisuals = true;



        // cc Manuito
        let GM_addStyle = function(s) {
            let style = document.createElement("style");
            style.type = "text/css";
            style.innerHTML = s;
            document.head.appendChild(style);
        };

        GM_addStyle(`
        .d .pack-open-content.disabled-link .pack-open-msg a.open-another-cache {
    	    pointer-events: auto !important;
            cursor: default !important;
            color: var(--default-blue-color) !important;
        }

        .d .pack-open-msg {
    	    animation-duration: 0s !important;
            animation-delay: 0s !important;
        }

        .d .animated-fadeIn {
    	    opacity: 1 !important;
        }

        .d .animated {
    	    animation-duration: 0s !important;
        }

        .d .pack-open-result .cache-item:nth-child(2) {
    	    animation-delay: 0s !important;
        }

        .d .pack-open-result .item-amount {
    	    animation: none !important;
            animation-delay: 0s !important;
            opacity: 1 !important;
        }

        .r .pack-open-result {
            animation-name: none !important;
            animation-duration: 0s !important;
        }

        .d .pack-open-result-divider.visible {
            transition: none !important;
        }
        `);

        if(removeVisuals) {
            GM_addStyle(`
                .pack-open-result {
    	            visibility: hidden !important;
                    height: 0px !important;
                }
                .d .cache_wrapper,
                .d .pack-open-result-divider {
                    display: none !important
                }
            `);
        }

    })();


    const sheepattack = localStorage.getItem("sheepattackAPI");

    if (isToggleEnabled()) {
        if (sheepattack === null) {createButtonAtPositionsheepattack("API", 0, 0);}
        else {

            (function () {
                'use strict';

                const STORAGE_KEY = "SheepAttackSetting";
                const OPTIONS = ["Primary", "Secondary", "Melee", "Temp"];

                // Initialize storage if missing
                if (!localStorage.getItem(STORAGE_KEY)) {
                    localStorage.setItem(STORAGE_KEY, OPTIONS[0]);
                }

                // Get current value
                let currentValue = localStorage.getItem(STORAGE_KEY);

                // Create button
                const btn = document.createElement("button");
                btn.textContent = currentValue;
                btn.style.position = "absolute";
                btn.style.top = "150px";
                btn.style.right = "40px";
                btn.style.padding = "8px 14px";
                btn.style.borderRadius = "8px";
                btn.style.border = "1px solid #888";
                btn.style.cursor = "pointer";
                btn.style.zIndex = "9999";
                btn.style.background = "#222";
                btn.style.backgroundColor = sheepColour;

                btn.addEventListener("click", function () {
                    let currentIndex = OPTIONS.indexOf(localStorage.getItem(STORAGE_KEY));
                    let nextIndex = (currentIndex + 1) % OPTIONS.length;

                    localStorage.setItem(STORAGE_KEY, OPTIONS[nextIndex]);

                    location.reload(); // Reload page
                });

                document.body.appendChild(btn);

            })();
            createButtonAtPositionsheepattackoff("API", 0, 0);}}
    function createButtonAtPositionsheepattackoff(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.style.width = sheepwide2 + "px";
        customButton.style.height = sheeptall2 + "px";
        customButton.innerHTML = 'Disable FastAttack';
        customButton.style.position = 'absolute';
        customButton.style.top = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '999999';
        customButton.style.fontSize = (sheeptall2 * 0.4) + "px";
        customButton.addEventListener('click', function() {
            localStorage.removeItem("sheepattackAPI");
            customButton.style.display = 'none';
            window.location.reload();;
        });

        requestIdleCallback(() => {document.body.appendChild(customButton); });
    }

    function createButtonAtPositionsheepattack(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.style.width = sheepwide2 + "px";
        customButton.style.height = sheeptall2 + "px";
        customButton.innerHTML = 'Enable FastAttack';
        customButton.style.position = 'absolute';
        customButton.style.top = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '999999';
        customButton.style.fontSize = (sheeptall2 * 0.4) + "px";
        customButton.addEventListener('click', function() {
            const sheepattackAPI = localStorage.getItem("sheepattackAPI");
            if (sheepattackAPI === null) {
                const sheepAttack = ("yes");
                localStorage.setItem("sheepattackAPI", sheepAttack);}
            window.location.reload();;
        });

        requestIdleCallback(() => {document.body.appendChild(customButton); });
    }



    // Attack Better
    // Auther smokey_ [2492729]

    (function () {
        'use strict';
        const attackSetting = localStorage.getItem("SheepAttackSetting") || "Primary";
        const BUTTON_LOCATION = attackSetting; // Change this value to 'Temp' to move the button to the Temp (HEG, Tear, Smoke) or Primary to move it ontop of the Primary Weapon element or Secondary or Melee

        function moveStartFightButton() {
            let startFightButton, weaponImage, weaponWrapper;

            if (BUTTON_LOCATION === 'Primary') {
                startFightButton = document.querySelector('.torn-btn.btn___RxE8_.silver'); // start fight button
                weaponImage = document.querySelector('.weaponImage___tUzwP img'); // equipped weapon image
                weaponWrapper = document.querySelector('.weaponWrapper___h3buK'); // common parent element

            } else if (BUTTON_LOCATION === 'Secondary') {
                startFightButton = document.querySelector('.torn-btn.btn___RxE8_.silver');
                weaponImage = document.querySelector('#weapon_second .weaponImage___tUzwP img');
                weaponWrapper = document.querySelector('#weapon_second');

            } else if (BUTTON_LOCATION === 'Melee') {
                startFightButton = document.querySelector('.torn-btn.btn___RxE8_.silver');
                weaponImage = document.querySelector('#weapon_melee .weaponImage___tUzwP img');
                weaponWrapper = document.querySelector('#weapon_melee');

            } else if (BUTTON_LOCATION === 'Temp') {
                startFightButton = document.querySelector('.torn-btn.btn___RxE8_.silver');
                weaponImage = document.querySelector('#weapon_temp .weaponImage___tUzwP img');
                weaponWrapper = document.querySelector('#weapon_temp');
            }



            if (startFightButton && weaponImage && weaponWrapper) {
                
                const buttonWrapper = document.createElement('div'); // create new div element
                buttonWrapper.classList.add('button-wrapper');
                buttonWrapper.appendChild(startFightButton); // append start fight button to new div element
                weaponWrapper.insertBefore(buttonWrapper, weaponImage.nextSibling); // insert new div element after equipped weapon image
                

                // Position the button wrapper over the weapon image
                buttonWrapper.style.position = 'absolute';
                buttonWrapper.style.top = weaponImage.offsetTop + 'px';
                buttonWrapper.style.left = '+15px'; // set left position to move it to the left
                startFightButton.addEventListener('click', function() {
                    buttonWrapper.remove();
                    
                });
            }
        }

        let loopCount = 0;
        const buttonIntervalId = setInterval(function () {
            loopCount++;
            if (loopCount > 5) { // stop the loop after 5s (20 loops * 250ms per loop = 5s)
                clearInterval(buttonIntervalId);
                
                return;
            }
            const sheepattackAPI = localStorage.getItem("sheepattackAPI");
            if (sheepattackAPI === 'yes') {
                moveStartFightButton();
                if (document.querySelector('.button-wrapper')) { // check if the button has been moved
                    clearInterval(buttonIntervalId);
                    
                }}
        }, 250);

        // Wait for page to load before executing this part of the script
        window.addEventListener('load', function () {

            //
            // Element Stripping
            //

            // get the custom-bg-desktop sidebar-off element
            const sidebarElement = document.querySelector('.custom-bg-desktop.sidebar-off');

            // if the element exists, remove it from the DOM to prevent it from being downloaded or loaded
            if (sidebarElement) {
                sidebarElement.remove();
            }
        })

        // Defender Model
        var startTimeDefender = Date.now();
        var intervalIdDefender = setInterval(function() {
            if (Date.now() - startTimeDefender > 5000) {
                clearInterval(intervalIdDefender);
                return;
            }

            var defenderModel = document.querySelectorAll("#defender > div.playerArea___oG4xu > div.playerWindow___FvmHZ > div > div.modelLayers___FdSU_.center___An_7Z > div.modelWrap___j3kfA *");

            for (const element of defenderModel) {
                element.remove();
            }
        }, 100);

        // Attacker Model
        var startTimeAttacker = Date.now();
        var intervalIdAttacker = setInterval(function() {
            if (Date.now() - startTimeAttacker > 5000) {
                clearInterval(intervalIdAttacker);
                return;
            }

            var attackerModel = document.querySelectorAll("#attacker > div.playerArea___oG4xu > div.playerWindow___FvmHZ > div.allLayers___cXY5i > div.modelLayers___FdSU_.center___An_7Z > div.modelWrap___j3kfA *");

            for (const element of attackerModel) {
                element.remove();
            }
        }, 100);
    })();



    //
    //
    // Dont forget your xanax
    // author Shade

    if (localStorage.getItem("warmode") === "true") {
        // do warmode stuff

        if (window.location.href !== "https://www.torn.com/loader.php?sid=attack*") {
            (function() {
                'use strict';
                $(document).ready(function() {
                    setTimeout(function() {
                        var drugcd = document.querySelector("[aria-label^='Drug Cooldown:']");
                        if (drugcd == null) {
                            const BANNER_ID = "xanaxBanner";
                            const banner = document.createElement("div");
                            banner.id = BANNER_ID;
                            banner.textContent = "Take your fwickin Xanax";

                            // Styling
                            banner.style.position = "fixed";
                            banner.style.top = "20px";
                            banner.style.left = "50%";
                            banner.style.transform = "translateX(-50%)";
                            banner.style.background = "#8b0000"; // original red
                            banner.style.color = "white";
                            banner.style.padding = "10px 20px";
                            banner.style.fontSize = "16px";
                            banner.style.fontWeight = "bold";
                            banner.style.borderRadius = "8px";
                            banner.style.boxShadow = "0 4px 12px rgba(0,0,0,0.4)";
                            banner.style.zIndex = "999999";
                            banner.style.cursor = "pointer";
                            banner.style.transition = "opacity 0.5s ease";

                            // Append to body
                            document.body.appendChild(banner);

                            // Optional: fade out after 3 seconds
                            setTimeout(() => {
                                banner.style.opacity = "0";
                                setTimeout(() => banner.remove(), 500);
                            }, 30000);
                        }
                    }, 3000);
                });
            })();
        }
    }

    // sheepchain


    const sheepchain = localStorage.getItem("sheepchain");
    if (sheepchain === null) {createButtonAtPositionsheepchain("API", 120, 10);}
    else {createButtonAtPositionsheepchainoff("API", 120, 10);
          createButtonAtPositionsheepchainactive("API", 10, 10);}
    function createButtonAtPositionsheepchainoff(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.style.width = sheepwide + "0px";
        customButton.style.height = sheeptall + "px";
        customButton.innerHTML = 'Disable SheepChain';
        customButton.style.position = 'absolute';
        customButton.style.top = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '99999';
        customButton.style.display = 'none';
        customButton.addEventListener('click', function() {
            localStorage.removeItem("sheepchain");
            customButton.style.display = 'none';
            window.location.reload();;
        });

        document.body.appendChild(customButton);
    }

    function createButtonAtPositionsheepchain(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.style.width = sheepwide + "px";
        customButton.style.height = sheeptall + "px";
        customButton.innerHTML = 'Enable SheepChain';
        customButton.style.position = 'absolute';
        customButton.style.top = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '99999';
        customButton.style.display = 'none';
        customButton.addEventListener('click', function() {
            const sheepchain = localStorage.getItem("sheepchain");
            if (sheepchain === null) {
                const sheepchain = ("yes");
                localStorage.setItem("sheepchain", sheepchain);}
            window.location.reload();;
        });

        document.body.appendChild(customButton);
    }



    function createButtonAtPositionsheepchainactive(text, topPx, leftPx) {
        var customButton = document.createElement("button");
        customButton.style.width = sheepwide + "px";
        customButton.style.height = sheeptall + "px";
        customButton.innerHTML = 'SheepChain Active';
        customButton.style.position = 'absolute';
        customButton.style.top = topPx + 'px';
        customButton.style.right = leftPx + 'px';
        customButton.style.backgroundColor = sheepColour;
        customButton.style.color = 'black';
        customButton.style.border = 'none';
        customButton.style.cursor = 'pointer';
        customButton.style.border = "1px solid purple";
        customButton.style.zIndex = '99999';
        customButton.style.display = 'none';
        customButton.addEventListener('load', function() {
            const sheepchain = localStorage.getItem("sheepchain");
            if (sheepchain === 'yes') {
                customButton.style.display = 'none';
            }
        });

        document.body.appendChild(customButton);
    }



    //Torn Territory War Time Left
    //Ramin Quluzade, Silmaril [2665762]

    (function() {
        'use strict';

        const targetElementSelector = '.f-war-list.war-new';
        const observerOptions = { childList: true, subtree: true };

        const observerCallback = async function(mutationsList, observer) {
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    const targetElement = document.querySelector(targetElementSelector);
                    if (targetElement) {
                        let territoryWars = mutation.target.querySelectorAll(".f-war-list.war-new div[class^='status-wrap territoryBox']");
                        if (territoryWars.length > 0) {
                            territoryWars.forEach(war => {
                                war.querySelector('.info .faction-progress-wrap').style.paddingTop = '0px';
                                let timeLeftElement = document.createElement('div');
                                timeLeftElement.classList.add('time-left', 'timer');
                                let timeLeftBestElement = document.createElement('div');
                                timeLeftBestElement.classList.add('time-left-best', 'timer');
                                war.querySelector('.info .faction-progress-wrap').append(timeLeftElement, timeLeftBestElement);
                            });
                            territoryWars.forEach(war => {
                                let enemyCountDiv = war.querySelector('.info .member-count.enemy-count .count');
                                let allyCountDiv = war.querySelector('.info .member-count.your-count .count');

                                renderTimeLeft(war);

                                // Set up a MutationObserver on the added child element
                                const childObserver = new MutationObserver(function(childMutations) {
                                    childMutations.forEach(function(childMutation) {
                                        if (childMutation.type === 'characterData') {
                                            let territoryWar = childMutation.target.parentNode.parentNode.parentNode.parentNode;
                                            renderTimeLeft(territoryWar);
                                        }
                                    });
                                });

                                setInterval(renderTimeLeft, 1000 + Math.floor(Math.random() * 10) + 1, war);

                                childObserver.observe(enemyCountDiv, { characterData: true, subtree: true });
                                childObserver.observe(allyCountDiv, { characterData: true, subtree: true });
                            });
                            observer.disconnect();
                        }
                    }
                }
            }
        };

        const observer = new MutationObserver(observerCallback);
        observer.observe(document.documentElement, observerOptions);

        function renderTimeLeft(war) {
            let enemyCountDiv = war.querySelector('.info .member-count.enemy-count .count');
            let allyCountDiv = war.querySelector('.info .member-count.your-count .count');
            let enemyCount = Number(enemyCountDiv.innerText);
            let allyCount = Number(allyCountDiv.innerText);
            let isAllyAttack = war.querySelector('.info .member-count.your-count .count i').classList.contains('swords-icon');
            let remainder = isAllyAttack ? allyCount - enemyCount : enemyCount - allyCount;
            let timeLeft = '??:??:??:??';
            let timeLeftBest = '??:??:??:??';
            let scoreText = war.querySelector('.info .faction-progress-wrap .score').innerText;
            let score = scoreText.replaceAll(',', '').split('/');
            let pointsLeft = Number(score[1]) - Number(score[0]);
            let maximumSlots = Number(score[1]) / 50000;
            if (remainder > 0) {
                let secondsUntilGoal = pointsLeft / remainder;
                timeLeft = convertSecondsToDHMS(secondsUntilGoal);
            }
            timeLeftBest = convertSecondsToDHMS(pointsLeft / maximumSlots);
            let timeLeftDiv = war.querySelector('.info .faction-progress-wrap .time-left');
            let timeLeftBestDiv = war.querySelector('.info .faction-progress-wrap .time-left-best');
            const timeLeftCharacters = timeLeft.split('');
            const timeLeftBestCharacters = timeLeftBest.split('');
            const timeLeftSpanArray = ['CURRENT '];
            timeLeftCharacters.forEach(char => {
                const span = document.createElement('span');
                span.textContent = char;
                timeLeftSpanArray.push(span);
            });
            timeLeftDiv.replaceChildren(...timeLeftSpanArray);
            const timeLeftBestSpanArray = ['BESTCASE '];
            timeLeftBestCharacters.forEach(char => {
                const span = document.createElement('span');
                span.textContent = char;
                timeLeftBestSpanArray.push(span);
            });
            timeLeftBestDiv.replaceChildren(...timeLeftBestSpanArray);
        }

        function convertSecondsToDHMS(seconds) {
            if (seconds === Infinity){
                return '??:??:??:??';
            }

            const oneDay = 86400; // number of seconds in a day
            const oneHour = 3600; // number of seconds in an hour
            const oneMinute = 60; // number of seconds in a minute

            // Calculate the number of days, hours, minutes, and seconds
            const days = Math.floor(seconds / oneDay);
            const hours = Math.floor((seconds % oneDay) / oneHour);
            const minutes = Math.floor((seconds % oneHour) / oneMinute);
            const remainingSeconds = Math.round(seconds % oneMinute);

            // Construct a formatted string with the results
            let output = '';
            output += `${days.toString().padStart(2, '0')}:`;
            output += `${hours.toString().padStart(2, '0')}:`;
            output += `${minutes.toString().padStart(2, '0')}:`;
            output += `${remainingSeconds.toString().padStart(2, '0')}`;

            return output;
        }
    })();
    let apiKey = localStorage.getItem("sheepAPI") ?? '###PDA-APIKEY###';
    const REQUIRED_FACTION = 14432;
    let sheepAuthorized = false;
    function checkFaction(callback) {
        let storedFaction = localStorage.getItem("sheepFaction");
        if (storedFaction) {
            sheepAuthorized = Number(storedFaction) === REQUIRED_FACTION;
            callback(sheepAuthorized);
            return;
        }
        GM_xmlhttpRequest({
            method: "GET",
            url: `https://api.torn.com/v2/user/faction?key=${apiKey}`,
            onload: function(response) {
                const data = JSON.parse(response.responseText);
                if (data.error) {
                    callback(false);
                    return;
                }
                const factionID = data.faction?.id;
                localStorage.setItem("sheepFaction", factionID);
                sheepAuthorized = Number(factionID) === REQUIRED_FACTION;
                callback(sheepAuthorized);
            }
        });
    }
    (function() {
        'use strict';

        const TIMER_MAX_MS = 5 * 60 * 1000;
        const TOGGLE_BTN_SIZE = 40;

        let timerRemainingMs = null;
        let lastPulledValue = null;
        let timerDiv, toggleBtn;
        let lastUpdate = Date.now();
        let flashing = false;

        // Toggle persistence
        let toggleEnabled = localStorage.getItem('sheepTimerToggle') === 'true';

        function formatTime(ms) {
            const totalSec = Math.floor(ms / 1000);
            const m = Math.floor(totalSec / 60);
            const s = totalSec % 60;
            return `${m.toString().padStart(2,'0')}:${s.toString().padStart(2,'0')}`;
        }

        function checkSessionStorage() {
            const stored = sessionStorage.getItem('floatingTimerRemaining');
            if (stored !== null && stored !== lastPulledValue) {
                lastPulledValue = stored;
                const val = parseInt(stored);
                if (!isNaN(val) && val > 0) {
                    timerRemainingMs = Math.min(val, TIMER_MAX_MS);
                    if (timerDiv) timerDiv.style.display = toggleEnabled ? 'flex' : 'none';
                }
            }
        }

        function createTimerDiv() {
            if (timerDiv) return;

            timerDiv = document.createElement('div');
            timerDiv.id = 'floatingTimer';
            timerDiv.style.position = 'fixed';
            timerDiv.style.width = localStorage.getItem('timerWidth') || (window.innerWidth*0.3)+'px';
            timerDiv.style.height = localStorage.getItem('timerHeight') || '60px';
            timerDiv.style.left = localStorage.getItem('timerLeft') || (window.innerWidth*0.35)+'px';
            timerDiv.style.top = localStorage.getItem('timerTop') || (window.innerHeight*0.35)+'px';
            timerDiv.style.display = toggleEnabled ? 'flex' : 'none';
            timerDiv.style.alignItems = 'center';
            timerDiv.style.justifyContent = 'center';
            timerDiv.style.fontWeight = 'bold';
            timerDiv.style.backgroundColor = 'black';
            timerDiv.style.border = '3px solid green';
            timerDiv.style.borderRadius = '10px';
            timerDiv.style.cursor = 'move';
            timerDiv.style.userSelect = 'none';
            timerDiv.style.zIndex = '99999999';
            timerDiv.innerText = '';
            document.body.appendChild(timerDiv);
            makeDraggable(timerDiv);
            makeResizable(timerDiv);
        }

        function updateFontSize() {
            if (!timerDiv) return;
            const w = timerDiv.clientWidth;
            const h = timerDiv.clientHeight;
            timerDiv.style.fontSize = Math.min(h*0.6, w*0.2) + 'px';
        }

        function makeDraggable(div) {
            let offsetX, offsetY, isDragging = false;
            div.addEventListener('mousedown', e => {
                isDragging = true;
                offsetX = e.clientX - div.getBoundingClientRect().left;
                offsetY = e.clientY - div.getBoundingClientRect().top;
            });
            document.addEventListener('mousemove', e => {
                if (isDragging) {
                    div.style.left = e.clientX - offsetX + 'px';
                    div.style.top = e.clientY - offsetY + 'px';
                }
            });
            document.addEventListener('mouseup', () => {
                if (isDragging) {
                    isDragging = false;
                    localStorage.setItem('timerLeft', div.style.left);
                    localStorage.setItem('timerTop', div.style.top);
                }
            });
        }

        function makeResizable(div) {
            div.style.resize = 'both';
            div.style.overflow = 'auto';
            const observer = new ResizeObserver(() => {
                localStorage.setItem('timerWidth', div.style.width);
                localStorage.setItem('timerHeight', div.style.height);
                updateFontSize();
            });
            observer.observe(div);
        }

        // Gradient from green -> yellow -> orange -> red
        function getGradientColor(perc) {
            if (perc > 0.66) { // green -> yellow
                const ratio = (perc - 0.66)/0.34;
                const r = Math.floor(255*(1-ratio));
                const g = 255;
                return `rgb(${r},${g},0)`;
            } else if (perc > 0.33) { // yellow -> orange
                const ratio = (perc - 0.33)/0.33;
                const r = 255;
                const g = Math.floor(255*ratio);
                return `rgb(${r},${g},0)`;
            } else { // orange -> red
                const ratio = perc/0.33;
                const r = 255;
                const g = Math.floor(165*ratio); // orange(255,165,0) -> red(255,0,0)
                return `rgb(${r},${g},0)`;
            }
        }

        function updateText() {
            if (!timerDiv || timerRemainingMs === null || timerRemainingMs <= 0) return;
            const perc = Math.min(Math.max(timerRemainingMs / TIMER_MAX_MS, 0),1);
            const color = getGradientColor(perc);
            timerDiv.style.color = color;
            timerDiv.style.borderColor = color;
            timerDiv.innerText = formatTime(timerRemainingMs);
            updateFontSize();
        }

        function triggerSadBaaahs() {
            if (!timerDiv) return;
            flashing = true;
            let flashes = 6;
            let flashInterval = setInterval(()=>{
                timerDiv.innerText = (flashes%2===0)?'sad baaahs':'';
                timerDiv.style.color = 'red';
                timerDiv.style.borderColor = 'red';
                flashes--;
                if (flashes<=0){
                    clearInterval(flashInterval);
                    timerDiv.style.display = 'none';
                    timerRemainingMs = null;
                    flashing = false;
                    sessionStorage.removeItem('floatingTimerRemaining');
                }
            }, 200);
        }

        function startLoop() {
            lastUpdate = Date.now();
            setInterval(()=>{
                const now = Date.now();
                const delta = now - lastUpdate;
                lastUpdate = now;

                if (!flashing) {
                    if (timerRemainingMs !== null && timerRemainingMs > 0) {
                        const prev = timerRemainingMs;
                        timerRemainingMs -= delta;
                        if (timerRemainingMs <= 0 && prev > 0) triggerSadBaaahs();
                    }
                    checkSessionStorage();
                    if (toggleEnabled) updateText();
                }
            }, 50);
        }

        function createToggleButton() {
            toggleBtn = document.createElement('button');
            toggleBtn.innerHTML = '⏱';
            toggleBtn.style.position = 'fixed';
            //       toggleBtn.style.width = TOGGLE_BTN_SIZE+'px';
            //        toggleBtn.style.height = TOGGLE_BTN_SIZE+'px';
            toggleBtn.style.width = +'30px';
            toggleBtn.style.height = '25px';
            toggleBtn.style.bottom = '60px';
            toggleBtn.style.left = '50px';
            toggleBtn.style.border = 'none';
            toggleBtn.style.borderRadius = '5px';
            toggleBtn.style.background = sheepColour;
            toggleBtn.style.color = 'black';
            toggleBtn.style.fontSize = '1.5em';
            toggleBtn.style.cursor = 'pointer';
            toggleBtn.style.zIndex = '99999';
            toggleBtn.style.opacity = '0.9';
            if (isTornPDA()) { toggleBtn.style.position = 'absolute';
                              toggleBtn.style.top = '3330px';
                              toggleBtn.style.right = '3330px';
                              toggleBtn.style.width = '0px';}
            toggleBtn.addEventListener('click', ()=>{
                toggleEnabled = !toggleEnabled;
                localStorage.setItem('sheepTimerToggle', toggleEnabled);
                if (timerDiv) timerDiv.style.display = toggleEnabled ? 'flex' : 'none';
                window.location.reload();
            });

            requestIdleCallback(() => {if (isToggleEnabled()) {document.body.appendChild(toggleBtn);}});
        }

        createTimerDiv();
        createToggleButton();
        startLoop();

    })();


    // ================================
    // 🐑 Sheepie TCT Timezone Engine w/ Persistence
    // ================================

    (function () {
        'use strict';

        if (document.getElementById("sheepieTctWrapper")) return;

        // =====================
        // Base Zones
        // =====================

        const DEFAULT_ZONES = [
            { label: "Netherlands", zone: "Europe/Amsterdam" },
            { label: "Syria", zone: "Asia/Damascus" },
            { label: "Thailand", zone: "Asia/Bangkok" },
            { label: "Malaysia", zone: "Asia/Kuala_Lumpur" },
            { label: "Halifax", zone: "America/Halifax" },
            { label: "Toronto", zone: "America/Toronto" },
            { label: "Winnipeg", zone: "America/Winnipeg" },
            { label: "Calgary", zone: "America/Edmonton" },
            { label: "Vancouver", zone: "America/Vancouver" }
        ];

        const STORAGE_KEY = "sheepieTctZones";
        let activeZones = [];

        // Load saved zones if exist
        const saved = localStorage.getItem(STORAGE_KEY);
        if (saved) {
            try {
                activeZones = JSON.parse(saved);
            } catch {
                activeZones = [...DEFAULT_ZONES];
            }
        } else {
            activeZones = [...DEFAULT_ZONES];
        }

        const allTimezones = Intl.supportedValuesOf("timeZone");

        let interval = null;
        let manualMode = false;

        // =====================
        // Wrapper
        // =====================

        const wrapper = document.createElement("div");
        wrapper.id = "sheepieTctWrapper";
        wrapper.style.position = "fixed";
        wrapper.style.bottom = "40px";
        wrapper.style.right = "0px";
        wrapper.style.zIndex = "9999";
        wrapper.style.fontFamily = "Verdana, sans-serif";
        requestIdleCallback(() => {if (isToggleEnabled()) {document.body.appendChild(wrapper);}});

        const toggle = document.createElement("button");
        toggle.textContent = "⏰";
        toggle.style.background = sheepColour;
        toggle.style.color = "#000";
        toggle.style.border = "none";
        toggle.style.cursor = "pointer";
        toggle.style.borderRadius = "4px";
        toggle.style.fontSize = "100%";
        wrapper.appendChild(toggle);

        const panel = document.createElement("div");
        panel.style.display = "none";
        panel.style.marginTop = "6px";
        panel.style.background = "#111";
        panel.style.border = `1px solid ${sheepColour}`;
        panel.style.padding = "10px";
        panel.style.borderRadius = "6px";

        panel.style.width = "150px";
        wrapper.appendChild(panel);

        // =====================
        // Manual TCT Time
        // =====================

        const manualInput = document.createElement("input");
        manualInput.type = "time";
        manualInput.step = "1";
        manualInput.style.width = "100%";
        manualInput.style.marginBottom = "6px";
        panel.appendChild(manualInput);






        const manualToggle = document.createElement("button");
        manualToggle.textContent = "Live Mode";
        manualToggle.style.width = "100%";
        manualToggle.style.marginBottom = "10px";
        manualToggle.style.cursor = "pointer";
        panel.appendChild(manualToggle);
        // =====================
        // Sheepie Dropdown Manual Time Picker
        // =====================

        const pickerContainer = document.createElement("div");
        pickerContainer.style.display = "none";
        pickerContainer.style.position = "absolute";
        pickerContainer.style.top = "25px"; // just below manual input
        pickerContainer.style.left = "10";
        pickerContainer.style.background = "#111";
        pickerContainer.style.border = `1px solid ${sheepColour}`;
        pickerContainer.style.borderRadius = "6px";
        pickerContainer.style.padding = "6px";
        pickerContainer.style.zIndex = "999";
        pickerContainer.style.display = "flex";
        pickerContainer.style.gap = "6px";
        pickerContainer.style.fontSize = "12px";

        // attach to the parent of manualInput
        manualInput.parentElement.style.position = "relative";
        manualInput.parentElement.appendChild(pickerContainer);

        // helper to create a select element
        function createSelect(min, max) {
            const sel = document.createElement("select");
            sel.style.background = "#222";
            sel.style.color = "#fff";
            sel.style.border = `1px solid ${sheepColour}`;
            sel.style.borderRadius = "4px";
            sel.style.padding = "2px 4px";
            sel.style.cursor = "pointer";
            for (let i = min; i <= max; i++) {
                const opt = document.createElement("option");
                opt.value = i.toString().padStart(2, "0");
                opt.textContent = i.toString().padStart(2, "0");
                sel.appendChild(opt);
            }
            return sel;
        }

        // create hour, minute, second selects
        const hourSelect = createSelect(0, 23);
        const minuteSelect = createSelect(0, 59);
        const secondSelect = createSelect(0, 59);
        pickerContainer.appendChild(hourSelect);
        pickerContainer.appendChild(minuteSelect);
        pickerContainer.appendChild(secondSelect);

        // sync picker -> manualInput
        function updateManualInputFromPicker() {
            const h = hourSelect.value;
            const m = minuteSelect.value;
            const s = secondSelect.value;
            manualInput.value = `${h}:${m}:${s}`;
            updateDisplay(); // call your existing update function
        }

        // sync manualInput -> picker
        function syncPickerToInput() {
            const [h, m, s] = manualInput.value.split(":").map(v => v || "00");
            hourSelect.value = h.padStart(2, "0");
            minuteSelect.value = m.padStart(2, "0");
            secondSelect.value = s.padStart(2, "0");

        }

        // show picker when manualInput is focused
        manualInput.addEventListener("focus", () => {
            pickerContainer.style.display = "flex";
            syncPickerToInput();
            manualMode = true;
            manualToggle.textContent = "Manual Mode Active";
            manualToggle.style.background = sheepColour;
            manualToggle.style.color = "#000";
        });
        [hourSelect, minuteSelect, secondSelect].forEach(sel => {
            sel.addEventListener("change", () => {
                manualMode = true; // pause live updates
                manualToggle.textContent = "Manual Mode Active";
                manualToggle.style.background = sheepColour;
                manualToggle.style.color = "#000";

                updateManualInputFromPicker();
            });

            // Also trigger manual mode if user clicks the select
            sel.addEventListener("mousedown", () => {
                manualMode = true;
                manualToggle.textContent = "Manual Mode Active";
                manualToggle.style.background = sheepColour;
                manualToggle.style.color = "#000";
            });
        });

        // hide picker if click outside
        document.addEventListener("click", (e) => {
            if (!pickerContainer.contains(e.target) && e.target !== manualInput) {
                pickerContainer.style.display = "none";
            }
        });

        // update manualInput when pickers change
        [hourSelect, minuteSelect, secondSelect].forEach(sel => {
            sel.addEventListener("change", updateManualInputFromPicker);
        });
        // =====================
        // Search Bar
        // =====================

        const searchInput = document.createElement("input");
        searchInput.type = "text";
        searchInput.placeholder = "Search timezone...";
        searchInput.style.width = "100%";
        searchInput.style.marginBottom = "6px";
        panel.appendChild(searchInput);

        const searchResults = document.createElement("div");
        searchResults.style.maxHeight = "120px";
        searchResults.style.overflowY = "auto";
        searchResults.style.fontSize = "11px";
        searchResults.style.marginBottom = "10px";
        panel.appendChild(searchResults);

        // =====================
        // Output
        // =====================

        const output = document.createElement("div");
        panel.appendChild(output);

        // =====================
        // Helpers
        // =====================

        function saveZones() {
            localStorage.setItem(STORAGE_KEY, JSON.stringify(activeZones));
        }

        function getTctDate() {
            const now = new Date();

            if (manualMode && manualInput.value) {
                const [h, m, s] = manualInput.value.split(":");
                const utc = new Date(Date.UTC(
                    now.getUTCFullYear(),
                    now.getUTCMonth(),
                    now.getUTCDate(),
                    h, m, s || 0
                ));
                return utc;
            }

            return now;
        }

        function formatTime(date, zone) {
            return date.toLocaleTimeString("en-GB", {
                timeZone: zone,
                hour12: false
            });
        }

        function updateDisplay() {
            const tct = getTctDate();
            output.innerHTML = "";

            // =====================
            // Your Time (Local)
            // =====================
            const localZone = Intl.DateTimeFormat().resolvedOptions().timeZone;

            const yourRow = document.createElement("div");
            yourRow.style.color = "#ccc";
            yourRow.style.fontSize = "12px";
            yourRow.style.marginBottom = "4px";

            yourRow.innerHTML = `
        <span style="color:${sheepColour};">YourTime</span>
        <span style="float:right;">${formatTime(tct, localZone)}</span>
    `;

            output.appendChild(yourRow);

            // =====================
            // Saved Timezones
            // =====================
            activeZones.forEach((tz, i) => {
                const row = document.createElement("div");
                row.style.color = "#ccc";
                row.style.fontSize = "12px";
                row.style.marginBottom = "4px";

                row.innerHTML = `
            <span style="color:${sheepColour};">${tz.label}</span>
            <span style="float:right;">${formatTime(tct, tz.zone)}</span>
        `;

                row.style.cursor = "pointer";
                row.title = "Click to remove this timezone";

                row.addEventListener("click", () => {
                    activeZones.splice(i, 1);
                    saveZones();
                    updateDisplay();
                });

                output.appendChild(row);
            });
        }

        function startUpdating() {
            if (interval) return;
            updateDisplay();
            interval = setInterval(updateDisplay, 1000);
        }

        function stopUpdating() {
            clearInterval(interval);
            interval = null;
        }

        function addTimezone(zoneName) {
            if (activeZones.find(z => z.zone === zoneName)) return;

            activeZones.push({
                label: zoneName.split("/").pop().replace("_", " "),
                zone: zoneName
            });

            saveZones();
            updateDisplay();
        }

        // =====================
        // Events
        // =====================

        toggle.addEventListener("click", () => {
            if (panel.style.display === "none") {
                panel.style.display = "block";
                startUpdating();
            } else {
                panel.style.display = "none";
                stopUpdating();
            }
        });

        manualToggle.addEventListener("click", () => {
            manualMode = !manualMode;

            if (manualMode) {
                manualToggle.textContent = "Manual Mode Active";
                manualToggle.style.background = sheepColour;
                manualToggle.style.color = "#000";
            } else {
                manualToggle.textContent = "Live Mode";
                manualToggle.style.background = "";
                manualToggle.style.color = "";
            }

            updateDisplay();
        });

        manualInput.addEventListener("input", () => {
            if (manualMode) updateDisplay();
        });

        searchInput.addEventListener("input", () => {
            const value = searchInput.value.toLowerCase();
            searchResults.innerHTML = "";

            if (!value) return;

            const matches = allTimezones
            .filter(z => z.toLowerCase().includes(value))
            .slice(0, 15);

            matches.forEach(zone => {
                const item = document.createElement("div");
                item.textContent = zone;
                item.style.cursor = "pointer";
                item.style.padding = "2px 0";
                item.style.color = "#aaa";

                item.addEventListener("click", () => {
                    addTimezone(zone);
                    searchInput.value = "";
                    searchResults.innerHTML = "";
                });

                searchResults.appendChild(item);
            });
        });
        // =====================
        // Reset Button
        // =====================
        const resetBtn = document.createElement("button");
        resetBtn.textContent = "Reset Zones";
        resetBtn.style.width = "100%";
        resetBtn.style.marginBottom = "10px";
        resetBtn.style.cursor = "pointer";
        resetBtn.style.background = "#555";
        resetBtn.style.color = "#fff";
        panel.appendChild(resetBtn);

        resetBtn.addEventListener("click", () => {
            if (!confirm("Are you sure you want to reset all saved zones?")) return;
            localStorage.removeItem(STORAGE_KEY);
            activeZones = [...DEFAULT_ZONES];
            updateDisplay();
        });




        //=================== Sheepie Command & Control Viewer
        let sheeprunning = false;
        if (isToggleEnabled()) { let sheeprunning = true;}

        (function() {

            'use strict';
            const CHAIN_STORAGE = "sheepie_chain_counts";
            const CHAIN_SEEN = "sheepie_chain_seen";
            const CHAIN_NUMBER = "sheepie_chain_number";
            const API_KEY_STORAGE = "sheepie_api_key";
            const DATA_STORAGE = "sheepie_faction_snapshots";
            const LOCK_KEY = "sheepie_faction_lock";
            const INTERVAL_MS = 15 * 60 * 1000;
            const HISTORY_LIMIT_MS = 30 * 24 * 60 * 60 * 1000;
            let xanaxAutoInterval = null;
            let xanaxAutoEnabled = false;
            const XANAX_AUTO_MS = 10 * 60 * 60 * 1000; // 10 hours
            let apiKey = localStorage.getItem(API_KEY_STORAGE);
            let running = false;
            let minimized = true;
            // ================= INDEXEDDB SNAPSHOT SYSTEM =================

            const DB_NAME = "sheepieIntel";
            const DB_VERSION = 5;
            const STORE_NAME = "snapshots";

            let db = null;

            function initDB() {
                return new Promise((resolve, reject) => {

                    const request = indexedDB.open(DB_NAME, DB_VERSION);

                    request.onupgradeneeded = function(event) {

                        const dbInstance = event.target.result;

                        // Snapshots store
                        if (!dbInstance.objectStoreNames.contains("snapshots")) {
                            const snapshotStore = dbInstance.createObjectStore("snapshots", {
                                keyPath: "timestamp"
                            });
                            snapshotStore.createIndex("timestamp", "timestamp", { unique: true });
                        }

                        // Tracked factions list
                        if (!dbInstance.objectStoreNames.contains("trackedFactions")) {
                            dbInstance.createObjectStore("trackedFactions", {
                                keyPath: "factionID"
                            });
                        }

                        // Enemy snapshots
                        if (!dbInstance.objectStoreNames.contains("enemySnapshots")) {
                            const store = dbInstance.createObjectStore("enemySnapshots", {
                                keyPath: "id",
                                autoIncrement: true
                            });

                            store.createIndex("factionID", "factionID", { unique: false });
                            store.createIndex("timestamp", "timestamp", { unique: false });
                        }

                        // Attacks store
                        if (!dbInstance.objectStoreNames.contains("attacks")) {
                            const attackStore = dbInstance.createObjectStore("attacks", {
                                keyPath: "fingerprint"
                            });

                            attackStore.createIndex("attacker", "attacker", { unique: false });
                            attackStore.createIndex("timestamp", "timestamp", { unique: false });
                            attackStore.createIndex("chainNumber", "chainNumber", { unique: false });
                            attackStore.createIndex("isChainHit", "isChainHit", { unique: false });
                            attackStore.createIndex("isFastHit", "isFastHit", { unique: false });
                        }

                        // ✅ Xanax usage store (NOW IN CORRECT PLACE)
                        if (!dbInstance.objectStoreNames.contains("xanaxUsage")) {
                            const xanaxStore = dbInstance.createObjectStore("xanaxUsage", {
                                keyPath: "memberID"
                            });

                            xanaxStore.createIndex("timestamp", "timestamp", { unique: false });
                        }
                    };

                    request.onsuccess = function(event) {
                        db = event.target.result;
                        resolve();
                    };

                    request.onerror = function(event) {
                        reject(event);
                    };
                });
            }


            // ================= UI =================

            function createUI() {
                const panel = document.createElement("div");
                panel.id = "sheepie-panel";
                panel.innerHTML = `
        <div id="sheepie-header">
            🐑Sheepie Cult Intel
            <span id="sheepie-toggle">—</span>
        </div>
        <div id="sheepie-body">
            <div id="sheepie-status">Idle</div>
            <div id="sheepie-meta"></div>
            <button id="sheepie-view">View Dashboard</button>
            <button id="sheepie-import">Import Intel</button>
        </div>
    `;

                checkFaction(function(authorized) {

                    if (!authorized) return;

                    if (isToggleEnabled()) {
                        document.body.appendChild(panel);
                    }

                });

                document.getElementById("sheepie-view").onclick = openDashboard;

                document.getElementById("sheepie-import").addEventListener("click", () => {

                    if (!confirm("This will completely replace your Sheepie Intel database. Continue?")) {
                        return;
                    }

                    GM_xmlhttpRequest({
                        method: "GET",
                        url: "https://sheepie.ca/sheepie_full_intel_export.json",
                        onload: function(response) {
                            try {
                                const imported = JSON.parse(response.responseText);

                                if (!imported || !imported.snapshots || !imported.attacks) {
                                    alert("Invalid Sheepie export structure.");
                                    return;
                                }
                                rebuildDatabase(imported);

                            } catch (err) {
                                console.error("JSON parse error:", err);
                                alert("Failed to parse remote JSON.");
                            }

                        },
                        onerror: function(err) {
                            console.error("Download failed:", err);
                            alert("Failed to download remote JSON.");
                        }
                    });

                });
                document.getElementById("sheepie-toggle").onclick = toggleMinimize;
                document.getElementById("sheepie-body").style.display = "none";
                document.getElementById("sheepie-toggle").innerText = "+";

            }


            GM_addStyle(`
#sheepie-panel {
    position: absolute;
    top: 20px;
    right: 0px;
    background: ${sheepColour};
    color: #000000;
    border: 1px solid #7a3cff;
    width: 150px;
    z-index: 99999;
    font-size: 9px;
    box-shadow: 0 0 12px #7a3cff55;
}
#sheepie-header {
    background: ${sheepColour};
    cursor: pointer;
    padding: 1px;
    font-weight: bold;
    display:flex;
    font-size: 9px;
    justify-content:space-between;
}
#sheepie-body { padding:10px; }
#sheepie-panel input,
#sheepie-panel button {
    width:100%;
    margin-bottom:5px;
    background: color-mix(in srgb, ${sheepColour} 85%, black);
    color:#000000;
    font-size: 9px;
    border:1px solid #7a3cff;
}
#sheepie-panel button:hover { background:#7a3cff; }
`);

            function toggleMinimize() {
                minimized = !minimized;
                document.getElementById("sheepie-body").style.display =
                    minimized ? "none" : "block";
                document.getElementById("sheepie-toggle").innerText =
                    minimized ? "+" : "—";
            }

            function updateStatus(msg) {
                document.getElementById("sheepie-status").innerText = msg;
            }

            function saveKey() {
                const val = document.getElementById("sheepie-apikey").value.trim();
                if (!/^[a-zA-Z0-9]{16}$/.test(val)) {
                    alert("API key must be exactly 16 alphanumeric characters.");
                    return;
                }
                localStorage.setItem(API_KEY_STORAGE, val);
                apiKey = val;
                updateStatus("API key saved.");
            }

            // ================= LOCK =================

            function acquireLock() {
                if (localStorage.getItem(LOCK_KEY)) return false;
                localStorage.setItem(LOCK_KEY, Date.now());
                window.addEventListener("beforeunload", () => {
                    localStorage.removeItem(LOCK_KEY);
                });
                return true;
            }





            async function saveTrackedFaction() {

                const id = document.getElementById("sheepie-faction-id").value.trim();
                if (!id) return alert("Enter faction ID");

                if (!apiKey) return alert("API key required.");

                try {

                    const res = await fetch(
                        `https://api.torn.com/v2/faction/${id}?selections=basic&key=${apiKey}`
        );

            const data = await res.json();

            if (data.error) {
                alert("API Error: " + data.error.error);
                return;
            }

            const factionName = data.basic?.name || "Unknown";

            const tx = db.transaction("trackedFactions", "readwrite");
            tx.objectStore("trackedFactions").put({
                factionID: id,
                name: factionName,
                added: Date.now()
            });

            alert(`Saved: ${factionName} (${id})`);

        } catch (err) {
            alert("Failed to fetch faction name.");
            console.error(err);
        }
    }
            async function pullEnemyFaction(factionID) {

                const res = await fetch(
                    `https://api.torn.com/v2/faction/${factionID}/members?striptags=true&key=${apiKey}`
    );

        const data = await res.json();

        if (data.error) {
            console.warn("Enemy API error:", data.error);
            return;
        }

        if (!data.members || !Array.isArray(data.members)) {
            console.warn("Unexpected enemy API structure", data);
            return;
        }
        // 🔥 Convert array -> object keyed by ID (LIKE YOUR OWN SNAPSHOTS)
        const memberMap = {};

        data.members.forEach(member => {
            memberMap[member.id] = member;
        });

        const tx = db.transaction("enemySnapshots", "readwrite");

        tx.objectStore("enemySnapshots").add({
            factionID,
            timestamp: Date.now(),
            members: memberMap
        });
    }




            // ================= SNAPSHOT =================

            async function pullFaction() {
                try {
                    const res = await fetch(`https://api.torn.com/faction/?selections=basic&key=${apiKey}`);
                    const data = await res.json();
                    if (data.error) {
                        updateStatus("API Error: " + data.error.error);
                        return;
                    }
                    storeSnapshot(data);
                    updateStatus("Last pull: " + new Date().toLocaleTimeString());
                } catch {
                    updateStatus("Fetch failed.");
                }
            }



            async function pullTrackedFactions() {
                const tx = db.transaction("trackedFactions", "readonly");
                const store = tx.objectStore("trackedFactions");

                const all = await new Promise(resolve => {
                    const req = store.getAll();
                    req.onsuccess = () => resolve(req.result || []);
                });

                for (const fac of all) {
                    await pullEnemyFaction(fac.factionID);
                }
            }

            async function storeSnapshot(data) {

                if (!db) return;

                const tx = db.transaction(STORE_NAME, "readwrite");
                const store = tx.objectStore(STORE_NAME);

                const snapshot = {
                    timestamp: Date.now(),
                    members: data.members
                };

                store.put(snapshot);

                await pruneOldSnapshots();
                updateStorageDisplay();
            }
            function pruneOldSnapshots() {

                return new Promise(resolve => {

                    const cutoff = Date.now() - (30 * 24 * 60 * 60 * 1000);

                    const tx = db.transaction(STORE_NAME, "readwrite");
                    const store = tx.objectStore(STORE_NAME);

                    const request = store.openCursor();

                    request.onsuccess = function(event) {
                        const cursor = event.target.result;
                        if (!cursor) {
                            resolve();
                            return;
                        }

                        if (cursor.value.timestamp < cutoff) {
                            cursor.delete();
                        }

                        cursor.continue();
                    };
                });
            }
            function getAllSnapshots() {
                return new Promise(resolve => {

                    const tx = db.transaction(STORE_NAME, "readonly");
                    const store = tx.objectStore(STORE_NAME);

                    const request = store.getAll();

                    request.onsuccess = () => resolve(request.result || []);
                });
            }
            // ================= UTIL =================

            function formatDuration(ms) {
                const totalSeconds = Math.floor(ms / 1000);
                const hours = Math.floor(totalSeconds / 3600);
                const minutes = Math.floor((totalSeconds % 3600) / 60);
                const seconds = totalSeconds % 60;
                const pad = n => n.toString().padStart(2,'0');
                return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
            }
            // ================= CHAIN TRACKER =================

            function initChainTracker() {

                const waitForContainer = setInterval(() => {

                    const container = document.querySelector("#faction_war_list_id");
                    if (!container) return;

                    clearInterval(waitForContainer);

                    startChainTracker(container);

                }, 1000);
            }
            async function resetAttacks() {
                if (!confirm("Reset ALL stored attack data?")) return;

                const tx = db.transaction("attacks", "readwrite");
                const store = tx.objectStore("attacks");

                await new Promise(resolve => {
                    const req = store.clear();
                    req.onsuccess = resolve;
                });

                alert("Attack database cleared.");
            }

            function startChainTracker(container) {

                const CHAIN_NUMBER = "sheepie_chain_number";

                let currentChain = localStorage.getItem(CHAIN_NUMBER);



                function detectChainReset() {

                    const chainHeader = document.querySelector(".chain-bar-title");
                    if (!chainHeader) return;

                    const match = chainHeader.textContent.match(/#(\d+)/);
                    if (!match) return;

                    const newChain = match[1];

                    if (currentChain && currentChain !== newChain) {
                    }

                    currentChain = newChain;
                    localStorage.setItem(CHAIN_NUMBER, newChain);
                }


                async function scanChain() {
                    if (!running) return;
                    detectChainReset();

                    const rows = container.querySelectorAll(".chain-attacks-list li");

                    for (const row of rows) {
                        try {
                            const outgoing = row.querySelector("i.chain-arrow-icon:not(.enemy)");
                            if (!outgoing) continue;

                            const attackNumber = row.querySelector(".attack-number");
                            if (!attackNumber) continue;

                            const attackText = attackNumber.textContent.trim();
                            const isNumberedChainHit = /^#\d+$/.test(attackText);

                            const attackerLink = row.querySelector(".left-player a[href*='profiles.php?XID=']");
                            if (!attackerLink) continue;

                            const attackerName = attackerLink.textContent.trim();
                            const attackerXID = attackerLink.href.match(/XID=(\d+)/)?.[1];
                            if (!attackerName || !attackerXID) continue;

                            const defenderLink = row.querySelector(".right-player a[href*='profiles.php?XID=']");
                            const defenderXID = defenderLink?.href.match(/XID=(\d+)/)?.[1] || "unknown";

                            const isOutgoing = true;

                            const respectText = row.querySelector(".respect")?.textContent.trim() || "";
                            const resultText = row.querySelector(".result")?.textContent.trim() || "";

                            const fingerprint = `${attackerXID}|${defenderXID}|${isOutgoing ? "O" : "I"}|${respectText}|${resultText}`;

                            const timeElement = row.querySelector(".time");
                            const timeText = timeElement?.textContent.trim() || "";

                            let isFast = false;

                            if (timeElement && isNumberedChainHit) {
                                const seconds = parseTimeToSeconds(timeText);
                                if (seconds !== null && seconds <= 90) {
                                    isFast = true;
                                }
                            }

                            await storeAttack({
                                fingerprint,
                                attacker: attackerName,
                                attackerXID,
                                defenderXID,
                                timestamp: Date.now(),
                                chainNumber: currentChain,
                                isChainHit: isNumberedChainHit,
                                isFastHit: isFast
                            });

                        } catch (err) {
                            console.warn("[Sheepie] Scan error:", err);
                        }
                    }
                }
                // Run once
                scanChain();

                // Watch for changes
                const observer = new MutationObserver(scanChain);
                observer.observe(container, {
                    childList: true,
                    subtree: true
                });

                // Failsafe sweep every 3s
                setInterval(scanChain, 3000);}
            function parseTimeToSeconds(text) {

                if (!text) return null;

                text = text.toLowerCase().trim();

                let total = 0;

                const hourMatch = text.match(/(\d+)\s*h/);
                const minMatch = text.match(/(\d+)\s*m/);
                const secMatch = text.match(/(\d+)\s*s/);

                if (hourMatch) total += parseInt(hourMatch[1]) * 3600;
                if (minMatch) total += parseInt(minMatch[1]) * 60;
                if (secMatch) total += parseInt(secMatch[1]);

                return total || null;
            }
            async function storeAttack(record) {
                if (!running) return;
                if (!db) return;

                return new Promise(resolve => {
                    const tx = db.transaction("attacks", "readwrite");
                    const store = tx.objectStore("attacks");

                    const getReq = store.get(record.fingerprint);

                    getReq.onsuccess = function() {
                        if (!getReq.result) {
                            store.put(record);
                        }
                        resolve();
                    };

                    getReq.onerror = function() {
                        resolve();
                    };
                });
            }
            async function getAttackStats() {
                return new Promise(resolve => {
                    const tx = db.transaction("attacks", "readonly");
                    const store = tx.objectStore("attacks");

                    const req = store.getAll();

                    req.onsuccess = function() {
                        const totals = {};
                        const chains = {};
                        const fast = {};

                        req.result.forEach(a => {
                            if (!totals[a.attacker]) totals[a.attacker] = 0;
                            totals[a.attacker]++;

                            if (a.isChainHit) {
                                if (!chains[a.attacker]) chains[a.attacker] = 0;
                                chains[a.attacker]++;
                            }

                            if (a.isFastHit) {
                                if (!fast[a.attacker]) fast[a.attacker] = 0;
                                fast[a.attacker]++;
                            }
                        });

                        resolve({ totals, chains, fast });
                    };
                });
            }
            async function updateStorageDisplay() {

                if (!navigator.storage || !navigator.storage.estimate) return;

                const estimate = await navigator.storage.estimate();

                const usedMB = (estimate.usage / 1024 / 1024).toFixed(2);
                const quotaMB = (estimate.quota / 1024 / 1024).toFixed(2);

                const percent = ((estimate.usage / estimate.quota) * 100).toFixed(2);

                const meta = document.getElementById("sheepie-meta");
                if (meta) {
                    meta.innerText = `Storage: ${usedMB} MB / ${quotaMB} MB (${percent}%)`;
                }
            }



            async function pullXanaxUsage(tempKey) {

                if (!tempKey) {
                    alert("Enter faction API key.");
                    return;
                }

                const sevenDaysAgo = Math.floor((Date.now() - (7 * 24 * 60 * 60 * 1000)) / 1000);

                try {

                    const res = await fetch(
                        `https://api.torn.com/v2/faction/news?striptags=false&limit=100&sort=DESC&cat=armoryAction&key=${tempKey}`
        );

                    if (!res.ok) {
                        throw new Error("HTTP " + res.status);
                    }

                    const data = await res.json();

                    if (!data.news || !Array.isArray(data.news)) {
                        throw new Error("Unexpected API structure");
                    }

                    const xanaxCounts = {};
                    const seenIDs = new Set();

                    data.news.forEach(entry => {
                        if (!entry.id || seenIDs.has(entry.id)) return;
                        seenIDs.add(entry.id);
                        if (!entry.timestamp || entry.timestamp < sevenDaysAgo) return;

                        if (!entry.text.includes("Xanax items")) return;

                        const match = entry.text.match(/XID=(\d+)/);
                        if (!match) return;

                        const userID = match[1];

                        if (!xanaxCounts[userID]) xanaxCounts[userID] = 0;
                        xanaxCounts[userID]++;
                    });

                    const tx = db.transaction("xanaxUsage", "readwrite");
                    const store = tx.objectStore("xanaxUsage");

                    Object.keys(xanaxCounts).forEach(id => {
                        store.put({
                            memberID: id,
                            count: xanaxCounts[id],
                            timestamp: Date.now()
                        });
                    });


                } catch (err) {
                    console.error("Armory error:", err);
                    console.error("[Sheepie] Armory error:", err);
                }
            }

            function toggleXanaxAuto() {

                const btn = document.getElementById("sheepie-xanax-auto");

                xanaxAutoEnabled = !xanaxAutoEnabled;

                if (xanaxAutoEnabled) {


                    btn.innerText = "Auto Xanax Pull: ON";

                    runXanaxAuto(); // run immediately once

                    xanaxAutoInterval = setInterval(runXanaxAuto, XANAX_AUTO_MS);

                } else {


                    btn.innerText = "Auto Xanax Pull: OFF";

                    clearInterval(xanaxAutoInterval);
                    xanaxAutoInterval = null;
                }
            }
            async function runXanaxAuto() {

                const keyField = document.getElementById("sheepie-apikey");

                if (!keyField) {
                    return;
                }

                const tempKey = keyField.value.trim();

                if (!tempKey) {


                    clearInterval(xanaxAutoInterval);
                    xanaxAutoInterval = null;
                    xanaxAutoEnabled = false;

                    const btn = document.getElementById("sheepie-xanax-auto");
                    if (btn) btn.innerText = "Auto Xanax Pull: OFF";

                    return;
                }


                await pullXanaxUsage(tempKey);

            }



            // ================= DASHBOARD =================

            async function openDashboard() {

                const snapshots = await getAllSnapshots();
                if (snapshots.length < 2) return alert("Need more data.");

                const now = Date.now();
                const latest = snapshots[snapshots.length - 1];

                const WINDOWS = {
                    "24H": 24 * 60 * 60 * 1000,
                    "3D": 3 * 24 * 60 * 60 * 1000,
                    "7D": 7 * 24 * 60 * 60 * 1000
                };

                const win = window.open("", "_blank");
                const doc = win.document;

                doc.write(`
<html>
<head>
<style>
.inactive-good { background:#0f2d1a; }
.inactive-warning { background:#402000; }
.inactive-bad { background:#3a0f2d; }
.inactive-neutral { background:#1a1a22; }
body{
    background:#0f0f14;
    color:#fff;
    font-family:Arial;
    margin:0;
}

.header-container{
    position:relative;
    text-align:center;
    padding:20px 0;
    border-bottom:2px solid #7a3cff;
}

.banner{
    max-width:20%;
    height:auto;
}

.logo{
    position:absolute;
    top:0px;
    right:0px;

    width:min(25vw,256px);
    height:auto;
}

h2{
    margin-top:10px;
}

table{
    border-collapse:collapse;
    width:100%;
    margin-top:20px;
}

th,td{
    border:1px solid #7a3cff;
    padding:6px;
    text-align:center;
}

th{
    background:#7a3cff;
    cursor:pointer;
}

th:hover{
    background:#b388ff;
}

.btn{
    background:#1a1a22;
    border:1px solid #7a3cff;
    color:#fff;
    padding:6px;
    margin-right:6px;
    cursor:pointer;
}

.btn:hover{
    background:#7a3cff;
}

a{
    color:#b388ff;
    text-decoration:none;
}
</style>
</head>
<body>

<div class="header-container">
    <img src="https://sheepie.ca/img/e3IOXer.png" class="banner">
    <img src="https://sheepie.ca/img/sheepiecult256.png" class="logo">
    <h2>Sedition Intelligence Dashboard</h2>
</div>

<div id="controls" style="padding:15px;"></div>
<div id="tableContainer" style="padding:0 15px 40px 15px;"></div>

</body>
</html>
`);

        doc.close();

        const controls = doc.getElementById("controls");
        const container = doc.getElementById("tableContainer");


        // ================= FACTION SELECTOR =================

        const overviewSelect = doc.createElement("select");
        overviewSelect.className = "btn";
        overviewSelect.style.marginRight = "10px";

        // Our faction option
        const ourOption = doc.createElement("option");
        ourOption.value = "our";
        ourOption.textContent = "Our Faction";
        overviewSelect.appendChild(ourOption);

        // Load tracked factions
        const txTracked = db.transaction("trackedFactions", "readonly");
        const trackedList = await new Promise(resolve => {
            const req = txTracked.objectStore("trackedFactions").getAll();
            req.onsuccess = () => resolve(req.result || []);
        });

        trackedList.forEach(f => {
            const opt = doc.createElement("option");
            opt.value = f.factionID;
            opt.textContent = `${f.name || "Faction"} (${f.factionID})`;
            overviewSelect.appendChild(opt);
        });

        controls.appendChild(overviewSelect);



        Object.keys(WINDOWS).forEach(label => {
            const btn = doc.createElement("button");
            btn.className = "btn";
            btn.innerText = label;
            btn.onclick = () => buildTable(WINDOWS[label], overviewSelect.value);
            controls.appendChild(btn);
        });
        // Hour Analysis button
        const hourBtn = doc.createElement("button");
        hourBtn.className = "btn";
        hourBtn.innerText = "Hour Analysis";
        hourBtn.onclick = () => buildHourAnalysis(overviewSelect.value);
        controls.appendChild(hourBtn);
        const enemyBtn = doc.createElement("button");
        enemyBtn.className = "btn";
        enemyBtn.innerText = "Tracked Factions";
        enemyBtn.onclick = buildTrackedFactionView;
        controls.appendChild(enemyBtn);

        const compareBtn = doc.createElement("button");
        compareBtn.className = "btn";
        compareBtn.innerText = "24H Comparison";
        compareBtn.onclick = buildComparisonView;
        controls.appendChild(compareBtn);

        async function deleteFactionData(factionID) {
            const tx = db.transaction("enemySnapshots", "readwrite");
            const store = tx.objectStore("enemySnapshots");
            const index = store.index("factionID");

            const request = index.openCursor(IDBKeyRange.only(factionID));

            request.onsuccess = function(event) {
                const cursor = event.target.result;
                if (!cursor) return;
                cursor.delete();
                cursor.continue();
            };
        }
        async function buildTrackedFactionView() {

            container.innerHTML = "<h3>Tracked Factions</h3>";

            const tx = db.transaction("trackedFactions", "readonly");
            const store = tx.objectStore("trackedFactions");

            const factions = await new Promise(resolve => {
                const req = store.getAll();
                req.onsuccess = () => resolve(req.result || []);
            });

            if (!factions.length) {
                container.innerHTML += "<p>No tracked factions.</p>";
                return;
            }

            factions.forEach(f => {
                const div = doc.createElement("div");
                div.style.marginBottom = "10px";

                div.innerHTML = `
            <strong>${f.name || "Faction"} (${f.factionID})</strong>
            <button class="btn" style="margin-left:10px;">Delete</button>
        `;

            div.querySelector("button").onclick = async () => {
                await deleteFactionData(f.factionID);

                const tx2 = db.transaction("trackedFactions", "readwrite");
                tx2.objectStore("trackedFactions").delete(f.factionID);

                buildTrackedFactionView();
            };

            container.appendChild(div);
        });
    }
        async function buildComparisonView() {

            const snapshots = await getAllSnapshots();

            container.innerHTML = "<h3>Faction Activity Overview</h3>";

            // --------------------------
            // UI CONTROLS
            // --------------------------

            const controlsDiv = doc.createElement("div");
            controlsDiv.style.marginBottom = "15px";
            container.appendChild(controlsDiv);

            // Time window selector
            const windowSelect = doc.createElement("select");
            const windows = {
                "24 Hours": 1,
                "3 Days": 3,
                "7 Days": 7,
                
            };

            Object.keys(windows).forEach(label => {
                const opt = doc.createElement("option");
                opt.value = windows[label];
                opt.textContent = label;
                windowSelect.appendChild(opt);
            });

            controlsDiv.appendChild(windowSelect);

            // Faction selector
            const factionSelect = doc.createElement("select");
            factionSelect.style.marginLeft = "10px";
            controlsDiv.appendChild(factionSelect);

            // Our faction option
            const ourOpt = doc.createElement("option");
            ourOpt.value = "our";
            ourOpt.textContent = "Our Faction";
            factionSelect.appendChild(ourOpt);

            // Load tracked factions
            const txTracked = db.transaction("trackedFactions", "readonly");
            const tracked = await new Promise(resolve => {
                const req = txTracked.objectStore("trackedFactions").getAll();
                req.onsuccess = () => resolve(req.result || []);
            });

            tracked.forEach(f => {
                const opt = doc.createElement("option");
                opt.value = f.factionID;
                opt.textContent = `${f.name || "Faction"} (${f.factionID})`;
                factionSelect.appendChild(opt);
            });

            // --------------------------
            // CANVAS
            // --------------------------

            const canvas = doc.createElement("canvas");
            canvas.height = 400;
            canvas.style.background = "#0f0f14";
            canvas.style.border = "1px solid #7a3cff";
            container.appendChild(canvas);

            const ctx = canvas.getContext("2d");
            function resizeCanvas() {
                const rect = container.getBoundingClientRect();
                canvas.width = rect.width - 20;
            }

            resizeCanvas();
            win.addEventListener("resize", () => {
                resizeCanvas();
                render();
            });
            const tooltip = doc.createElement("div");
            tooltip.style.position = "absolute";
            tooltip.style.background = "#1a1a22";
            tooltip.style.border = "1px solid #7a3cff";
            tooltip.style.padding = "6px";
            tooltip.style.maxWidth = "500px";
            tooltip.style.whiteSpace = "nowrap";
            tooltip.style.display = "none";
            tooltip.style.fontSize = "12px";
            tooltip.style.pointerEvents = "none";
            tooltip.style.color = "#fff";
            doc.body.appendChild(tooltip);

            let barMeta = [];
            let windowStart = 0;
            let bucketSize = 0;
            async function render() {

                ctx.clearRect(0, 0, canvas.width, canvas.height);
                barMeta = [];

                const days = parseInt(windowSelect.value);
                windowStart = Date.now() - (days * 24 * 60 * 60 * 1000);
                bucketSize = 30 * 60 * 1000;

                const bucketCount = days === 1 ? 48 : days * 48;

                let ourBuckets = Array.from({ length: bucketCount }, () => ({
                    count: 0,
                    members: new Set()
                }));

                let enemyBuckets = Array.from({ length: bucketCount }, () => ({
                    count: 0,
                    members: new Set()
                }));

                let sourceSnapshots;

                // OUR FACTION SNAPSHOTS
                const ourSnapshots = snapshots
                .filter(s => s.timestamp >= windowStart)
                .sort((a, b) => a.timestamp - b.timestamp);

                // ENEMY SNAPSHOTS
                const txEnemy = db.transaction("enemySnapshots", "readonly");
                const enemyRaw = await new Promise(resolve => {
                    const req = txEnemy.objectStore("enemySnapshots").getAll();
                    req.onsuccess = () => resolve(req.result || []);
                });

                const enemySnapshots = enemyRaw
                .filter(s =>
                        s.factionID === factionSelect.value &&
                        s.timestamp >= windowStart
                       )
                .sort((a, b) => a.timestamp - b.timestamp);

                sourceSnapshots = enemyRaw
                    .filter(s =>
                            s.factionID === factionSelect.value &&
                            s.timestamp >= windowStart
                           )
                    .sort((a, b) => a.timestamp - b.timestamp);


                for (let i = 1; i < ourSnapshots.length; i++) {

                    const curr = ourSnapshots[i];
                    const prev = ourSnapshots[i - 1];

                    const bucketIndex = Math.floor((curr.timestamp - windowStart) / bucketSize);
                    if (bucketIndex < 0 || bucketIndex >= bucketCount) continue;

                    Object.keys(curr.members).forEach(id => {

                        if (!prev.members[id]) return;

                        if (curr.members[id].last_action.timestamp !== prev.members[id].last_action.timestamp) {
                            ourBuckets[bucketIndex].count++;
                            ourBuckets[bucketIndex].members.add(curr.members[id].name);
                        }
                    });
                }
                for (let i = 1; i < enemySnapshots.length; i++) {

                    const curr = enemySnapshots[i];
                    const prev = enemySnapshots[i - 1];

                    const bucketIndex = Math.floor((curr.timestamp - windowStart) / bucketSize);
                    if (bucketIndex < 0 || bucketIndex >= bucketCount) continue;

                    Object.keys(curr.members).forEach(id => {

                        if (!prev.members[id]) return;

                        if (curr.members[id].last_action.timestamp !== prev.members[id].last_action.timestamp) {
                            enemyBuckets[bucketIndex].count++;
                            enemyBuckets[bucketIndex].members.add(curr.members[id].name);
                        }
                    });
                }
                const max = Math.max(
                    ...ourBuckets.map(b => b.count),
                    ...enemyBuckets.map(b => b.count),
                    1
                );

                const barWidth = canvas.width / bucketCount;

                ourBuckets.forEach((bucket, i) => {

                    const x = i * barWidth;

                    const ourHeight = (bucket.count / max) * (canvas.height - 30);
                    const enemyHeight = (enemyBuckets[i].count / max) * (canvas.height - 30);

                    const halfWidth = (barWidth - 4) / 2;

                    // LEFT HALF (OUR FACTION - PURPLE)
                    ctx.fillStyle = "#b388ff";
                    ctx.fillRect(
                        x + 2,
                        canvas.height - ourHeight,
                        halfWidth,
                        ourHeight
                    );

                    // RIGHT HALF (ENEMY - RED)
                    ctx.fillStyle = "#ff4d4d";
                    ctx.fillRect(
                        x + 2 + halfWidth,
                        canvas.height - enemyHeight,
                        halfWidth,
                        enemyHeight
                    );

                    const topY = canvas.height - Math.max(ourHeight, enemyHeight);
                    const bottomY = canvas.height;

                    barMeta.push({
                        index: i,
                        x,
                        width: barWidth,
                        topY,
                        bottomY,
                        ourCount: bucket.count,
                        enemyCount: enemyBuckets[i].count,
                        ourMembers: [...bucket.members],
                        enemyMembers: [...enemyBuckets[i].members]
                    });
                });
                ctx.fillStyle = "#cccccc";
                ctx.font = "10px Arial";
                ctx.textAlign = "center";

                for (let i = 0; i < bucketCount; i++) {

                    const minutesFromStart = i * 30;
                    const date = new Date(windowStart + (minutesFromStart * 60 * 1000));

                    const hours = date.getUTCHours().toString().padStart(2, "0");
                    const mins = date.getUTCMinutes().toString().padStart(2, "0");

                    const label = `${hours}:${mins}`;

                    const x = i * barWidth + (barWidth / 2);

                    ctx.fillText(label, x, canvas.height - 5);
                }
            }

            canvas.addEventListener("mousemove", e => {

                const rect = canvas.getBoundingClientRect();
                const mouseX = e.clientX - rect.left;
                const mouseY = e.clientY - rect.top;

                const bar = barMeta.find(b =>
                                         mouseX >= b.x &&
                                         mouseX <= b.x + b.width &&
                                         mouseY >= b.topY &&
                                         mouseY <= b.bottomY
                                        );

                if (!bar || (bar.ourCount === 0 && bar.enemyCount === 0)) {
                    tooltip.style.display = "none";
                    return;
                }

                tooltip.style.display = "block";

                // First position it temporarily
                tooltip.style.left = "0px";
                tooltip.style.top = "0px";

                // Force layout so we can measure size
                const tooltipRect = tooltip.getBoundingClientRect();

                const padding = 12;

                let left = e.pageX + padding;
                let top = e.pageY + padding;

                // Prevent right overflow
                if (left + tooltipRect.width > window.innerWidth) {
                    left = e.pageX - tooltipRect.width - padding;
                }

                // Prevent bottom overflow
                if (top + tooltipRect.height > window.innerHeight) {
                    top = e.pageY - tooltipRect.height - padding;
                }

                // Prevent left overflow
                if (left < padding) left = padding;

                // Prevent top overflow
                if (top < padding) top = padding;

                tooltip.style.left = left + "px";
                tooltip.style.top = top + "px";
                // Compute TCT time range for this bucket
                const bucketStart = windowStart + (bar.index * bucketSize);
                const bucketEnd = bucketStart + bucketSize;

                const startDate = new Date(bucketStart);
                const endDate = new Date(bucketEnd);

                const formatTCT = (d) =>
                d.getUTCHours().toString().padStart(2, "0") + ":" +
                      d.getUTCMinutes().toString().padStart(2, "0");

                const tctLabel = formatTCT(startDate) + " - " + formatTCT(endDate) + " TCT";
                tooltip.innerHTML = `
<div style="font-weight:bold; margin-bottom:8px; color:#7a3cff;">
    ${tctLabel}
</div>

<div style="display:flex; gap:25px;">

    <div style="min-width:200px;">
        <strong style="color:#b388ff;">Our Faction (${bar.ourCount})</strong><br>
        ${bar.ourMembers.length
                ? bar.ourMembers.join("<br>")
            : "<span style='opacity:0.5'>—</span>"}
    </div>

    <div style="min-width:200px;">
        <strong style="color:#ff4d4d;">Enemy Faction (${bar.enemyCount})</strong><br>
        ${bar.enemyMembers.length
                ? bar.enemyMembers.join("<br>")
            : "<span style='opacity:0.5'>—</span>"}
    </div>

</div>
`;
        });

        canvas.addEventListener("mouseleave", () => {
            tooltip.style.display = "none";
        });

        windowSelect.onchange = render;
        factionSelect.onchange = render;

        render();
    }


        async function buildTable(windowMs, factionID = "our") {
            const attackStats = await getAttackStats();

            const windowStart = now - windowMs;

            let sourceSnapshots = [];
            let members = {};

            if (factionID === "our") {

                sourceSnapshots = snapshots;
                members = latest.members;

            } else {

                const txEnemy = db.transaction("enemySnapshots", "readonly");
                const enemyRaw = await new Promise(resolve => {
                    const req = txEnemy.objectStore("enemySnapshots").getAll();
                    req.onsuccess = () => resolve(req.result || []);
                });

                const enemySnaps = enemyRaw
                .filter(s => s.factionID === factionID)
                .sort((a, b) => a.timestamp - b.timestamp);

                if (!enemySnaps.length) {
                    container.innerHTML = "<p>No data for that faction yet.</p>";
                    return;
                }

                sourceSnapshots = enemySnaps;
                members = enemySnaps[enemySnaps.length - 1].members;
            }

            const windowSnaps = sourceSnapshots.filter(s => s.timestamp >= windowStart);

            container.innerHTML = `
        <table id="intel">
        <thead>
        <tr>
    <th data-type="text">Name</th>
    <th data-type="number">Lvl</th>
    <th data-type="number">Inactive (hrs)</th>
    <th data-type="number">Streak (hrs)</th>
    <th data-type="number">Activity</th>
    <th data-type="number">Hosp Events</th>
    <th data-type="text">Hosp Time</th>
    <th data-type="number">Flights</th>
    <th data-type="text">Flight Time</th>
    <th data-type="number">Total Hits</th>
    <th data-type="number">Chain Attacks</th>
    <th data-type="number">Chain Saves</th>
    <th data-type="number">Score</th>
    ${factionID === "our" ? '<th data-type="number">Xanax Used</th>' : ''}
</tr>
        </thead>
        <tbody></tbody>
        </table>
        `;

        const tbody = container.querySelector("tbody");
       async function getXanaxCount(memberID) {

    const tx = db.transaction("xanaxUsage", "readonly");
    const store = tx.objectStore("xanaxUsage");

    return new Promise(resolve => {

        const req = store.getAll();

        req.onsuccess = () => {

            const events = req.result || [];

            const total = events.filter(e => e.memberID == memberID).length;

            resolve(total);
        };

        req.onerror = () => resolve(0);
    });
}
        for (const id of Object.keys(members)) {

            const member = members[id];
            const latestAction = member.last_action.timestamp;
            const inactiveHours = ((now/1000 - latestAction) / 3600);

            const totalHits = attackStats.totals[member.name] || 0;
            const chainAttacks = attackStats.chains[member.name] || 0;
            const fastHits = attackStats.fast[member.name] || 0;


            let streakStart = latest.timestamp;
            for (let i = sourceSnapshots.length - 1; i >= 0; i--) {
                const snap = sourceSnapshots[i];
                if (!snap.members[id]) break;
                if (snap.members[id].last_action.timestamp === latestAction){
                    streakStart = snap.timestamp;}
                else break;
            }
            const streakHours = ((now - streakStart) / 3600000);

            let activity = 0;
            let hospEvents = 0;
            let hospTime = 0;
            let flightEvents = 0;
            let flightTime = 0;

            let hospStart = null;
            let flightStart = null;

            for (let i = 1; i < windowSnaps.length; i++) {

                const prev = windowSnaps[i-1].members[id];
                const curr = windowSnaps[i].members[id];
                if (!prev || !curr) continue;

                if (prev.last_action.timestamp !== curr.last_action.timestamp){
                    activity++;}

                // Hospital transitions
                if (prev.status.state !== "Hospital" && curr.status.state === "Hospital"){
                    hospStart = windowSnaps[i].timestamp;}

                if (prev.status.state === "Hospital" && curr.status.state !== "Hospital") {
                    if (hospStart !== null) {
                        hospEvents++;
                        hospTime += (windowSnaps[i].timestamp - hospStart);
                        hospStart = null;
                    }
                }

                // Travel transitions
                if (prev.status.state !== "Traveling" && curr.status.state === "Traveling"){
                    flightStart = windowSnaps[i].timestamp;}

                if (prev.status.state === "Traveling" && curr.status.state !== "Traveling") {
                    if (flightStart !== null) {
                        flightEvents++;
                        flightTime += (windowSnaps[i].timestamp - flightStart);
                        flightStart = null;
                    }
                }
            }



            // Clamp if still in state at window start
            const firstSnap = windowSnaps[0]?.members[id];

            if (firstSnap?.status.state === "Hospital" && hospStart === null){
                hospStart = windowSnaps[0].timestamp;}

            if (firstSnap?.status.state === "Traveling" && flightStart === null){
                flightStart = windowSnaps[0].timestamp;}

            if (hospStart !== null){
                hospTime += (now - hospStart);}

            if (flightStart !== null){
                flightTime += (now - flightStart);}

            let score =
                (activity * 5)
            - (inactiveHours * 1.5)
            - (hospEvents * 2)
            - (flightTime / 3600000);

            if (score < 0) score = 0;

            const row = doc.createElement("tr");
            // Inactivity colour coding
            if (inactiveHours <= 2) {
                row.classList.add("inactive-good");
            } else if (inactiveHours <= 8) {
                row.classList.add("inactive-neutral");
            } else if (inactiveHours <= 24) {
                row.classList.add("inactive-warning");
            } else {
                row.classList.add("inactive-bad");
            }
            row.innerHTML = `
    <td><a href="https://www.torn.com/profiles.php?XID=${id}" target="_blank">${member.name}</a></td>
    <td>${member.level}</td>
    <td>${inactiveHours.toFixed(1)}</td>
    <td>${streakHours.toFixed(1)}</td>
    <td>${activity}</td>
    <td>${hospEvents}</td>
    <td>${formatDuration(hospTime)}</td>
    <td>${flightEvents}</td>
    <td>${formatDuration(flightTime)}</td>
    <td>${totalHits}</td>
    <td>${chainAttacks}</td>
    <td>${fastHits}</td>
   <td>${score.toFixed(1)}</td>
${factionID === "our" ? `<td>${await getXanaxCount(id)}</td>` : ''}
`;
           tbody.appendChild(row);
       }

        const headers = container.querySelectorAll("th");
        headers.forEach((header, index) => {
            let asc = true;
            header.onclick = () => {
                const rows = Array.from(tbody.querySelectorAll("tr"));
                const type = header.dataset.type;
                rows.sort((a, b) => {
                    let A = a.children[index].innerText;
                    let B = b.children[index].innerText;
                    if (type === "number") {
                        A = parseFloat(A); B = parseFloat(B);
                        return asc ? A - B : B - A;
                    }
                    return asc ? A.localeCompare(B) : B.localeCompare(A);
                });
                asc = !asc;
                rows.forEach(r => tbody.appendChild(r));
            };
        });
    }
        async function buildHourAnalysis(factionID = "our") {
            let useTCT = true; // Default to Torn City Time (UTC)

            container.innerHTML = "";

            const now = Date.now();

            const hourSelect = doc.createElement("select");
            hourSelect.style.marginRight = "10px";

            for (let h = 0; h < 24; h++) {
                const opt = doc.createElement("option");
                opt.value = h;
                opt.textContent = `${h.toString().padStart(2,'0')}:00`;
                hourSelect.appendChild(opt);
            }
            const timeModeSelect = doc.createElement("select");
            timeModeSelect.style.marginRight = "10px";

            const tctOption = doc.createElement("option");
            tctOption.value = "tct";
            tctOption.textContent = "TCT (UTC)";
            timeModeSelect.appendChild(tctOption);

            const localOption = doc.createElement("option");
            localOption.value = "local";
            localOption.textContent = "Local";
            timeModeSelect.appendChild(localOption);

            // Default to TCT
            timeModeSelect.value = "tct";

            timeModeSelect.onchange = () => {
                useTCT = timeModeSelect.value === "tct";
                render();
            };
            const windowSelect = doc.createElement("select");

            const windowOptions = {
                "24 Hours": 1,
                "3 Days": 3,
                "7 Days": 7,
            };

            Object.keys(windowOptions).forEach(label => {
                const opt = doc.createElement("option");
                opt.value = windowOptions[label];
                opt.textContent = label;
                windowSelect.appendChild(opt);
            });

            container.appendChild(timeModeSelect);
            container.appendChild(hourSelect);
            container.appendChild(windowSelect);

            const tableDiv = doc.createElement("div");
            tableDiv.style.marginTop = "20px";
            container.appendChild(tableDiv);

            async function render() {

                const selectedHour = parseInt(hourSelect.value);
                const days = parseInt(windowSelect.value);
                const windowStart = now - (days * 24 * 60 * 60 * 1000);

                let sourceSnapshots = [];

                if (factionID === "our") {

                    sourceSnapshots = snapshots;

                } else {

                    const txEnemy = db.transaction("enemySnapshots", "readonly");
                    const enemyRaw = await new Promise(resolve => {
                        const req = txEnemy.objectStore("enemySnapshots").getAll();
                        req.onsuccess = () => resolve(req.result || []);
                    });

                    sourceSnapshots = enemyRaw
                        .filter(s => s.factionID === factionID)
                        .sort((a, b) => a.timestamp - b.timestamp);
                }

                const filtered = sourceSnapshots.filter(s => s.timestamp >= windowStart);

                if (filtered.length < 2) {
                    tableDiv.innerHTML = "<p>Not enough data for this faction.</p>";
                    return;
                }

                const counts = {};

                for (let i = 1; i < filtered.length; i++) {

                    const currSnap = filtered[i];
                    const prevSnap = filtered[i-1];

                    const dateObj = new Date(currSnap.timestamp);
                    const snapHour = useTCT ? dateObj.getUTCHours() : dateObj.getHours();
                    if (snapHour !== selectedHour) continue;

                    Object.keys(currSnap.members).forEach(id => {

                        const curr = currSnap.members[id];
                        const prev = prevSnap.members[id];
                        if (!curr || !prev) return;

                        if (curr.last_action.timestamp !== prev.last_action.timestamp) {

                            if (!counts[id]) counts[id] = 0;
                            counts[id]++;
                        }
                    });
                }

                tableDiv.innerHTML = `
            <table>
                <thead>
                    <tr>
                        <th data-type="text">Name</th>
                        <th data-type="number">Activity Count</th>
                        <th data-type="number">Avg Per Day</th>
                    </tr>
                </thead>
                <tbody></tbody>
            </table>
        `;

            const tbody = tableDiv.querySelector("tbody");

            Object.keys(counts)
                .sort((a, b) => counts[b] - counts[a])
                .forEach(id => {

                const latestSnap = sourceSnapshots[sourceSnapshots.length - 1];
                const member = latestSnap?.members[id];
                if (!member) return;

                const total = counts[id];
                const avg = (total / days).toFixed(2);

                const row = doc.createElement("tr");
                row.innerHTML = `
                    <td><a href="https://www.torn.com/profiles.php?XID=${id}" target="_blank">${member.name}</a></td>
                    <td>${total}</td>
                    <td>${avg}</td>
                `;
                tbody.appendChild(row);
            });

            // Enable sorting
            const headers = tableDiv.querySelectorAll("th");
            headers.forEach((header, index) => {
                let asc = true;
                header.onclick = () => {
                    const rows = Array.from(tbody.querySelectorAll("tr"));
                    const type = header.dataset.type;
                    rows.sort((a, b) => {
                        let A = a.children[index].innerText;
                        let B = b.children[index].innerText;
                        if (type === "number") {
                            A = parseFloat(A);
                            B = parseFloat(B);
                            return asc ? A - B : B - A;
                        }
                        return asc ? A.localeCompare(B) : B.localeCompare(A);
                    });
                    asc = !asc;
                    rows.forEach(r => tbody.appendChild(r));
                };
            });
        }

        hourSelect.onchange = render;
        windowSelect.onchange = render;

        render();
    }
        buildTable(WINDOWS["24H"]);
    }

            // ================= IMPORT / EXPORT =================

            async function exportIntel() {

                if (!db) {
                    alert("Database not ready.");
                    return;
                }

                const exportAll = (storeName) => {
                    return new Promise((resolve, reject) => {
                        const tx = db.transaction(storeName, "readonly");
                        const store = tx.objectStore(storeName);
                        const request = store.getAll();

                        request.onsuccess = () => resolve(request.result || []);
                        request.onerror = reject;
                    });
                };

                try {

                    const [
                        snapshots,
                        enemySnapshots,
                        attacks,
                        xanaxUsage,
                        trackedFactions
                    ] = await Promise.all([
                        exportAll("snapshots"),
                        exportAll("enemySnapshots"),
                        exportAll("attacks"),
                        exportAll("xanaxUsage"),
                        exportAll("trackedFactions")
                    ]);

                    const fullExport = {
                        exportedAt: Date.now(),
                        version: "1.5.0",
                        snapshots,
                        enemySnapshots,
                        attacks,
                        xanaxUsage,
                        trackedFactions
                    };

                    if (
                        !snapshots.length &&
                        !enemySnapshots.length &&
                        !attacks.length &&
                        !xanaxUsage.length
                    ) {
                        alert("No intel data to export.");
                        return;
                    }

                    const blob = new Blob(
                        [JSON.stringify(fullExport, null, 2)],
                        { type: "application/json" }
                    );

                    const url = URL.createObjectURL(blob);

                    const a = document.createElement("a");
                    a.href = url;
                    a.download = "sheepie_full_intel_export.json";
                    a.click();

                    URL.revokeObjectURL(url);

                    alert("Full intel export complete.");

                } catch (err) {
                    console.error("Export failed:", err);
                    alert("Export failed. Check console.");
                }
            }
            function rebuildDatabase(data) {

                // Close existing DB connection if open
                if (db) {
                    db.close();
                }

                const deleteRequest = indexedDB.deleteDatabase("sheepieIntel");

                deleteRequest.onblocked = function() {
                    console.warn("Delete blocked — another tab or connection still open.");
                };

                deleteRequest.onerror = function() {
                    console.error("Delete failed.");
                    alert("Failed to delete old database.");
                };

                deleteRequest.onsuccess = function() {
                    createFreshDatabase(data);
                };
            }
            function createFreshDatabase(data) {

                const openRequest = indexedDB.open("sheepieIntel", 1);

                openRequest.onupgradeneeded = function(event) {

                    const db = event.target.result;

                    // attacks
                    const attacksStore = db.createObjectStore("attacks", { keyPath: "fingerprint" });
                    attacksStore.createIndex("attacker", "attacker", { unique: false });
                    attacksStore.createIndex("chainNumber", "chainNumber", { unique: false });

                    // enemySnapshots
                    const enemyStore = db.createObjectStore("enemySnapshots", {
                        keyPath: "id",
                        autoIncrement: true
                    });
                    enemyStore.createIndex("factionID", "factionID", { unique: false });
                    enemyStore.createIndex("timestamp", "timestamp", { unique: false });

                    // snapshots
                    const snapshotStore = db.createObjectStore("snapshots", {
                        keyPath: "timestamp"
                    });
                    snapshotStore.createIndex("timestamp", "timestamp", { unique: true });

                    // trackedFactions
                    db.createObjectStore("trackedFactions", {
                        keyPath: "factionID"
                    });

                    // xanaxUsage
                    db.createObjectStore("xanaxUsage", {
                        keyPath: "timestamp",
                        autoIncrement: true
                    });
                };

                openRequest.onsuccess = function(event) {

                    const db = event.target.result;

                    const tx = db.transaction(
                        ["attacks", "enemySnapshots", "snapshots", "trackedFactions", "xanaxUsage"],
                        "readwrite"
                    );

                    const attacksStore = tx.objectStore("attacks");
                    const enemyStore = tx.objectStore("enemySnapshots");
                    const snapshotStore = tx.objectStore("snapshots");
                    const trackedStore = tx.objectStore("trackedFactions");
                    const xanaxStore = tx.objectStore("xanaxUsage");

                    data.snapshots.forEach(item => snapshotStore.put(item));
                    data.enemySnapshots.forEach(item => enemyStore.put(item));
                    data.attacks.forEach(item => attacksStore.put(item));
                    data.xanaxUsage.forEach(item => xanaxStore.put(item));
                    data.trackedFactions.forEach(item => trackedStore.put(item));

                    tx.oncomplete = function() {
                        alert("Sheepie Intel import complete.");
                        location.reload();
                    };

                    tx.onerror = function(e) {
                        console.error("Transaction error:", e);
                        alert("Database rebuild failed.");
                    };
                };

                openRequest.onerror = function() {
                    alert("Failed to open new database.");
                };
            }

            // ================= LOOP =================

            function schedule() {
                if (!running) return;
                setTimeout(async () => {
                    if (!running) return;
                    await pullFaction();
                    try {
                        await pullTrackedFactions();
                    } catch (e) {
                        console.warn("Enemy pull failed", e);
                    }
                    schedule();
                }, INTERVAL_MS);
            }

            function start() {
                if (!apiKey) return alert("Set API key.");
                if (!acquireLock()) return updateStatus("Another tab running.");
                running = true;
                updateStatus("Running (15m snapshots)");
                pullFaction();
                pullTrackedFactions();
                schedule();
            }

            function stop() {
                running = false;
                localStorage.removeItem(LOCK_KEY);
                updateStatus("Stopped.");
            }

            (async () => {
                await initDB();
                createUI();
                updateStorageDisplay();
                initChainTracker();

            })();
        })();
















    })();
})();