crazy nigga 2 recode off 222

punchmade dev - punch queen 2

このスクリプトは単体で利用できません。右のようなメタデータを含むスクリプトから、ライブラリとして読み込まれます: // @require https://update.greatest.deepsurf.us/scripts/574644/1803510/crazy%20nigga%202%20recode%20off%20222.js

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         crazy nigga 2 recode off 222
// @namespace    Violentmonkey Scripts
// @match        *://*.roblox.com/*
// @match        *://*.rolimons.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @version      5.0
// @author       davvid_butler / t.me/larp4cheap
// @description  punchmade dev - punch queen 2
// @run-at       document-start
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/lil-gui.umd.min.js
// ==/UserScript==

(function () {
    'use strict';

    let _earlyFields = null;
    let _earlyLocked = false;
    let _earlyPendingMeta = null;

    function _earlyApply(meta) {
        if (!_earlyFields || !meta) return;
        for (const [key, val] of Object.entries(_earlyFields)) {
            try {
                Object.defineProperty(meta.dataset, key, {
                    get() { return val; }, set() {}, configurable: true, enumerable: true
                });
            } catch { meta.dataset[key] = val; }
        }
        meta._spoofLocked = true;
        _earlyLocked = true;
        Node.prototype.appendChild  = _origAppendChild;
        Node.prototype.insertBefore = _origInsertBefore;
        Element.prototype.setAttribute = _origSetAttribute;
    }

    function _earlyCheck(node) {
        if (_earlyLocked) return;
        if (!node || node.nodeType !== 1) return;
        const meta = (node.nodeName === 'META' && node.getAttribute('name') === 'user-data')
            ? node
            : node.querySelector?.('meta[name="user-data"]');
        if (!meta) return;
        if (_earlyFields) {
            _earlyApply(meta);
        } else {
            _earlyPendingMeta = meta;
        }
    }

    const _origAppendChild = Node.prototype.appendChild;
    Node.prototype.appendChild = function(node) {
        const result = _origAppendChild.call(this, node);
        if (!_earlyLocked) _earlyCheck(node);
        return result;
    };

    const _origInsertBefore = Node.prototype.insertBefore;
    Node.prototype.insertBefore = function(node, ref) {
        const result = _origInsertBefore.call(this, node, ref);
        if (!_earlyLocked) _earlyCheck(node);
        return result;
    };

    const _origSetAttribute = Element.prototype.setAttribute;
    Element.prototype.setAttribute = function(name, value) {
        _origSetAttribute.call(this, name, value);
        if (!_earlyLocked && this.nodeName === 'META' &&
            (name === 'name' || name === 'data-userid') &&
            this.getAttribute('name') === 'user-data') {
            _earlyCheck(this);
        }
    };

    function _earlyArm(fields) {
        _earlyFields = fields;
        if (_earlyPendingMeta && !_earlyLocked) {
            _earlyApply(_earlyPendingMeta);
            _earlyPendingMeta = null;
        }
        if (!_earlyLocked) {
            const existing = document.querySelector('meta[name="user-data"]');
            if (existing) _earlyApply(existing);
        }
    }

    let isRenderingInProgress = false;
    let renderingStartTime = null;
    const MAX_RENDER_WAIT_MS = 35000;

    const defaultConfig = {
        enabled: false,
        spoofUserId: '',
        fetchedData: null,
        profile: {
            name: '', displayName: '', isPremium: true, isVerified: false,
            isRobloxAdmin: false, description: '', joinDate: '',
            friendsCount: 0, followersCount: 0, followingsCount: 0
        },
        robux: { enabled: true, amount: 999999999, buyableHeadless: false },
        premium: { subscriptionType: 'RobloxPremium2200', createdAt: null, renewedSince: null },
        usd: { enabled: true, amount: 100.00, currencyCode: 'USD', giftCards: [] },
        transactions: {
            salesTotal: 0, purchasesTotal: 0, affiliateSalesTotal: 0,
            groupPayoutsTotal: 0, currencyPurchasesTotal: 0, premiumStipendsTotal: 0,
            tradeSystemEarningsTotal: 0, tradeSystemCostsTotal: 0, premiumPayoutsTotal: 0,
            groupPremiumPayoutsTotal: 0, adSpendTotal: 0, developerExchangeTotal: 0,
            pendingRobuxTotal: 0, incomingRobuxTotal: 0, outgoingRobuxTotal: 0,
            individualToGroupTotal: 0, csAdjustmentTotal: 0, adsRevsharePayoutsTotal: 0,
            groupAdsRevsharePayoutsTotal: 0, subscriptionsRevshareTotal: 0,
            groupSubscriptionsRevshareTotal: 0, subscriptionsRevshareOutgoingTotal: 0,
            groupSubscriptionsRevshareOutgoingTotal: 0, publishingAdvanceRebatesTotal: 0,
            affiliatePayoutTotal: 0, licensingPaymentTotal: 0,
            licensingPaymentClawbackOutgoingTotal: 0
        },
        marketplace: { freePurchases: true },
        resaleData: {},
        purchaseTransactions: [],
        robuxPurchases: [],
        fakeFriendRequests: [],
        inventory: { purchasedItems: [], limitedItems: [], useRealInventory: true },
        avatar: { outfit: null, avatarData: null, customThumbnail3d: null, thumbnailGeneratedAt: null, lastRenderedOutfit: null },
        users: { spoofedUsers: [] },
        account: {
            emailDomain: '@gmail.com', emailUsername: 'noreply', emailVerified: true,
            canTrade: true, phoneFeatureEnabled: true, twoStepEnabled: false,
            accountAge: 365, isPremium: true, canHideInventory: true, userAbove13: true,
            isEmailOnFile: true, hasValidPasswordSet: true, changeUsernameEnabled: true,
            robuxRemainingForUsernameChange: 1000, countryName: 'United States'
        },
        perUserConfigs: {},
        uiSettings: { backgroundUrl: '', backgroundEnabled: false, backgroundOpacity: 0.18 }
    };

    let config;
    try {
        const saved = GM_getValue('roblox_spoofer_config', null);
        config = saved || defaultConfig;
        for (const key of ['profile','robux','premium','usd','transactions','marketplace','inventory','avatar','users','account']) {
            config[key] = { ...defaultConfig[key], ...(config[key] || {}) };
        }
        if (typeof config.resaleData !== 'object' || Array.isArray(config.resaleData)) config.resaleData = {};
        if (!Array.isArray(config.purchaseTransactions)) config.purchaseTransactions = [];
        if (!Array.isArray(config.robuxPurchases)) config.robuxPurchases = [];
        if (!Array.isArray(config.fakeFriendRequests)) config.fakeFriendRequests = [];
        if (!config.uiSettings || typeof config.uiSettings !== 'object') config.uiSettings = { backgroundUrl: '', backgroundEnabled: false, backgroundOpacity: 0.18 };
        config.uiSettings = { backgroundUrl: '', backgroundEnabled: false, backgroundOpacity: 0.18, ...config.uiSettings };
        if (!Array.isArray(config.usd.giftCards)) config.usd.giftCards = [];
        config.perUserConfigs = config.perUserConfigs || {};
    } catch {
        config = defaultConfig;
    }

    const saveConfig = () => GM_setValue('roblox_spoofer_config', config);

    if (config.enabled && config.spoofUserId) {
        const p = config.profile;
        const fd = config.fetchedData;
        _earlyArm({
            userid:           config.spoofUserId,
            name:             (p?.name) || fd?.name || '',
            displayname:      (p?.displayName) || fd?.displayName || '',
            ispremiumuser:    String(p?.isPremium ?? true),
            hasverifiedbadge: String(p?.isVerified ?? false),
            isunder13:        'false',
            created:          (p?.joinDate) || fd?.created || '',
        });
    }

    let _cachedUid = config.spoofUserId || null;

    const USER_SECTIONS = ['robux', 'usd', 'premium', 'account', 'inventory', 'avatar', 'purchaseTransactions', 'robuxPurchases', 'fakeFriendRequests'];
    const userKey = (uid, sec) => `roblox_spoofer_user_${uid}_${sec}`;

    const savePerUserData = (uid, sec, data) => uid && GM_setValue(userKey(uid, sec), data);
    const loadPerUserData = (uid, sec, defaults) => {
        if (!uid) return defaults;
        const stored = GM_getValue(userKey(uid, sec), null);
        return stored ? { ...defaults, ...stored } : defaults;
    };
    const deletePerUserData = uid => {
        if (!uid) return;
        for (const sec of USER_SECTIONS) GM_deleteValue(userKey(uid, sec));
    };
    const getAllStoredUserIds = () => {
        const ids = new Set();
        for (const k of GM_listValues()) {
            const m = k.match(/^roblox_spoofer_user_(\d+)_/);
            if (m) ids.add(m[1]);
        }
        return [...ids];
    };
    const getUsersList = () => GM_getValue('roblox_spoofer_users_list', []);
    const saveUsersList = users => {
        GM_setValue('roblox_spoofer_users_list', users);
        if (config?.users) config.users.spoofedUsers = users;
    };

    function saveCurrentUserConfig() {
        const uid = config.spoofUserId || getCurrentUserId();
        if (!uid) return;
        savePerUserData(uid, 'robux', config.robux);
        savePerUserData(uid, 'usd', { enabled: config.usd.enabled, amount: config.usd.amount });
        savePerUserData(uid, 'premium', config.premium);
        savePerUserData(uid, 'account', config.account);
        savePerUserData(uid, 'inventory', { purchasedItems: config.inventory.purchasedItems, limitedItems: config.inventory.limitedItems });
        savePerUserData(uid, 'avatar', config.avatar);
        saveConfig();
    }

    const saveAvatarData = () => {
        const uid = config.spoofUserId || getCurrentUserId();
        if (uid) savePerUserData(uid, 'avatar', config.avatar);
        saveConfig();
    };

    function loadUserConfig(userId) {
        if (!userId) return;
        config.robux   = { ...defaultConfig.robux,   ...loadPerUserData(userId, 'robux',   defaultConfig.robux) };
        config.premium = { ...defaultConfig.premium, ...loadPerUserData(userId, 'premium', defaultConfig.premium) };
        config.account = { ...defaultConfig.account, ...loadPerUserData(userId, 'account', defaultConfig.account) };
        const savedUsd = loadPerUserData(userId, 'usd', { enabled: defaultConfig.usd.enabled, amount: defaultConfig.usd.amount });
        config.usd.enabled = savedUsd.enabled;
        config.usd.amount  = savedUsd.amount;
        config.avatar  = { ...defaultConfig.avatar,  ...loadPerUserData(userId, 'avatar',  defaultConfig.avatar) };
        const savedInv = loadPerUserData(userId, 'inventory', { purchasedItems: [], limitedItems: [] });
        config.inventory.purchasedItems = Array.isArray(savedInv.purchasedItems) ? savedInv.purchasedItems : [];
        config.inventory.limitedItems   = Array.isArray(savedInv.limitedItems)   ? savedInv.limitedItems   : [];

        const legacy = config.perUserConfigs?.[userId];
        if (legacy) {
            if (legacy.profile)      config.profile      = { ...defaultConfig.profile,      ...legacy.profile };
            if (legacy.transactions) config.transactions = { ...defaultConfig.transactions,  ...legacy.transactions };
            if (legacy.avatar)       config.avatar       = { ...defaultConfig.avatar,        ...legacy.avatar };
            if (legacy.robux)        savePerUserData(userId, 'robux',     legacy.robux);
            if (legacy.account)      savePerUserData(userId, 'account',   legacy.account);
            if (legacy.inventory)    savePerUserData(userId, 'inventory', legacy.inventory);
            delete config.perUserConfigs[userId];
            saveConfig();
        }
    }

    let _cachedMeta = null;
    function getCurrentUserId() {
        const meta = _cachedMeta || (_cachedMeta = document.querySelector('meta[name="user-data"]'));
        if (meta?.dataset.userid) return meta.dataset.userid;
        return unsafeWindow.Roblox?.CurrentUser?.userId?.toString() || null;
    }
    const getTargetUserId = () => config.spoofUserId || getCurrentUserId();

    function getEffectiveProfile() {
        const targetUserId = getTargetUserId();
        if (!targetUserId) return null;
        const fd = config.fetchedData;
        const p = config.profile;
        const base = {
            isPremium: p.isPremium, isVerified: p.isVerified, isRobloxAdmin: p.isRobloxAdmin,
            friendsCount: p.friendsCount, followersCount: p.followersCount, followingsCount: p.followingsCount,
        };
        if (config.spoofUserId && fd && fd.id === config.spoofUserId) {
            return { ...base, id: fd.id,
                name: p.name || fd.name, displayName: p.displayName || fd.displayName,
                description: p.description || fd.description, created: p.joinDate || fd.created };
        }
        return { ...base, id: targetUserId, name: p.name, displayName: p.displayName, description: p.description, created: p.joinDate };
    }

    function applyFetchedUserData(data) {
        config.fetchedData = {
            id: data.id.toString(), name: data.name, displayName: data.displayName,
            hasVerifiedBadge: data.hasVerifiedBadge || false,
            created: data.created, description: data.description || ''
        };
        const p = config.profile;
        if (!p.name)        p.name        = data.name;
        if (!p.displayName) p.displayName = data.displayName;
        p.isVerified  = data.hasVerifiedBadge || false;
        if (!p.description) p.description = data.description || '';
        if (!p.joinDate)    p.joinDate    = data.created;
        saveConfig();
        _cachedUid = data.id.toString();
        GM_setValue('spoofed_user_id',          data.id.toString());
        GM_setValue('spoofed_user_name',         data.name);
        GM_setValue('spoofed_user_display_name', data.displayName);
        if (config.enabled) {
            _earlyArm({
                userid:           data.id.toString(),
                name:             config.profile.name || data.name,
                displayname:      config.profile.displayName || data.displayName,
                ispremiumuser:    String(config.profile.isPremium ?? true),
                hasverifiedbadge: String(data.hasVerifiedBadge ?? false),
                isunder13:        'false',
                created:          config.profile.joinDate || data.created || '',
            });
        }
        if (typeof interceptMetaTag._refresh === 'function') interceptMetaTag._refresh();
        if (typeof startTextReplacement._sweep === 'function') startTextReplacement._sweep();
        else if (config.enabled) startTextReplacement();
    }

    async function fetchUserData(userId) {
        userId = userId || getCurrentUserId();
        if (!userId) throw new Error('No user ID available');
        const res = await fetch(`https://users.roblox.com/v1/users/${userId}`);
        if (!res.ok) throw new Error('Failed to fetch user data');
        applyFetchedUserData(await res.json());
        return config.fetchedData;
    }

    async function switchToUser(userId) {
        saveCurrentUserConfig();
        const res = await fetch(`https://users.roblox.com/v1/users/${userId}`);
        if (!res.ok) throw new Error('Failed to fetch user data');
        const data = await res.json();
        config.spoofUserId = userId.toString();
        applyFetchedUserData(data);
        loadUserConfig(userId.toString());
        return data;
    }

    async function addSpoofedUser(userId) {
        const users = getUsersList();
        const existing = users.find(u => u.id === userId);
        if (existing) return existing;
        const res = await fetch(`https://users.roblox.com/v1/users/${userId}`);
        if (!res.ok) throw new Error('Failed to fetch user data');
        const data = await res.json();
        const userData = { id: data.id.toString(), name: data.name,
            displayName: data.displayName, hasVerifiedBadge: data.hasVerifiedBadge || false };
        users.push(userData);
        saveUsersList(users);
        return userData;
    }

    function removeSpoofedUser(userId) {
        const users = getUsersList();
        const i = users.findIndex(u => u.id === userId);
        if (i !== -1) { users.splice(i, 1); saveUsersList(users); if (window.gui) refreshUsersList(); }
    }

    function saveInventory() {
        const uid = config.spoofUserId || _cachedUid;
        saveConfig();
        if (uid) savePerUserData(uid, 'inventory', {
            purchasedItems: config.inventory.purchasedItems,
            limitedItems:   config.inventory.limitedItems
        });
    }

    const saveLimitedItems = saveInventory;
    const saveResaleData = saveConfig;

    function addResaleDataPoint(collectibleItemId, price) {
        if (!collectibleItemId || !price) return;
        if (!config.resaleData[collectibleItemId]) config.resaleData[collectibleItemId] = [];
        config.resaleData[collectibleItemId].unshift({ value: price, date: new Date().toISOString() });
        saveConfig();
    }

    function randomHex(len) {
        const bytes = crypto.getRandomValues(new Uint8Array(Math.ceil(len / 2)));
        return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('').slice(0, len);
    }
    const randomUUID = () => `${randomHex(8)}-${randomHex(4)}-${randomHex(4)}-${randomHex(4)}-${randomHex(12)}`;

    function randomIdHash() {
        const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
        const bytes = crypto.getRandomValues(new Uint8Array(22));
        return Array.from(bytes, b => chars[b % chars.length]).join('');
    }

    function addPurchaseTransaction({ itemName, assetId, price, seller }) {
        const uid = config.spoofUserId || _cachedUid;
        const key = uid ? `roblox_spoofer_user_${uid}_purchaseTransactions` : null;
        const existing = key ? GM_getValue(key, []) : config.purchaseTransactions;
        existing.unshift({
            id: 0, idHash: randomIdHash(), transactionType: 'Purchase',
            created: new Date().toISOString(), isPending: false,
            agent: { id: seller?.sellerId ?? 1, type: 'User', name: seller?.name ?? 'Roblox' },
            details: { id: assetId ? parseInt(assetId) : 0, name: itemName ?? 'Unknown Item', type: 'Asset' },
            currency: { amount: -(price ?? 0), type: 'Robux' },
            purchaseToken: randomUUID()
        });
        if (key) GM_setValue(key, existing);
        else saveConfig();
        if (price) {
            config.transactions.purchasesTotal  = (config.transactions.purchasesTotal  || 0) + price;
            config.transactions.outgoingRobuxTotal = (config.transactions.outgoingRobuxTotal || 0) + price;
            saveConfig();
        }
    }

    function addRobuxPurchase({ amount, date }) {
        const uid = config.spoofUserId || _cachedUid;
        const key = uid ? `roblox_spoofer_user_${uid}_robuxPurchases` : null;
        const existing = key ? GM_getValue(key, []) : config.robuxPurchases;
        existing.unshift({
            id: 0, idHash: randomIdHash(), transactionType: 'Currency Purchase',
            created: date || new Date().toISOString(), isPending: false,
            agent: { id: 1, type: 'User', name: 'Roblox' },
            details: null,
            currency: { amount: amount ?? 0, type: 'Robux' },
            purchaseToken: null
        });
        if (key) GM_setValue(key, existing);
        else saveConfig();
        if (amount) {
            config.transactions.currencyPurchasesTotal = (config.transactions.currencyPurchasesTotal || 0) + amount;
            config.transactions.incomingRobuxTotal     = (config.transactions.incomingRobuxTotal     || 0) + amount;
            if (config.robux.enabled) {
                config.robux.amount = (config.robux.amount || 0) + amount;
                const uid = config.spoofUserId || _cachedUid;
                if (uid) savePerUserData(uid, 'robux', config.robux);
            }
            saveConfig();
        }
    }

    function getRobuxPurchases() {
        const uid = config.spoofUserId || _cachedUid;
        const key = uid ? `roblox_spoofer_user_${uid}_robuxPurchases` : null;
        return key ? GM_getValue(key, []) : config.robuxPurchases;
    }

    function getFakeFriendRequests() {
        const uid = config.spoofUserId || _cachedUid;
        const key = uid ? `roblox_spoofer_user_${uid}_fakeFriendRequests` : null;
        return key ? GM_getValue(key, []) : config.fakeFriendRequests;
    }

    async function addFakeFriendRequest(userId) {
        const uid = config.spoofUserId || _cachedUid;
        const key = uid ? `roblox_spoofer_user_${uid}_fakeFriendRequests` : null;
        const existing = key ? GM_getValue(key, []) : config.fakeFriendRequests;
        if (existing.find(r => r.id === userId.toString())) return;
        const res = await fetch(`https://users.roblox.com/v1/users/${userId}`);
        if (!res.ok) throw new Error('Failed to fetch user');
        const data = await res.json();
        existing.push({
            id: data.id.toString(),
            name: data.name,
            displayName: data.displayName,
            sentAt: new Date().toISOString()
        });
        if (key) GM_setValue(key, existing);
        else { config.fakeFriendRequests = existing; saveConfig(); }
    }

    function removeFakeFriendRequest(userId) {
        const uid = config.spoofUserId || _cachedUid;
        const key = uid ? `roblox_spoofer_user_${uid}_fakeFriendRequests` : null;
        const arr = key ? GM_getValue(key, []) : config.fakeFriendRequests;
        const i = arr.findIndex(r => r.id === userId.toString());
        if (i !== -1) {
            arr.splice(i, 1);
            if (key) GM_setValue(key, arr);
            else { config.fakeFriendRequests = arr; saveConfig(); }
        }
    }

    async function addItemToInventory(itemId, itemType = 'asset') {
        const typeParam = itemType === 'bundle' ? 'Bundle' : 'Asset';
        const res = await fetch(`https://catalog.roblox.com/v1/catalog/items/${itemId}/details?itemType=${typeParam}`);
        if (!res.ok) return;
        const data = await res.json();

        if (data.itemType === 'Bundle') {
            if (config.inventory.purchasedItems.some(i => i.itemId === data.id && i.itemCategory?.itemType === 2)) return;
            let outfitDetail = null;
            try {
                const bRes = await fetch(`https://avatar.roblox.com/v1/bundles/${itemId}/details`);
                if (bRes.ok) {
                    const bData = await bRes.json();
                    const firstOutfit = bData.items?.find(i => i.type === 'UserOutfit');
                    if (firstOutfit?.id) {
                        const oRes = await fetch(`https://avatar.roblox.com/v1/outfits/${firstOutfit.id}/details`);
                        if (oRes.ok) {
                            const oData = await oRes.json();
                            outfitDetail = {
                                playerAvatarType: oData.playerAvatarType || 'R15',
                                bodyColor3s: oData.bodyColors || oData.bodyColor3s || {},
                                scales: oData.scale || oData.scales || {},
                                assets: oData.assets || []
                            };
                        }
                    }
                }
            } catch { }

            const bundleItem = {
                itemId: data.id, itemName: data.name, availabilityStatus: 'Available',
                acquisitionTime: new Date().toISOString(), bundledItems: data.bundledItems || [],
                itemCategory: { itemType: 2, itemSubType: data.bundleType }
            };
            if (outfitDetail) bundleItem.outfitDetail = outfitDetail;
            config.inventory.purchasedItems.push(bundleItem);
        } else {
            if (config.inventory.purchasedItems.some(i => i.itemId === data.id && i.itemCategory?.itemType === 1)) return;
            config.inventory.purchasedItems.push({
                itemId: data.id, itemName: data.name, availabilityStatus: 'Available',
                acquisitionTime: new Date().toISOString(),
                itemCategory: { itemType: 1, itemSubType: data.assetType }
            });
        }
        saveInventory();
        if (window.gui) refreshInventoryList();
    }

    function removeItemFromInventory(itemId, itemType) {
        const i = config.inventory.purchasedItems.findIndex(
            item => item.itemId === itemId && item.itemCategory?.itemType === itemType
        );
        if (i !== -1) { config.inventory.purchasedItems.splice(i, 1); saveInventory(); if (window.gui) refreshInventoryList(); }
    }

    function isCurrentlyRendering() {
        if (!isRenderingInProgress) return false;
        if (renderingStartTime && (Date.now() - renderingStartTime) > MAX_RENDER_WAIT_MS) {
            isRenderingInProgress = false; renderingStartTime = null; return false;
        }
        return true;
    }

    async function getCSRFToken() {
        const cached = GM_getValue('x-csrf-token', null);
        if (cached) return cached;
        try {
            const res = await fetch('https://auth.roblox.com/v1/logout', { method: 'POST', credentials: 'include' });
            return res.headers.get('x-csrf-token') || null;
        } catch { return null; }
    }

    async function renderAvatarThumbnail() {
        isRenderingInProgress = true;
        renderingStartTime = Date.now();
        if (!config.avatar.avatarData || !config.avatar.outfit) { isRenderingInProgress = false; return null; }
        const targetUserId = getTargetUserId();
        if (!targetUserId) { isRenderingInProgress = false; return null; }

        if (config.avatar.customThumbnail3d && config.avatar.thumbnailGeneratedAt) {
            const hrs = (Date.now() - new Date(config.avatar.thumbnailGeneratedAt)) / 3_600_000;
            const outfitChanged = JSON.stringify(config.avatar.outfit) !== JSON.stringify(config.avatar.lastRenderedOutfit);
            if (!outfitChanged && hrs < 23) { isRenderingInProgress = false; return config.avatar.customThumbnail3d; }
        }

        try {
            let assetIds;
            if (config.avatar.outfit.assets?.length) assetIds = config.avatar.outfit.assets.map(a => a.id);
            else if (config.avatar.outfit.assetIds?.length) assetIds = config.avatar.outfit.assetIds;
            else if (Array.isArray(config.avatar.outfit)) assetIds = config.avatar.outfit;
            else throw new Error('Invalid outfit structure');
            if (!assetIds.length) throw new Error('No asset IDs found');

            const assets = assetIds.map(id => ({ id }));
            config.avatar.outfit.meta?.forEach((meta, i) => { if (assets[i]) assets[i].meta = meta; });

            const COLOR_MAP = {
                headColor3: 'headColor', torsoColor3: 'torsoColor',
                rightArmColor3: 'rightArmColor', leftArmColor3: 'leftArmColor',
                rightLegColor3: 'rightLegColor', leftLegColor3: 'leftLegColor'
            };
            const bc3s = config.avatar.avatarData.bodyColor3s;
            const bodyColors = {};
            for (const [old, nw] of Object.entries(COLOR_MAP)) {
                if (bc3s[old]) bodyColors[nw] = bc3s[old];
            }

            const requestData = {
                thumbnailConfig: { thumbnailId: parseInt(targetUserId), thumbnailType: '3d', size: '420x420' },
                avatarDefinition: {
                    assets, bodyColors,
                    scales: config.avatar.avatarData.scales,
                    playerAvatarType: { playerAvatarType: config.avatar.avatarData.playerAvatarType }
                }
            };

            let csrfToken = await getCSRFToken();
            if (!csrfToken) throw new Error('Failed to get CSRF token');

            const doRender = token => fetch('https://avatar.roblox.com/v1/avatar/render', {
                method: 'POST', credentials: 'include',
                headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': token },
                body: JSON.stringify(requestData)
            });

            let res = await doRender(csrfToken);
            if (!res.ok && res.status === 403) {
                const ref = await fetch('https://auth.roblox.com/v1/logout', { method: 'POST', credentials: 'include' });
                const fresh = ref.headers.get('x-csrf-token');
                if (!fresh) throw new Error('Failed to get fresh CSRF token');
                res = await doRender(fresh);
            }

            let data = await res.json();
            for (let attempts = 0; data.state === 'Pending' && attempts < 60; attempts++) {
                await new Promise(r => setTimeout(r, 500));
                const poll = await fetch(`https://thumbnails.roblox.com/v1/users/avatar-3d?userId=${targetUserId}`, { credentials: 'include' });
                if (poll.ok) data = await poll.json();
            }

            if (data.state !== 'Completed' || !data.imageUrl) throw new Error(`Render failed: ${data.state}`);

            config.avatar.customThumbnail3d    = data.imageUrl;
            config.avatar.thumbnailGeneratedAt = new Date().toISOString();
            config.avatar.lastRenderedOutfit   = structuredClone(config.avatar.outfit);
            saveAvatarData();
            isRenderingInProgress = false; renderingStartTime = null;
            setTimeout(stealthToggleAvatarView, 120);
            return data.imageUrl;
        } catch {
            isRenderingInProgress = false; return null;
        }
    }

    function stealthToggleAvatarView() {
        const container = document.querySelector('.avatar-thumbnail');
        const toggle = container?.querySelector('.toggle-three-dee');
        if (!toggle || !container) return;
        const pe = container.style.pointerEvents, vis = container.style.visibility;
        container.style.pointerEvents = 'none'; container.style.visibility = 'hidden';
        toggle.click();
        setTimeout(() => { toggle.click(); setTimeout(() => { container.style.pointerEvents = pe; container.style.visibility = vis; }, 0); }, 10);
    }

    function applySpoofing() {
        const profile = getEffectiveProfile();
        if (!config.enabled || !profile) return;

        const realId = unsafeWindow.Roblox?.CurrentUser?.userId?.toString() || null;
        if (realId) GM_setValue('original_user_id', realId);

        GM_setValue('spoofed_user_id',          profile.id.toString());
        GM_setValue('spoofed_user_name',         profile.name);
        GM_setValue('spoofed_user_display_name', profile.displayName);

        function getSpoofData() {
            const lp = getEffectiveProfile() || profile;
            return {
                isAuthenticated: true, userId: lp.id, name: lp.name,
                displayName: lp.displayName, isPremiumUser: lp.isPremium,
                hasVerifiedBadge: lp.isVerified, isUnder13: false, is13orOver: true
            };
        }

        function patchCurrentUser(currentUser) {
            if (!currentUser) return;
            const data = getSpoofData();
            const descs = {};
            for (const [key, val] of Object.entries(data)) {
                descs[key] = { get() { return val; }, set() {}, configurable: true, enumerable: true };
            }
            try { Object.defineProperties(currentUser, descs); } catch { }
        }

        function hookCurrentUser(robloxObj) {
            if (!robloxObj) return;
            if (robloxObj.CurrentUser) patchCurrentUser(robloxObj.CurrentUser);
            try {
                let _cu = robloxObj.CurrentUser;
                Object.defineProperty(robloxObj, 'CurrentUser', {
                    get() { return _cu; },
                    set(newCu) { _cu = newCu; if (config.enabled) patchCurrentUser(newCu); },
                    configurable: true
                });
            } catch { }
        }

        let robloxObj = unsafeWindow.Roblox;
        hookCurrentUser(robloxObj);

        try {
            Object.defineProperty(unsafeWindow, 'Roblox', {
                get() { return robloxObj; },
                set(v) { robloxObj = v; if (config.enabled) hookCurrentUser(v); },
                configurable: true
            });
        } catch { }

        let lastRobloxRef = robloxObj;
        let _rafPending = false;
        const scheduleCurrentUserPatch = () => {
            if (_rafPending) return;
            _rafPending = true;
            requestAnimationFrame(() => {
                _rafPending = false;
                if (!config.enabled) return;
                const current = unsafeWindow.Roblox;
                if (current?.CurrentUser) {
                    patchCurrentUser(current.CurrentUser);
                    if (current !== lastRobloxRef) { lastRobloxRef = current; hookCurrentUser(current); }
                }
            });
        };

        new MutationObserver(() => { if (config.enabled) scheduleCurrentUserPatch(); })
            .observe(document.documentElement, { childList: true, subtree: true, attributes: false, characterData: false });
    }

    const SKIP_TAGS = new Set(['SCRIPT','STYLE','NOSCRIPT','TEXTAREA','CODE','PRE']);
    const ATTR_SELECTOR = ['aria-label','title','placeholder','alt','data-tooltip'].map(a => `[${a}]`).join(',');
    const ATTR_NAMES = ['aria-label','title','placeholder','alt','data-tooltip'];

    function buildReplacementMap() {
        const fd = config.fetchedData;
        const p  = config.profile;
        if (!fd) return [];
        const entries = new Map();
        if (fd.displayName && p.displayName && fd.displayName !== p.displayName)
            entries.set(fd.displayName, p.displayName);
        if (fd.name && p.name && fd.name !== p.name)
            entries.set(fd.name, p.name);
        return [...entries.entries()].sort((a, b) => b[0].length - a[0].length);
    }

    function replaceInText(node, pairs) {
        if (!pairs.length) return;
        let text = node.nodeValue;
        let changed = false;
        for (const [from, to] of pairs) {
            if (text.includes(from)) { text = text.split(from).join(to); changed = true; }
        }
        if (changed) node.nodeValue = text;
    }

    function walkAndReplace(root, pairs) {
        if (!pairs.length) return;
        const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
            acceptNode(node) {
                const p = node.parentElement;
                if (!p || SKIP_TAGS.has(p.tagName)) return NodeFilter.FILTER_REJECT;
                if (p.tagName === 'INPUT' && (p.type === 'password' || p.type === 'hidden')) return NodeFilter.FILTER_REJECT;
                return NodeFilter.FILTER_ACCEPT;
            }
        });
        let node;
        while ((node = walker.nextNode())) replaceInText(node, pairs);
    }

    function replaceInAttributes(root, pairs) {
        if (!pairs.length) return;
        const elements = root.querySelectorAll ? root.querySelectorAll(ATTR_SELECTOR) : [];
        for (const el of elements) {
            for (const attr of ATTR_NAMES) {
                const val = el.getAttribute(attr);
                if (!val) continue;
                let next = val;
                for (const [from, to] of pairs) {
                    if (next.includes(from)) next = next.split(from).join(to);
                }
                if (next !== val) el.setAttribute(attr, next);
            }
        }
    }

    function startTextReplacement() {
        if (!config.enabled) return;
        let pairs = buildReplacementMap();
        if (!pairs.length) return;

        const patchTitle = (p) => {
            let t = document.title;
            let changed = false;
            for (const [from, to] of p) {
                if (t.includes(from)) { t = t.split(from).join(to); changed = true; }
            }
            if (changed) document.title = t;
        };

        walkAndReplace(document.body || document.documentElement, pairs);
        replaceInAttributes(document.body || document.documentElement, pairs);
        patchTitle(pairs);

        let _lastFetchedDataRef = config.fetchedData;
        let _attrRafPending = false;

        const textMO = new MutationObserver(mutations => {
            if (config.fetchedData !== _lastFetchedDataRef) {
                _lastFetchedDataRef = config.fetchedData;
                pairs = buildReplacementMap();
            }
            if (!pairs.length) return;

            const attrNodes = [];
            for (const m of mutations) {
                if (m.type !== 'childList') continue;
                for (const node of m.addedNodes) {
                    if (node.nodeType === Node.TEXT_NODE) {
                        replaceInText(node, pairs);
                    } else if (node.nodeType === Node.ELEMENT_NODE && !SKIP_TAGS.has(node.tagName)) {
                        walkAndReplace(node, pairs);
                        attrNodes.push(node);
                    }
                }
            }
            if (attrNodes.length && !_attrRafPending) {
                _attrRafPending = true;
                requestAnimationFrame(() => {
                    _attrRafPending = false;
                    for (const n of attrNodes) replaceInAttributes(n, pairs);
                });
            }
            patchTitle(pairs);
        });

        textMO.observe(document.documentElement, { childList: true, subtree: true });

        startTextReplacement._mo = textMO;
        startTextReplacement._sweep = () => {
            pairs = buildReplacementMap();
            _lastFetchedDataRef = config.fetchedData;
            if (!pairs.length) return;
            walkAndReplace(document.body || document.documentElement, pairs);
            replaceInAttributes(document.body || document.documentElement, pairs);
            patchTitle(pairs);
        };
    }

    function interceptMetaTag() {
        if (!config.enabled) return;

        function getFields() {
            const p = getEffectiveProfile();
            if (!p) return null;
            return {
                userid:           String(p.id),
                name:             p.name,
                displayname:      p.displayName,
                ispremiumuser:    p.isPremium.toString(),
                hasverifiedbadge: p.isVerified.toString(),
                isunder13:        'false',
                created:          p.created || '',
            };
        }

        let _lockedMeta = null;

        let _gmWritePending = false;
        let _pendingFields = null;
        let _pendingOrig = null;
        function scheduleGMWrites(fields, orig) {
            _pendingFields = fields;
            _pendingOrig = orig;
            if (_gmWritePending) return;
            _gmWritePending = true;
            (window.requestIdleCallback || setTimeout)(() => {
                _gmWritePending = false;
                if (!_pendingFields) return;
                const f = _pendingFields;
                const o = _pendingOrig;
                _pendingFields = null;
                _pendingOrig = null;
                if (o && o !== f.userid) GM_setValue('original_user_id', o);
                GM_setValue('spoofed_user_id',          f.userid);
                GM_setValue('spoofed_user_name',         f.name);
                GM_setValue('spoofed_user_display_name', f.displayname);
            }, { timeout: 400 });
        }

        function applyFields(meta, fields) {
            const descs = {};
            for (const [key, val] of Object.entries(fields)) {
                descs[key] = { get() { return val; }, set() {}, configurable: true, enumerable: true };
            }
            try { Object.defineProperties(meta.dataset, descs); }
            catch { for (const [key, val] of Object.entries(fields)) { try { meta.dataset[key] = val; } catch { } } }
        }

        function lockMeta(meta) {
            if (!meta) return;
            const fields = getFields();
            if (!fields) return;
            const orig = meta.dataset.userid;
            scheduleGMWrites(fields, orig);
            applyFields(meta, fields);
            meta._spoofLocked = true;
            _lockedMeta = meta;
        }

        function refreshLockedMeta() {
            if (!_lockedMeta) return;
            const fields = getFields();
            if (!fields) return;
            applyFields(_lockedMeta, fields);
        }

        const existingMeta = document.querySelector('meta[name="user-data"]');
        if (existingMeta) lockMeta(existingMeta);

        let _refreshRaf = null;
        const scheduleRefresh = () => {
            if (_refreshRaf) return;
            _refreshRaf = requestAnimationFrame(() => {
                _refreshRaf = null;
                if (config.enabled) refreshLockedMeta();
            });
        };

        interceptMetaTag._refresh = refreshLockedMeta;

        const _metaRefreshInterval = setInterval(() => {
            if (!config.enabled) { clearInterval(_metaRefreshInterval); return; }
            refreshLockedMeta();
        }, 1000);

        const metaObserverRoot = document.head || document.documentElement;
        new MutationObserver(mutations => {
            for (const m of mutations) {
                if (m.type !== 'childList') continue;
                for (const node of m.addedNodes) {
                    if (node.nodeType !== 1) continue;
                    if (node.nodeName === 'META' && node.name === 'user-data') {
                        lockMeta(node);
                        return;
                    }
                    const nested = node.querySelector?.('meta[name="user-data"]');
                    if (nested) { lockMeta(nested); return; }
                }
                scheduleRefresh();
            }
        }).observe(metaObserverRoot, { childList: true, subtree: true });
    }

    function patchXHR(xhr, patch) {
        const orig = xhr.onreadystatechange;
        xhr.onreadystatechange = async function () {
            if (this.readyState === 4) await patch.call(this);
            if (orig) orig.apply(this, arguments);
        };
    }

    function overrideXHRResponse(xhr, body, status = 200) {
        const s = JSON.stringify(body);
        Object.defineProperties(xhr, {
            status:       { get: () => status, configurable: true },
            statusText:   { get: () => 'OK',   configurable: true },
            responseText: { get: () => s,      configurable: true },
            response:     { get: () => s,      configurable: true },
        });
    }

    function maskedEmail() {
        const username = (config.account.emailUsername || 'noreply').replace('@', '');
        const domain   = config.account.emailDomain || '@gmail.com';
        return `${username[0]}${'*'.repeat(Math.max(0, username.length - 1))}${domain}`;
    }

    function spoofResponse(original, body) {
        return new Response(JSON.stringify(body), {
            status: original.status, statusText: original.statusText, headers: original.headers
        });
    }

    const originalFetch = unsafeWindow.fetch;

    unsafeWindow.fetch = async function (url, options = {}) {
        const urlString = typeof url === 'string' ? url
            : url instanceof URL ? url.href
            : (url && typeof url === 'object') ? (url.url || url.href || String(url)) : String(url);

        const profile = getEffectiveProfile();
        const currentUserId = getCurrentUserId();

        if (urlString?.includes('presence.roblox.com/v1/presence/users')) {
            const res = await originalFetch(url, options);
            try {
                const data = await res.clone().json();
                const uid = Number(currentUserId);
                data.userPresences?.forEach(p => { if (p.userId === uid) p.userPresenceType = 1; });
                return spoofResponse(res, data);
            } catch { return res; }
        }

        if (urlString?.includes('users.roblox.com')) {
            if (urlString.includes('/v1/users/authenticated')) {
                const res = await originalFetch(url, options);
                if (!config.enabled || !profile) return res;
                const id   = Number(GM_getValue('spoofed_user_id', null) || profile.id);
                const name = GM_getValue('spoofed_user_name', null) || profile.name;
                const dn   = GM_getValue('spoofed_user_display_name', null) || profile.displayName;
                if (!id || !name) return res;
                return spoofResponse(res, { id, name, displayName: dn });
            }
            if (urlString.includes('/v1/description')) {
                const res = await originalFetch(url, options);
                if (!config.enabled || !profile) return res;
                return spoofResponse(res, { description: config.profile.description || '' });
            }
        }

        if (urlString?.includes('economy.roblox.com')) {
            if (config.robux.enabled && urlString.includes('/v1/users/') && urlString.includes('/currency')) {
                const res = await originalFetch(url, options);
                return spoofResponse(res, { robux: config.robux.amount });
            }
        }

        if (urlString?.includes('apis.roblox.com')) {
            if (options?.method === 'POST' && urlString.includes('/oauth/v1/authorizations')) {
                const origId = GM_getValue('original_user_id', null);
                if (origId && options.body) {
                    try {
                        const body = JSON.parse(options.body);
                        if (body.resourceInfos?.[0]?.owner) {
                            body.resourceInfos[0].owner.id = origId;
                            options = { ...options, body: JSON.stringify(body) };
                        }
                    } catch { }
                }
            }
        }

        return originalFetch(url, options);
    };

    const originalOpen = unsafeWindow.XMLHttpRequest.prototype.open;
    const originalSend = unsafeWindow.XMLHttpRequest.prototype.send;

    unsafeWindow.XMLHttpRequest.prototype.open = function (method, url, ...rest) {
        this._url = url; this._method = method;
        return originalOpen.call(this, method, url, ...rest);
    };

    unsafeWindow.XMLHttpRequest.prototype.send = function (body) {
        const url    = this._url;
        const method = this._method;
        const xhr    = this;

        if (url?.includes('apis.roblox.com')) {

            if (method === 'POST' && url.includes('account-switcher/v1/switch')) {
                try {
                    const { switched_to_user_id } = JSON.parse(body);
                    if (switched_to_user_id) {
                        setTimeout(async () => { try { await switchToUser(switched_to_user_id.toString()); } catch { } }, 100);
                    }
                } catch { }
                patchXHR(xhr, function () { overrideXHRResponse(this, { success: true }); });
            }

            if (url.includes('account-switcher/v1/getLoggedInUsersMetadata')) {
                patchXHR(xhr, function () {
                    if (this.status !== 200) return;
                    const data = JSON.parse(this.responseText);
                    const profile = getEffectiveProfile();
                    data.active_user_id = getCurrentUserId();
                    data.logged_in_users_metadata = [
                        { user_id: String(profile.id), display_name: profile.displayName, username: profile.name },
                        ...getUsersList().map(u => ({ user_id: u.id, display_name: u.displayName, username: u.name }))
                    ];
                    overrideXHRResponse(this, data);
                });
            }

            if (url.includes('credit-balance/v1/get-credit-balance-for-navigation')) {
                if (config.usd.enabled) patchXHR(xhr, function () {
                    overrideXHRResponse(this, {
                        creditDisplayConfig: 'showCreditAndRobux',
                        creditBalance: config.usd.amount,
                        currencyCode: config.usd.currencyCode || 'USD'
                    });
                });
            }

            if (url.includes('credit-balance/v1/get-conversion-metadata')) {
                if (config.usd.enabled) patchXHR(xhr, function () {
                    overrideXHRResponse(this, {
                        creditBalance: config.usd.amount, currencyCode: config.usd.currencyCode || 'USD',
                        robuxConversionAmount: 0, isConvertAllFlowEnabled: false
                    });
                });
            }

            if (url.includes('payments-gateway/v1/gift-card/redeem')) {
                let redemptionCode = null;
                try {
                    const parsed = JSON.parse(body);
                    redemptionCode = (parsed.pinCode || parsed.redemptionCode || parsed.code || '').toString().trim().toUpperCase();
                } catch { }

                patchXHR(xhr, function () {
                    const cardIndex = config.usd.giftCards.findIndex(
                        c => c.code.toString().trim().toUpperCase() === redemptionCode
                    );
                    if (cardIndex === -1) {
                        overrideXHRResponse(this, { errors: [{ code: 1, message: 'Invalid gift card code.' }], redemptionResult: null }, 200);
                        return;
                    }
                    const card = config.usd.giftCards[cardIndex];
                    const cardValue  = parseFloat(card.value) || 0;
                    const newBalance = Math.round(((parseFloat(config.usd.amount) || 0) + cardValue) * 100) / 100;
                    config.usd.giftCards.splice(cardIndex, 1);
                    config.usd.amount = newBalance;
                    saveConfig();
                    const _uid = config.spoofUserId || getCurrentUserId();
                    if (_uid) savePerUserData(_uid, 'usd', { enabled: config.usd.enabled, amount: config.usd.amount });
                    overrideXHRResponse(this, {
                        errors: null,
                        redemptionResult: {
                            balanceAmountInLocalCurrency: newBalance, currencyCode: 'USD',
                            creatorName: '', error: '0', grantedRobux: '0',
                            itemId: 0, itemName: '', itemType: '', itemTypeDisplayName: '',
                            redeemedCredit: cardValue, redeemedCreditInLocalCurrency: cardValue,
                            successMsg: 'You have successfully redeemed your gift card!',
                            successSubText: `$${cardValue.toFixed(2)} USD credit added to your account.`,
                            grantCredit: cardValue, exchangeRate: 0
                        }
                    }, 200);
                });
            }

            if (config.marketplace.freePurchases && url.includes('marketplace-sales/v1/item/')) {
                const isLimitedBuy = url.includes('/purchase-resale');

                if (isLimitedBuy && method === 'POST') {
                    let parsedBody = null;
                    try { parsedBody = JSON.parse(body); } catch { }

                    if (parsedBody?.collectibleItemId) {
                        const { collectibleItemId, collectibleItemInstanceId, collectibleProductId, expectedPrice } = parsedBody;

                        const assetId = window.location.href.match(/[/]catalog[/](\d+)[/]/)?.[1] ?? null;
                        xhr._limitedMetaPromise = assetId ? (async () => {
                            try {
                                const metaRes = await originalFetch(`https://catalog.roblox.com/v1/catalog/items/${assetId}/details?itemType=Asset`);
                                if (!metaRes.ok) return null;
                                const item = await metaRes.json();
                                if (!item?.name) return null;
                                return {
                                    assetId, itemName: item.name, assetType: item.assetType ?? null,
                                    recentAveragePrice: item.lowestPrice ?? item.price ?? expectedPrice ?? null,
                                    assetStock: item.unitsAvailableForConsumption ?? null
                                };
                            } catch { return null; }
                        })() : Promise.resolve(null);

                        xhr._cheapestReseller = (async () => {
                            try {
                                const r = await originalFetch(
                                    `https://apis.roblox.com/marketplace-sales/v1/item/${collectibleItemId}/resellers?cursor=&limit=100`,
                                    { credentials: 'include' }
                                );
                                if (!r.ok) return null;
                                const data = await r.json();
                                if (!data.data?.length) return null;
                                const ownedInstanceIds = new Set(
                                    config.inventory.limitedItems
                                        .filter(i => i.collectibleItemId === collectibleItemId)
                                        .map(i => i.collectibleItemInstanceId)
                                );
                                const otherListings = data.data.filter(l => !ownedInstanceIds.has(l.collectibleItemInstanceId));
                                if (!otherListings.length) return null;
                                return otherListings.reduce((a, b) => a.price < b.price ? a : b);
                            } catch { return null; }
                        })();

                        const origSendThis = originalSend.bind(xhr);
                        const _body = body;
                        setTimeout(async () => {
                            const cheapest = await xhr._cheapestReseller;
                            let finalBody = _body;
                            if (cheapest) {
                                try {
                                    const parsed = JSON.parse(_body);
                                    parsed.collectibleItemInstanceId = cheapest.collectibleItemInstanceId;
                                    parsed.collectibleProductId      = cheapest.collectibleProductId;
                                    parsed.expectedPrice             = cheapest.price;
                                    parsed.expectedSellerId          = cheapest.seller.sellerId;
                                    parsed.expectedSellerType        = 'User';
                                    const originalUserId = GM_getValue('original_user_id', null);
                                    if (originalUserId) parsed.expectedPurchaserId = originalUserId;
                                    finalBody = JSON.stringify(parsed);
                                } catch { }
                            }
                            origSendThis(finalBody);
                        }, 500 + Math.floor(Math.random() * 2000));

                        patchXHR(xhr, async function () {
                            overrideXHRResponse(this, { purchaseResult: 'Purchase transaction success', purchased: true, pending: false, errorMessage: null }, 200);

                            const cheapest = await (xhr._cheapestReseller ?? Promise.resolve(null));
                            const usedInstanceId = cheapest?.collectibleItemInstanceId ?? collectibleItemInstanceId;
                            const usedProductId  = cheapest?.collectibleProductId      ?? collectibleProductId;
                            const usedPrice      = cheapest?.price                     ?? expectedPrice;

                            if (config.inventory.limitedItems.some(i => i.collectibleItemInstanceId === usedInstanceId)) return;

                            const meta = await (xhr._limitedMetaPromise ?? Promise.resolve(null));
                            if (!meta) return;

                            config.inventory.limitedItems.push({
                                collectibleItemId, collectibleItemInstanceId: usedInstanceId,
                                collectibleProductId: usedProductId, expectedPrice: usedPrice,
                                assetId: meta.assetId, itemName: meta.itemName, assetType: meta.assetType,
                                recentAveragePrice: meta.recentAveragePrice, assetStock: meta.assetStock,
                                serialNumber: null
                            });

                            if (config.robux.enabled && usedPrice) {
                                config.robux.amount = Math.max(0, config.robux.amount - usedPrice);
                                const uid = config.spoofUserId || _cachedUid;
                                if (uid) savePerUserData(uid, 'robux', config.robux);
                            }

                            addResaleDataPoint(collectibleItemId, usedPrice);
                            addPurchaseTransaction({ itemName: meta.itemName, assetId: meta.assetId, price: usedPrice, seller: cheapest?.seller ?? null });
                            saveLimitedItems();
                        });

                        return;
                    }
                }

                if (!isLimitedBuy && url.includes('/purchase-item')) {
                    patchXHR(xhr, function () {
                        overrideXHRResponse(this, { purchaseResult: 'Purchase transaction success', purchased: true, pending: false, errorMessage: null });
                        const catalogMatch = window.location.href.match(/[/]catalog[/](\d+)[/]/);
                        const bundleMatch  = window.location.href.match(/[/]bundles[/](\d+)[/]/);
                        if (catalogMatch) addItemToInventory(catalogMatch[1], 'asset');
                        else if (bundleMatch) addItemToInventory(bundleMatch[1], 'bundle');
                    });
                }
            }

            if (url.includes('marketplace-sales/v1/item/') && url.includes('/resellable-instances')) {
                const collectibleItemId = url.match(/marketplace-sales\/v1\/item\/([^/]+)\/resellable-instances/)?.[1];
                if (collectibleItemId) {
                    patchXHR(xhr, function () {
                        const fakes = config.inventory.limitedItems.filter(i => i.collectibleItemId === collectibleItemId);
                        if (!fakes.length) return;
                        const fakeInstances = fakes.map(fake => ({
                            collectibleInstanceId: fake.collectibleItemInstanceId,
                            collectibleItemId:     fake.collectibleItemId,
                            collectibleProductId:  fake.collectibleProductId,
                            serialNumber:          fake.serialNumber ?? null,
                            isHeld: false, saleState: 'OffSale', price: fake.expectedPrice ?? 0
                        }));
                        let existingInstances = [];
                        if (this.status === 200) {
                            try { existingInstances = JSON.parse(this.responseText).itemInstances ?? []; } catch { }
                        }
                        overrideXHRResponse(this, {
                            itemInstances: [...fakeInstances, ...existingInstances],
                            previousPageCursor: null, nextPageCursor: null
                        });
                    });
                }
            }

            if (url.includes('marketplace-sales/v1/item/') && url.includes('/resellers')) {
                const collectibleItemId = url.match(/marketplace-sales\/v1\/item\/([^/]+)\/resellers/)?.[1];
                if (collectibleItemId) {
                    patchXHR(xhr, function () {
                        if (this.status !== 200) return;
                        const data = JSON.parse(this.responseText);
                        if (!data.data?.length) return;
                        const fakeInstanceIds = new Set(
                            config.inventory.limitedItems
                                .filter(i => i.collectibleItemId === collectibleItemId)
                                .map(i => i.collectibleItemInstanceId)
                        );
                        if (!fakeInstanceIds.size) return;
                        data.data = data.data.filter(entry => !fakeInstanceIds.has(entry.collectibleItemInstanceId));
                        overrideXHRResponse(this, data);
                    });
                }
            }

            if (url.includes('marketplace-sales/v1/item/') && url.includes('/resale-data')) {
                const collectibleItemId = url.match(/marketplace-sales\/v1\/item\/([^/]+)\/resale-data/)?.[1];
                if (collectibleItemId) {
                    patchXHR(xhr, function () {
                        if (this.status !== 200) return;
                        const fakePoints = config.resaleData[collectibleItemId] ?? [];
                        if (!fakePoints.length) return;
                        const data = JSON.parse(this.responseText);
                        data.priceDataPoints = [...fakePoints, ...(data.priceDataPoints ?? [])];
                        if (data.priceDataPoints.length) {
                            const sum = data.priceDataPoints.reduce((acc, p) => acc + p.value, 0);
                            data.recentAveragePrice = Math.round(sum / data.priceDataPoints.length);
                        }
                        overrideXHRResponse(this, data);
                    });
                }
            }
        }

        if (url?.includes('avatar.roblox.com')) {
            if (method === 'POST' && url.includes('/v2/avatar/set-wearing-assets')) {
                config.avatar.outfit = JSON.parse(body);
                saveAvatarData();
                const currentUserId = getCurrentUserId();
                setTimeout(async () => {
                    try {
                        const r = await fetch(`https://avatar.roblox.com/v2/avatar/users/${currentUserId}/avatar`, { credentials: 'include' });
                        if (r.ok) {
                            const d = await r.json();
                            if (d.bodyColor3s && d.scales && d.playerAvatarType) {
                                config.avatar.avatarData = { bodyColor3s: d.bodyColor3s, scales: d.scales, playerAvatarType: d.playerAvatarType };
                                saveAvatarData();
                            }
                        }
                        await renderAvatarThumbnail();
                    } catch { }
                }, 1500);
                patchXHR(xhr, function () { overrideXHRResponse(this, { invalidAssets: [], invalidAssetIds: [], success: true }); });
            }

            if (url.includes('/v1/avatar-inventory')) {
                const currentUserId = getCurrentUserId();
                patchXHR(xhr, async function () {
                    if (this.status !== 200) return;
                    if (!config.inventory.purchasedItems.length && !config.inventory.limitedItems.length && !config.inventory.useRealInventory) return;

                    const data = JSON.parse(this.responseText);
                    data.avatarInventoryItems ??= [];

                    const urlObj = new URL(url, 'https://avatar.roblox.com');
                    let filterSubType = null, filterType = null;
                    for (const [k, v] of urlObj.searchParams) {
                        if (k.includes('ItemSubType')) filterSubType = parseInt(v);
                        if (k.includes('ItemType')) filterType = v === 'Asset' || v === '1' ? 1 : v === 'Bundle' || v === '2' ? 2 : null;
                    }

                    let itemsToAdd = [];
                    const fromLocalStorage = () => {
                        for (const item of config.inventory.purchasedItems) {
                            const isBundle = item.itemCategory?.itemType === 2;
                            if (isBundle) {
                                if (filterType === null || filterType === 2) {
                                    if (filterSubType === null || item.itemCategory?.itemSubType === filterSubType) itemsToAdd.push(item);
                                }
                                if ((filterType === null || filterType === 1) && item.bundledItems) {
                                    for (const bi of item.bundledItems) {
                                        if (bi.type !== 'Asset') continue;
                                        if (filterSubType !== null && bi.assetType !== filterSubType) continue;
                                        itemsToAdd.push({ itemId: bi.id, itemName: bi.name, availabilityStatus: 'Available',
                                            acquisitionTime: item.acquisitionTime || new Date().toISOString(),
                                            itemCategory: { itemType: 1, itemSubType: bi.assetType } });
                                    }
                                }
                            } else {
                                if ((filterSubType === null || item.itemCategory?.itemSubType === filterSubType) &&
                                    (filterType === null || item.itemCategory?.itemType === filterType)) {
                                    itemsToAdd.push(item);
                                }
                            }
                        }
                        for (const item of config.inventory.limitedItems) {
                            if (filterType !== null && filterType !== 1) continue;
                            if (filterSubType !== null && item.assetType !== filterSubType) continue;
                            itemsToAdd.push({
                                itemId: parseInt(item.assetId), itemName: item.itemName,
                                availabilityStatus: 'Available', acquisitionTime: new Date().toISOString(),
                                itemCategory: { itemType: 1, itemSubType: item.assetType ?? null }
                            });
                        }
                    };

                    if (config.inventory.useRealInventory && filterSubType !== null) {
                        try {
                            const r = await fetch(`https://inventory.roblox.com/v2/users/${currentUserId}/inventory/${filterSubType}?cursor=&limit=100&sortOrder=Desc`, { credentials: 'include' });
                            if (!r.ok) throw new Error(`HTTP ${r.status}`);
                            const inv = await r.json();
                            if (inv.data) itemsToAdd = inv.data.map(i => ({
                                itemId: i.assetId, itemName: i.assetName, availabilityStatus: 'Available',
                                acquisitionTime: i.created, itemCategory: { itemType: 1, itemSubType: filterSubType }
                            }));
                        } catch { fromLocalStorage(); }
                    } else {
                        fromLocalStorage();
                    }

                    data.avatarInventoryItems = [...itemsToAdd, ...data.avatarInventoryItems];
                    overrideXHRResponse(this, data);
                });
            }

            if (url.includes('/v2/avatar/avatar')) {
                const currentUserId = getCurrentUserId();
                patchXHR(xhr, async function () {
                    if (this.status !== 200) return;
                    const r = await fetch(`https://avatar.roblox.com/v2/avatar/users/${currentUserId}/avatar`, { credentials: 'include' });
                    if (!r.ok) return;
                    const spoofed = await r.json();
                    const data    = JSON.parse(this.responseText);
                    if (data.bodyColor3s && data.scales && data.playerAvatarType) {
                        config.avatar.avatarData = { bodyColor3s: spoofed.bodyColor3s, scales: spoofed.scales, playerAvatarType: spoofed.playerAvatarType };
                        saveAvatarData();
                    }
                    if (!config.avatar.outfit) { config.avatar.outfit = { assets: spoofed.assets }; saveAvatarData(); }
                    if (config.avatar.outfit?.assets) data.assets = config.avatar.outfit.assets;
                    overrideXHRResponse(this, data);
                });
            }
        }

        if (url?.includes('thumbnails.roblox.com/v1/users/avatar-3d')) {
            const reqUserId    = url.match(/userId=(\d+)/)?.[1];
            const currentUserId = getCurrentUserId();
            if (reqUserId && currentUserId && reqUserId === currentUserId && config.avatar.customThumbnail3d) {
                patchXHR(xhr, async function () {
                    if (this.readyState !== 4) return;
                    const startWait = Date.now();
                    while (isRenderingInProgress && (Date.now() - startWait) < MAX_RENDER_WAIT_MS)
                        await new Promise(r => setTimeout(r, 500));
                    const base = config.avatar.customThumbnail3d;
                    const bust = base + (base.includes('?') ? '&' : '?') + 'spooferBust=' + Date.now();
                    overrideXHRResponse(this, { targetId: parseInt(reqUserId), state: 'Completed', imageUrl: bust, version: 'TN3' });
                });
            }
        }

        if (url?.includes('friends.roblox.com')) {
            const currentUserId = getCurrentUserId();
            if (url.includes('/v1/user/friend-requests/count')) {
                patchXHR(xhr, function () {
                    if (!config.enabled) return;
                    const fake = getFakeFriendRequests();
                    if (!fake.length) return;
                    let base = 0;
                    try { base = JSON.parse(this.responseText)?.count || 0; } catch { }
                    overrideXHRResponse(this, { count: base + fake.length });
                });
            }
            // ── FRIEND REQUESTS LIST ─────────────────────────────────────────
            if (url.includes('/v1/my/friends/requests')) {
                patchXHR(xhr, function () {
                    if (!config.enabled) return;
                    const fake = getFakeFriendRequests();
                    if (!fake.length) return;
                    let realData = { previousPageCursor: null, nextPageCursor: null, data: [] };
                    try { realData = JSON.parse(this.responseText); } catch { }
                    const fakeEntries = fake.map(r => ({
                        friendRequest: {
                            sentAt: r.sentAt,
                            senderId: parseInt(r.id),
                            sourceUniverseId: null,
                            originSourceType: 'PlayerSearch',
                            contactName: null,
                            senderNickname: ''
                        },
                        mutualFriendsList: [],
                        id: parseInt(r.id)
                    }));
                    const fakeIds = new Set(fake.map(r => parseInt(r.id)));
                    const filteredReal = Array.isArray(realData.data)
                        ? realData.data.filter(entry => !fakeIds.has(entry.id))
                        : [];
                    realData.data = [...fakeEntries, ...filteredReal];
                    overrideXHRResponse(this, realData);
                });
            }
            // ─────────────────────────────────────────────────────────────────
            if (url.includes(`/v1/users/${currentUserId}`) && url.includes('userSort=1')) {
                patchXHR(xhr, async function () {
                    if (this.status !== 403) return;
                    const r = await fetch(`https://friends.roblox.com/v1/users/${getCurrentUserId()}/friends/find?limit=18&cursor=&userSort=`, { credentials: 'include' });
                    if (r.ok) overrideXHRResponse(this, await r.json());
                });
            }
            if (url.includes('friends/online')) {
                patchXHR(xhr, function () {
                    if (this.status === 400) overrideXHRResponse(this, { data: [] });
                });
            }
        }

        if (url?.includes('trades.roblox.com')) {
            if (url.includes('/tradableitems')) {
                const currentUserId = getCurrentUserId();
                const urlUserId = url.match(/\/users\/(\d+)\/tradableitems/)?.[1];
                if (urlUserId && (urlUserId === currentUserId || urlUserId === config.spoofUserId)) {
                    patchXHR(xhr, function () {
                        if (this.status !== 200 || !config.inventory.limitedItems.length) return;
                        const data = JSON.parse(this.responseText);
                        const acc = {};
                        for (const limited of config.inventory.limitedItems) {
                            const cid = limited.collectibleItemId;
                            if (!acc[cid]) {
                                acc[cid] = {
                                    collectibleItemId: cid,
                                    itemTarget: { itemType: 'Asset', targetId: limited.assetId },
                                    itemName: limited.itemName, originalPrice: null,
                                    recentAveragePrice: limited.recentAveragePrice,
                                    assetStock: limited.assetStock, instances: []
                                };
                            }
                            acc[cid].instances.push({
                                collectibleItemInstanceId: limited.collectibleItemInstanceId,
                                itemTarget: { itemType: 'Asset', targetId: limited.assetId },
                                itemName: limited.itemName, serialNumber: limited.serialNumber ?? null,
                                originalPrice: null, recentAveragePrice: limited.recentAveragePrice,
                                assetStock: limited.assetStock, isOnHold: false
                            });
                        }
                        data.items = [...Object.values(acc), ...(data.items ?? [])];
                        data.userId = parseInt(currentUserId);
                        overrideXHRResponse(this, data);
                    });
                }
            }
            if (url.includes('/can-trade-with')) {
                patchXHR(xhr, function () { overrideXHRResponse(this, { canTrade: true, status: 'CanTrade' }, 200); });
            }
        }

        if (url?.includes('economy.roblox.com')) {
            if (config.robux.enabled && url.includes('/v1/users/') && url.includes('/currency')) {
                patchXHR(xhr, function () { overrideXHRResponse(this, { robux: config.robux.amount }); });
            }
        }

        if (url?.includes('transaction-records/v1/users/')) {
            if (url.includes('/transaction-types')) {
                patchXHR(xhr, function () {
                    const t = config.transactions;
                    const uid = config.spoofUserId || getCurrentUserId();
                    const hasFakePurchases = uid ? GM_getValue(`roblox_spoofer_user_${uid}_purchaseTransactions`, []).length > 0 : false;
                    const hasFakeRobuxPurchases = uid ? GM_getValue(`roblox_spoofer_user_${uid}_robuxPurchases`, config.robuxPurchases).length > 0 : config.robuxPurchases.length > 0;
                    overrideXHRResponse(this, {
                        HasPurchase: t.purchasesTotal > 0 || hasFakePurchases, HasSale: t.salesTotal > 0,
                        HasAffiliatePayout: t.affiliatePayoutTotal > 0, HasAffiliateSale: t.affiliateSalesTotal > 0,
                        HasGroupPayout: t.groupPayoutsTotal > 0, HasCurrencyPurchase: t.currencyPurchasesTotal > 0 || hasFakeRobuxPurchases,
                        HasTradeRobux: t.tradeSystemEarningsTotal > 0 || t.tradeSystemCostsTotal > 0,
                        HasPremiumStipend: t.premiumStipendsTotal > 0, HasEngagementPayout: t.premiumPayoutsTotal > 0,
                        HasGroupEngagementPayout: t.groupPremiumPayoutsTotal > 0,
                        HasAdSpend: t.adSpendTotal > 0, HasDevEx: t.developerExchangeTotal > 0,
                        HasPendingRobux: t.pendingRobuxTotal > 0, HasIndividualToGroup: t.individualToGroupTotal > 0,
                        HasCSAdjustment: t.csAdjustmentTotal > 0, HasAdsRevsharePayout: t.adsRevsharePayoutsTotal > 0,
                        HasGroupAdsRevsharePayout: t.groupAdsRevsharePayoutsTotal > 0,
                        HasSubscriptionsRevsharePayout: t.subscriptionsRevshareTotal > 0,
                        HasGroupSubscriptionsRevsharePayout: t.groupSubscriptionsRevshareTotal > 0,
                        HasPublishingAdvanceRebates: t.publishingAdvanceRebatesTotal > 0,
                        HasLicensingPayment: t.licensingPaymentTotal > 0
                    });
                });
            }
            if (url.includes('/transaction-totals')) {
                patchXHR(xhr, function () { overrideXHRResponse(this, config.transactions); });
            }
            if (url.includes('/transactions') && url.includes('transactionType=Purchase') && url.includes('PaidAndLimited')) {
                patchXHR(xhr, function () {
                    const uid = config.spoofUserId || getCurrentUserId();
                    const userTxns = uid ? GM_getValue(`roblox_spoofer_user_${uid}_purchaseTransactions`, []) : [];
                    overrideXHRResponse(this, { previousPageCursor: null, nextPageCursor: null, data: userTxns }, 200);
                });
            }
            if (url.includes('/transactions') && url.includes('transactionType=CurrencyPurchase')) {
                patchXHR(xhr, function () {
                    const uid = config.spoofUserId || getCurrentUserId();
                    const purchases = uid ? GM_getValue(`roblox_spoofer_user_${uid}_robuxPurchases`, config.robuxPurchases) : config.robuxPurchases;
                    overrideXHRResponse(this, { previousPageCursor: null, nextPageCursor: null, data: purchases }, 200);
                });
            }
        }

        if (url?.includes('catalog.roblox.com')) {
            if (url.includes('/v1/catalog/items/') && url.includes('/details')) {
                patchXHR(xhr, async function () {
                    if (this.status !== 200) return;
                    const data   = JSON.parse(this.responseText);
                    const itemId = data.id;
                    let modified = false;

                    if (itemId === 201 && config.robux.buyableHeadless) {
                        data.isPurchasable = true; data.isOffSale = false;
                        delete data.priceStatus; modified = true;
                    }

                    const ownedLimiteds = data.collectibleItemId
                        ? config.inventory.limitedItems.filter(i => i.collectibleItemId === data.collectibleItemId)
                        : [];
                    const owned = config.inventory.purchasedItems.some(i => i.itemId === itemId) || ownedLimiteds.length > 0;

                    if (owned) {
                        data.owned = true;
                        if (data.bundledItems?.length) {
                            data.bundledItems = data.bundledItems.map(bi => ({
                                ...bi,
                                owned: config.inventory.purchasedItems.some(i => i.itemId === bi.id) ||
                                       config.inventory.purchasedItems.some(i => i.itemCategory?.itemType === 2 && i.bundledItems?.some(b => b.id === bi.id))
                            }));
                        }
                        modified = true;
                    }

                    if (ownedLimiteds.length > 0 && data.collectibleItemId) {
                        try {
                            const r = await originalFetch(
                                `https://apis.roblox.com/marketplace-sales/v1/item/${data.collectibleItemId}/resellers?cursor=&limit=100`,
                                { credentials: 'include' }
                            );
                            if (r.ok) {
                                const resellData = await r.json();
                                const ownedInstanceIds = new Set(ownedLimiteds.map(i => i.collectibleItemInstanceId));
                                const otherListings = (resellData.data ?? []).filter(l => !ownedInstanceIds.has(l.collectibleItemInstanceId));
                                const cheapest = otherListings.length ? otherListings.reduce((a, b) => a.price < b.price ? a : b) : null;
                                data.lowestPrice       = cheapest?.price ?? data.lowestPrice;
                                data.lowestResalePrice = cheapest?.price ?? data.lowestResalePrice;
                                data.expectedSellerId  = cheapest?.seller?.sellerId ?? data.expectedSellerId;
                                data.hasResellers      = otherListings.length > 0;
                                data.isPurchasable     = otherListings.length > 0;
                                data.isOffSale         = otherListings.length === 0;
                                modified = true;
                            }
                        } catch { }
                    }

                    if (modified) overrideXHRResponse(this, data);
                });
            }
        }

        if (!config.enabled) return originalSend.call(this, body);

        if (url?.includes('users.roblox.com')) {
            if (url.includes('/v1/description')) {
                patchXHR(xhr, function () {
                    if (this.status !== 200) return;
                    overrideXHRResponse(this, { description: config.profile.description || '' });
                });
            }

            if (url.includes('/v1/users')) {
                patchXHR(xhr, function () {
                    if (this.status !== 200) return;
                    const data = JSON.parse(this.responseText);
                    if (!Array.isArray(data.data)) return;

                    const spoofedUsers = getUsersList();
                    const realId  = getCurrentUserId();
                    const spoofId = config.spoofUserId;
                    let modified  = false;

                    let realIdx = -1, spoofIdx = -1;
                    data.data.forEach((u, i) => {
                        if (realId  && u.id.toString() === realId)  realIdx  = i;
                        if (spoofId && u.id.toString() === spoofId) spoofIdx = i;
                    });

                    if (realIdx !== -1 && spoofIdx !== -1 && realIdx !== spoofIdx) {
                        [data.data[realIdx], data.data[spoofIdx]] = [data.data[spoofIdx], data.data[realIdx]];
                        modified = true;
                    }
                    if (spoofId && realIdx !== -1 && config.fetchedData) {
                        const entry = data.data[realIdx];
                        entry.id               = parseInt(spoofId);
                        entry.name             = config.profile.name || config.fetchedData.name;
                        entry.displayName      = config.profile.displayName || config.fetchedData.displayName;
                        entry.hasVerifiedBadge = config.profile.isVerified;
                        modified = true;
                    }

                    const existingIds = new Set(data.data.map(u => u.id.toString()));
                    for (const u of spoofedUsers) {
                        if (!existingIds.has(u.id)) {
                            data.data.push({ hasVerifiedBadge: u.hasVerifiedBadge, id: parseInt(u.id), name: u.name, displayName: u.displayName });
                            modified = true;
                        }
                    }

                    if (modified) overrideXHRResponse(this, data);
                });
            }
        }

        if (url?.includes('/my/settings/json')) {
            patchXHR(xhr, function () {
                if (this.status !== 200) return;
                const data    = JSON.parse(this.responseText);
                const profile = getEffectiveProfile();
                const tid     = getTargetUserId();
                if (!config.enabled || !tid || !profile) return;

                config.account.isPremium = config.profile.isPremium;
                const email = maskedEmail();

                Object.assign(data, {
                    Name: profile.name, DisplayName: profile.displayName,
                    UserId: parseInt(tid), IsAdmin: profile.isRobloxAdmin,
                    IsPremium: config.account.isPremium, CanTrade: config.account.canTrade,
                    IsEmailVerified: config.account.emailVerified, UserEmailVerified: config.account.emailVerified,
                    IsEmailOnFile: config.account.isEmailOnFile, IsPhoneFeatureEnabled: config.account.phoneFeatureEnabled,
                    AccountAgeInDays: config.account.accountAge, CanHideInventory: config.account.canHideInventory,
                    UserAbove13: config.account.userAbove13, HasValidPasswordSet: config.account.hasValidPasswordSet,
                    ChangeUsernameEnabled: config.account.changeUsernameEnabled,
                    RobuxRemainingForUsernameChange: config.account.robuxRemainingForUsernameChange,
                    UserEmail: email, UserEmailMasked: true, PreviousUserNames: ''
                });

                if (data.MyAccountSecurityModel) {
                    Object.assign(data.MyAccountSecurityModel, {
                        IsEmailSet: config.account.isEmailOnFile, IsEmailVerified: config.account.emailVerified,
                        IsTwoStepEnabled: config.account.twoStepEnabled
                    });
                    if (data.MyAccountSecurityModel.TwoStepVerificationViewModel) {
                        Object.assign(data.MyAccountSecurityModel.TwoStepVerificationViewModel, {
                            UserId: parseInt(tid), IsEnabled: config.account.twoStepEnabled
                        });
                    }
                }
                overrideXHRResponse(this, data);
            });
        }

        if (url?.includes('accountsettings.roblox.com')) {
            if (url.includes('/v1/emails')) {
                patchXHR(xhr, function () {
                    if (this.status !== 200 || !config.enabled) return;
                    const data  = JSON.parse(this.responseText);
                    const email = maskedEmail();
                    data.verifiedEmail = config.account.emailVerified ? email : null;
                    data.pendingEmail  = config.account.emailVerified ? null  : email;
                    overrideXHRResponse(this, data);
                });
            }
            if (url.includes('/v1/account/settings/account-country')) {
                patchXHR(xhr, function () {
                    if (this.status !== 200 || !config.enabled) return;
                    const data = JSON.parse(this.responseText);
                    const cn   = config.account.countryName || 'United States';
                    data.value = { countryName: cn, subdivisionIso: null, localizedSubdivision: null, localizedName: cn, countryId: 226 };
                    overrideXHRResponse(this, data);
                });
            }
        }

        if (url?.includes('apis.roblox.com')) {
            if (url.includes('profile-platform-api/v1/profiles/get')) {
                const currentUserId = getCurrentUserId();
                patchXHR(xhr, async function () {
                    if (this.status !== 200) return;
                    const data    = JSON.parse(this.responseText);
                    const profile = getEffectiveProfile();
                    const tid     = getTargetUserId();

                    if (tid && data.profileId === tid) {
                        if (data.components?.Actions) {
                            data.components.Actions.buttons    = [{ type: 'EditProfile' }, { type: 'EditAvatar' }, { type: 'QrCode' }];
                            data.components.Actions.contextual = ['ViewInventory', 'ViewFavorites'];
                        }
                        const hdr = data.components?.UserProfileHeader;
                        if (hdr) {
                            hdr.isPremium = profile.isPremium; hdr.isVerified = profile.isVerified;
                            hdr.isRobloxAdmin = profile.isRobloxAdmin;
                            hdr.names.displayName = profile.displayName;
                            hdr.names.primaryName = profile.displayName;
                            hdr.names.username    = profile.name;
                            if (profile.friendsCount > 0)    { hdr.counts.friendsCount    = profile.friendsCount;    hdr.counts.isFriendsCountEnabled    = true; }
                            if (profile.followersCount > 0)  { hdr.counts.followersCount  = profile.followersCount;  hdr.counts.isFollowersCountEnabled  = true; }
                            if (profile.followingsCount > 0) { hdr.counts.followingsCount = profile.followingsCount; hdr.counts.isFollowingsCountEnabled = true; }
                            if (hdr.contextualInformation) hdr.contextualInformation.context = 'Presence';
                        }
                        if (data.components?.About) {
                            data.components.About.description  = profile.description;
                            data.components.About.joinDateTime = profile.created;
                        }
                        if (data.components?.Statistics) data.components.Statistics.userJoinedDate = profile.created;
                    } else {
                        try {
                            const r = await fetch(`https://friends.roblox.com/v1/users/${currentUserId}/friends?limit=100`, { credentials: 'include' });
                            if (r.ok) {
                                const { data: friends } = await r.json();
                                if (friends.some(f => f.id === Number(data.profileId))) {
                                    const contextual = ['EditAlias', 'FollowUser', 'Unfriend'];
                                    if (data.components?.UserProfileHeader?.isPremium) contextual.push('TradeItems');
                                    contextual.push('ViewInventory', 'ViewFavorites', 'Block', 'Report');
                                    data.components.Actions.contextual = contextual;
                                    data.components.Actions.buttons = [{ type: 'Chat' }];
                                } else {
                                    const isFakeRequester = getFakeFriendRequests().some(r => r.id === data.profileId?.toString());
                                    if (isFakeRequester) {
                                        data.components.Actions.buttons = [{ type: 'AcceptFriendRequest' }];
                                    }
                                }
                            }
                        } catch { }
                    }
                    overrideXHRResponse(this, data);
                });
            }
        }

        if (url?.includes('premiumfeatures.roblox.com/v1/users/') && url?.includes('/subscriptions')) {
            if (config.profile.isPremium && config.premium?.createdAt) {
                patchXHR(xhr, function () {
                    const product = PREMIUM_PRODUCTS[config.premium.subscriptionType] || PREMIUM_PRODUCTS.RobloxPremium2200;
                    const created = new Date(config.premium.createdAt);
                    const renewal = new Date(created); renewal.setMonth(renewal.getMonth() + 1);
                    overrideXHRResponse(this, {
                        subscriptionProductModel: {
                            premiumFeatureId:     product.premiumFeatureId,
                            subscriptionTypeName: config.premium.subscriptionType,
                            robuxStipendAmount:   product.robuxStipendAmount,
                            isLifetime: true, expiration: renewal.toISOString(), renewal: renewal.toISOString(),
                            renewedSince: config.premium.renewedSince, created: config.premium.createdAt,
                            purchasePlatform: 'isDesktop', subscriptionName: product.subscriptionName
                        }
                    }, 200);
                });
            }
        }

        return originalSend.call(this, body);
    };

    const PREMIUM_PRODUCTS = {
        RobloxPremium450:  { premiumFeatureId: 505, robuxStipendAmount: 450,  subscriptionName: 'Roblox Premium 450'  },
        RobloxPremium1000: { premiumFeatureId: 506, robuxStipendAmount: 1000, subscriptionName: 'Roblox Premium 1000' },
        RobloxPremium2200: { premiumFeatureId: 507, robuxStipendAmount: 2200, subscriptionName: 'Roblox Premium 2200' },
    };

    const savePremium = () => {
        saveConfig();
        const uid = config.spoofUserId || getCurrentUserId();
        if (uid) savePerUserData(uid, 'premium', config.premium);
    };

    let gui = null;
    let currentTab = 'main';
    let inventoryFolder = null;
    let usersFolder = null;

    function refreshInventoryList() {
        if (!inventoryFolder) return;
        inventoryFolder.children.slice().forEach(c => {
            if (c._title?.startsWith('Item:') || c._title?.startsWith('Bundle:') || c._title?.startsWith('Limited:')) c.destroy();
        });
        config.inventory.purchasedItems.forEach(item => {
            const isBundle = item.itemCategory?.itemType === 2;
            const folder   = inventoryFolder.addFolder(`${isBundle ? 'Bundle' : 'Item'}: ${item.itemName || item.itemId}`);
            folder.close();
            if (isBundle && item.bundledItems?.length) {
                const sub = folder.addFolder(`Contents (${item.bundledItems.length})`);
                sub.close();
                item.bundledItems.slice(0, 5).forEach((bi, idx) => sub.add({ name: `${idx + 1}. ${bi.name || bi.id}` }, 'name').name('').disable());
                if (item.bundledItems.length > 5) sub.add({ more: `... and ${item.bundledItems.length - 5} more` }, 'more').name('').disable();
            }
            folder.add({ remove: () => removeItemFromInventory(item.itemId, item.itemCategory?.itemType || 1) }, 'remove').name('Remove');
            folder.add({ view: () => window.open(`https://www.roblox.com/${isBundle ? 'bundles' : 'catalog'}/${item.itemId}`, '_blank') }, 'view').name('View on Roblox');
        });
        config.inventory.limitedItems.forEach(item => {
            const folder = inventoryFolder.addFolder(`Limited: ${item.itemName || item.collectibleItemId}`);
            folder.close();
            folder.add({ price: item.expectedPrice ?? 0 }, 'price').name('Bought For').disable();
            folder.add({ rap: item.recentAveragePrice ?? 0 }, 'rap').name('RAP').disable();
            folder.add({ id: item.collectibleItemInstanceId }, 'id').name('Instance ID').disable();
            folder.add({ remove: () => {
                const idx = config.inventory.limitedItems.findIndex(i => i.collectibleItemInstanceId === item.collectibleItemInstanceId);
                if (idx !== -1) { config.inventory.limitedItems.splice(idx, 1); saveLimitedItems(); switchTab('inventory'); }
            }}, 'remove').name('Remove');
            folder.add({ view: () => window.open(`https://www.roblox.com/catalog/${item.assetId}/`, '_blank') }, 'view').name('View on Roblox');
        });
    }

    function refreshUsersList() {
        if (!usersFolder) return;
        usersFolder.children.slice().forEach(c => { if (c._title?.startsWith('User:')) c.destroy(); });
        getUsersList().forEach(user => {
            const f = usersFolder.addFolder(`User: ${user.displayName} (@${user.name})`);
            f.close();
            f.add({ id: user.id }, 'id').name('ID').disable();
            f.add({ verified: user.hasVerifiedBadge ? 'Yes' : 'No' }, 'verified').name('Verified').disable();
            f.add({ remove: () => removeSpoofedUser(user.id) }, 'remove').name('Remove');
            f.add({ view: () => window.open(`https://www.roblox.com/users/${user.id}/profile`, '_blank') }, 'view').name('View Profile');
        });
    }

    function saveFolderStates(container, prefix = '') {
        const states = {};
        for (const f of (container.folders || [])) {
            const key = prefix + f._title;
            states[key] = !f._closed;
            Object.assign(states, saveFolderStates(f, key + '/'));
        }
        return states;
    }

    function restoreFolderStates(container, states, prefix = '') {
        for (const f of (container.folders || [])) {
            const key = prefix + f._title;
            if (states[key] === true) f.open();
            else if (states[key] === false) f.close();
            restoreFolderStates(f, states, key + '/');
        }
    }

    let _pendingFolderStates = null;

    function switchTab(tabName) {
        currentTab = tabName;
        GM_setValue('roblox_spoofer_current_tab', tabName);
        if (gui) {
            _pendingFolderStates = saveFolderStates(gui);
            gui.destroy();
            createUI();
        }
    }

    let _bgElement = null;

    function applyBackground() {
        if (_bgElement) { _bgElement.remove(); _bgElement = null; }
        const existing = document.getElementById('spoofer-bg');
        if (existing) existing.remove();

        if (!config.uiSettings.backgroundEnabled || !config.uiSettings.backgroundUrl) return;

        const _doApply = () => {
            if (_bgElement) { _bgElement.remove(); _bgElement = null; }
            document.getElementById('spoofer-bg')?.remove();

            const el = document.createElement('div');
            el.id = 'spoofer-bg';
            const opacity = config.uiSettings.backgroundOpacity ?? 0.18;
            Object.assign(el.style, {
                position:           'fixed',
                top:                '0',
                left:               '0',
                width:              '100%',
                height:             '100%',
                zIndex:             '2147483646',
                pointerEvents:      'none',
                backgroundImage:    `url("${config.uiSettings.backgroundUrl.replace(/"/g, '%22')}")`,
                backgroundSize:     'cover',
                backgroundPosition: 'center',
                backgroundRepeat:   'no-repeat',
                opacity:            String(opacity),
                transform:          'translateZ(0)',
                willChange:         'opacity',
            });

            const target = document.body || document.documentElement;
            target.appendChild(el);
            _bgElement = el;
        };

        if (document.body) {
            _doApply();
        } else {
            document.addEventListener('DOMContentLoaded', _doApply, { once: true });
        }
    }

    const EXPORT_SKIP_KEYS = new Set([
        'roblox_spoofer_position', 'roblox_spoofer_ui_hidden',
        'roblox_spoofer_current_tab', 'x-csrf-token'
    ]);

    function exportData() {
        const dump = {};
        for (const k of GM_listValues()) {
            if (EXPORT_SKIP_KEYS.has(k)) continue;
            dump[k] = GM_getValue(k, null);
        }
        return btoa(unescape(encodeURIComponent(JSON.stringify(dump))));
    }

    function importData(b64) {
        let dump;
        try { dump = JSON.parse(decodeURIComponent(escape(atob(b64.trim())))); }
        catch { throw new Error('Invalid import string'); }
        for (const k of GM_listValues()) {
            if (!EXPORT_SKIP_KEYS.has(k)) GM_deleteValue(k);
        }
        for (const [k, v] of Object.entries(dump)) {
            if (!EXPORT_SKIP_KEYS.has(k)) GM_setValue(k, v);
        }
        const fresh = GM_getValue('roblox_spoofer_config', null);
        if (fresh) {
            Object.assign(config, fresh);
            for (const key of ['profile','robux','premium','usd','transactions','marketplace','inventory','avatar','users','account']) {
                config[key] = { ...defaultConfig[key], ...(config[key] || {}) };
            }
            if (!Array.isArray(config.purchaseTransactions)) config.purchaseTransactions = [];
            if (!Array.isArray(config.robuxPurchases)) config.robuxPurchases = [];
            if (!Array.isArray(config.fakeFriendRequests)) config.fakeFriendRequests = [];
            if (!config.uiSettings || typeof config.uiSettings !== 'object') config.uiSettings = defaultConfig.uiSettings;
            config.uiSettings = { ...defaultConfig.uiSettings, ...config.uiSettings };
        }
    }

    function refreshBackgroundStyle() {
        saveConfig();
        applyBackground();
    }

    let _drag = { active: false, ix: 0, iy: 0, ox: 0, oy: 0 };

    function _initDragListeners() {
        document.addEventListener('mousemove', e => {
            if (!_drag.active) return;
            e.preventDefault();
            _drag.ox = e.clientX - _drag.ix;
            _drag.oy = e.clientY - _drag.iy;
            if (gui) Object.assign(gui.domElement.style, { position: 'fixed', left: _drag.ox + 'px', top: _drag.oy + 'px', right: 'auto' });
        });
        document.addEventListener('mouseup', () => {
            if (_drag.active) { _drag.active = false; GM_setValue('roblox_spoofer_position', { x: _drag.ox, y: _drag.oy }); }
        });
    }
    _initDragListeners();

    function createUI() {
        if (typeof lil === 'undefined') { setTimeout(createUI, 100); return; }
        const savedTab = GM_getValue('roblox_spoofer_current_tab', null);
        if (savedTab) currentTab = savedTab;

        const TAB_MIGRATION = { robux: 'currency', trans: 'currency', account: 'identity' };
        if (TAB_MIGRATION[currentTab]) currentTab = TAB_MIGRATION[currentTab];

        try { gui = new lil.GUI({ title: 'roblox larp [silenthill10]', width: 420 }); }
        catch { setTimeout(createUI, 100); return; }

        gui.domElement.style.zIndex = '2147483647';
        if (GM_getValue('roblox_spoofer_ui_hidden', null) === true) gui.hide();

        const titleBar = gui.domElement.querySelector('.title');
        titleBar.style.cursor = 'move';

        const savedPos = GM_getValue('roblox_spoofer_position', null);
        if (savedPos) {
            Object.assign(gui.domElement.style, { position: 'fixed', left: savedPos.x + 'px', top: savedPos.y + 'px', right: 'auto' });
            _drag.ox = savedPos.x; _drag.oy = savedPos.y;
        }

        titleBar.addEventListener('mousedown', e => {
            if (!titleBar.contains(e.target)) return;
            _drag.ix = e.clientX - _drag.ox;
            _drag.iy = e.clientY - _drag.oy;
            _drag.active = true;
        });

        document.getElementById('spoofer-style')?.remove();
        const style = document.createElement('style');
        style.id = 'spoofer-style';
        style.textContent = `
            .spoofer-tabs { display:flex; flex-wrap:wrap; gap:4px; padding:8px; background:#111113; border-bottom:1px solid #2a2a2e; }
            .spoofer-tab { flex:1 1 calc(25% - 4px); min-width:80px; padding:6px 6px; background:#1c1c1f; border:1px solid #2e2e33; color:#888; cursor:pointer; font-size:9px; font-family:'JetBrains Mono',monospace; font-weight:500; letter-spacing:.04em; border-radius:6px; transition:background .18s,color .18s,border-color .18s; display:flex; flex-direction:row; align-items:center; justify-content:center; gap:5px; white-space:nowrap; line-height:1; }
            .spoofer-tab svg { display:block; width:12px; height:12px; flex-shrink:0; stroke:currentColor; fill:none; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
            .spoofer-tab span { overflow:hidden; text-overflow:ellipsis; }
            .spoofer-tab:hover { background:#242428; border-color:#3e3e45; color:#aaa; }
            .spoofer-tab.active { background:#2a2a2f; border-color:#555; color:#ddd; font-weight:700; }
            .lil-gui .controller.boolean input[type=checkbox] { accent-color:#999 !important; }
            .lil-gui .controller.function .name { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif; }
        `;
        document.head.appendChild(style);

        const lucide = {
            zap:      '<polyline points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
            user:     '<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
            coins:    '<circle cx="8" cy="8" r="6"/><path d="M18.09 10.37A6 6 0 1 1 10.34 18"/><path d="M7 6h1v4"/><path d="m16.71 13.88.7.71-2.82 2.82"/>',
            package:  '<path d="m16.5 9.4-9-5.19"/><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.29 7 12 12 20.71 7"/><line x1="12" y1="22" x2="12" y2="12"/>',
            sparkles: '<path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/>',
            shield:   '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
            users:    '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
            settings: '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>',
        };
        const makeSVG = p => `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">${p}</svg>`;

        const TABS = [
            { id: 'main',      label: 'Main',      icon: lucide.zap      },
            { id: 'profile',   label: 'Profile',   icon: lucide.user     },
            { id: 'currency',  label: 'Currency',  icon: lucide.coins    },
            { id: 'inventory', label: 'Inventory', icon: lucide.package  },
            { id: 'avatar',    label: 'Avatar',    icon: lucide.sparkles },
            { id: 'identity',  label: 'Identity',  icon: lucide.shield   },
            { id: 'users',     label: 'Users',     icon: lucide.users    },
            { id: 'settings',  label: 'Settings',  icon: lucide.settings },
        ];

        const tabContainer = document.createElement('div');
        tabContainer.className = 'spoofer-tabs';
        TABS.forEach(t => {
            const btn = document.createElement('button');
            btn.className = `spoofer-tab${currentTab === t.id ? ' active' : ''}`;
            btn.innerHTML = makeSVG(t.icon) + `<span>${t.label}</span>`;
            btn.onclick = () => switchTab(t.id);
            tabContainer.appendChild(btn);
        });
        gui.domElement.querySelector('.title').after(tabContainer);

        const saveAccount = () => {
            saveConfig();
            const uid = config.spoofUserId || getCurrentUserId();
            if (uid) savePerUserData(uid, 'account', config.account);
        };

        if (currentTab === 'main') {
            gui.add(config, 'enabled').name('Enable Spoofing').onChange(saveConfig);
            gui.add(config, 'spoofUserId').name('Spoof User ID').onChange(saveConfig);
            gui.add({ fetch: async () => {
                const uid = config.spoofUserId || getCurrentUserId();
                if (!uid) { alert('No user ID found.'); return; }
                await fetchUserData(uid); loadUserConfig(uid); switchTab('main');
            }}, 'fetch').name('Fetch & Load User Data');
            gui.add({ useCurrent: () => {
                config.spoofUserId = ''; saveConfig();
                const cid = getCurrentUserId(); if (cid) loadUserConfig(cid);
                switchTab('main');
            }}, 'useCurrent').name('Use Current User');
            const saveFolder = gui.addFolder('Save / Reset'); saveFolder.close();
            saveFolder.add({ save: () => { saveCurrentUserConfig(); alert('Per-user data saved!'); }}, 'save').name('Save Current User Data');

        } else if (currentTab === 'profile') {
            const badges = gui.addFolder('Badges'); badges.close();
            badges.add(config.profile, 'isPremium').name('Premium Badge').onChange(saveConfig);
            badges.add(config.profile, 'isVerified').name('Verified Badge').onChange(saveConfig);
            badges.add(config.profile, 'isRobloxAdmin').name('Admin Badge').onChange(saveConfig);

            const names = gui.addFolder('Identity'); names.close();
            names.add(config.profile, 'name').name('Username').onChange(saveConfig);
            names.add(config.profile, 'displayName').name('Display Name').onChange(saveConfig);
            names.add(config.profile, 'description').name('Description').onChange(saveConfig);
            names.add(config.profile, 'joinDate').name('Join Date').onChange(saveConfig);

            const social = gui.addFolder('Social Counts'); social.close();
            social.add(config.profile, 'friendsCount',    0, 999999999, 1).name('Friends').onChange(saveConfig);
            social.add(config.profile, 'followersCount',  0, 999999999, 1).name('Followers').onChange(saveConfig);
            social.add(config.profile, 'followingsCount', 0, 999999999, 1).name('Following').onChange(saveConfig);

            const existingFR = getFakeFriendRequests();
            const frFolder = gui.addFolder(`Friend Requests (${existingFR.length})`); frFolder.close();
            const frParams = { userId: '' };
            const frAddFolder = frFolder.addFolder('Add Request'); frAddFolder.close();
            frAddFolder.add(frParams, 'userId').name('User ID');
            frAddFolder.add({ add: async () => {
                const uid = frParams.userId.trim(); if (!uid) return;
                try { await addFakeFriendRequest(uid); frParams.userId = ''; switchTab('profile'); }
                catch (e) { alert(`Failed: ${e.message}`); }
            }}, 'add').name('Add');

            if (existingFR.length) {
                frFolder.add({ clearAll: () => {
                    if (!confirm('Clear all fake friend requests?')) return;
                    const uid = config.spoofUserId || _cachedUid;
                    const key = uid ? `roblox_spoofer_user_${uid}_fakeFriendRequests` : null;
                    if (key) GM_setValue(key, []);
                    else { config.fakeFriendRequests = []; saveConfig(); }
                    switchTab('profile');
                }}, 'clearAll').name('Clear All');
                existingFR.forEach((r, i) => {
                    const f = frFolder.addFolder(`${r.displayName} (@${r.name})`); f.close();
                    f.add({ id: r.id }, 'id').name('User ID').disable();
                    f.add({ sent: new Date(r.sentAt).toLocaleDateString() }, 'sent').name('Sent').disable();
                    f.add({ remove: () => { removeFakeFriendRequest(r.id); switchTab('profile'); }}, 'remove').name('Remove');
                    f.add({ view: () => window.open(`https://www.roblox.com/users/${r.id}/profile`, '_blank') }, 'view').name('View Profile');
                });
            }

        } else if (currentTab === 'currency') {
            const saveRobux = () => { saveConfig(); const uid = config.spoofUserId || getCurrentUserId(); if (uid) savePerUserData(uid, 'robux', config.robux); };
            const saveUsd   = () => { saveConfig(); const uid = config.spoofUserId || getCurrentUserId(); if (uid) savePerUserData(uid, 'usd', { enabled: config.usd.enabled, amount: config.usd.amount }); };
            const robux = gui.addFolder('Robux'); robux.close();
            robux.add(config.robux, 'enabled').name('Enable Robux Spoof').onChange(saveRobux);
            robux.add(config.robux, 'amount', 0, 999999999, 1).name('Robux Amount').onChange(saveRobux);
            robux.add(config.robux, 'buyableHeadless').name('Buyable Headless').onChange(saveRobux);
            robux.add(config.marketplace, 'freePurchases').name('Free Purchases').onChange(saveConfig);

            const usdFolder = gui.addFolder('Credit Balance'); usdFolder.close();
            usdFolder.add(config.usd, 'enabled').name('Enable Spoof').onChange(saveUsd);
            usdFolder.add(config.usd, 'amount', 0, 999999, 0.01).name('Amount').onChange(saveUsd);
            usdFolder.add(config.usd, 'currencyCode', ['USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY', 'BRL', 'MXN', 'INR', 'KRW']).name('Currency').onChange(saveUsd);

            const gcFolder = usdFolder.addFolder(`Gift Cards (${config.usd.giftCards.length})`); gcFolder.close();
            const gcParams = { code: '', value: 10.00 };
            const gcAddFolder = gcFolder.addFolder('Add Gift Card'); gcAddFolder.close();

            const generateCode = () => {
                const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
                const prefix = Math.random() < 0.5 ? 'RI' : 'RB';
                const bytes = crypto.getRandomValues(new Uint8Array(16));
                return prefix + Array.from(bytes, b => chars[b % chars.length]).join('');
            };

            const codeController = gcAddFolder.add(gcParams, 'code').name('Code');
            gcAddFolder.add(gcParams, 'value', 0.01, 500, 0.01).name('Value ($)');
            gcAddFolder.add({ generate: () => { gcParams.code = generateCode(); codeController.updateDisplay(); }}, 'generate').name('Generate Random Code');
            gcAddFolder.add({
                add: () => {
                    const code = gcParams.code.trim().toUpperCase();
                    if (!code) return;
                    if (config.usd.giftCards.some(c => c.code.toUpperCase() === code)) { alert('A gift card with that code already exists.'); return; }
                    config.usd.giftCards.push({ code, value: Math.round(gcParams.value * 100) / 100 });
                    saveConfig(); gcParams.code = ''; switchTab('currency');
                }
            }, 'add').name('Add Card');

            config.usd.giftCards.forEach((card, idx) => {
                const cf = gcFolder.addFolder(`${card.code}  —  $${parseFloat(card.value).toFixed(2)}`);
                cf.close();
                cf.add({ code: card.code }, 'code').name('Code').disable();
                cf.add({ value: `$${parseFloat(card.value).toFixed(2)}` }, 'value').name('Value').disable();
                cf.add({ remove: () => { config.usd.giftCards.splice(idx, 1); saveConfig(); switchTab('currency'); }}, 'remove').name('Remove');
            });

            const premFolder = gui.addFolder('Premium Subscription'); premFolder.close();
            premFolder.add(config.premium, 'subscriptionType', ['RobloxPremium450', 'RobloxPremium1000', 'RobloxPremium2200']).name('Plan').onChange(savePremium);
            premFolder.add({ status: config.premium.createdAt ? `Active since ${new Date(config.premium.createdAt).toLocaleDateString()}` : 'Not started' }, 'status').name('Status').disable();
            premFolder.add({
                start: () => {
                    const now = new Date();
                    const monthAgo = new Date(now); monthAgo.setMonth(monthAgo.getMonth() - 1);
                    config.premium.createdAt    = now.toISOString();
                    config.premium.renewedSince = monthAgo.toISOString();
                    savePremium(); switchTab('currency');
                }
            }, 'start').name(config.premium.createdAt ? 'Restart Subscription' : 'Start Subscription');
            if (config.premium.createdAt) {
                premFolder.add({
                    clear: () => { config.premium.createdAt = null; config.premium.renewedSince = null; savePremium(); switchTab('currency'); }
                }, 'clear').name('Clear Subscription');
            }

            const txLabel = k => k.replace(/Total$/, '').replace(/([A-Z])/g, ' $1').trim();
            const totals = gui.addFolder('Totals'); totals.close();
            const inc = totals.addFolder('Income'); inc.close();
            ['salesTotal','affiliateSalesTotal','groupPayoutsTotal','premiumStipendsTotal','premiumPayoutsTotal',
             'developerExchangeTotal','adsRevsharePayoutsTotal','groupAdsRevsharePayoutsTotal',
             'subscriptionsRevshareTotal','affiliatePayoutTotal','licensingPaymentTotal','publishingAdvanceRebatesTotal'
            ].forEach(k => inc.add(config.transactions, k, 0, 999999999, 1).name(txLabel(k)).onChange(saveConfig));

            const exp = totals.addFolder('Expenses'); exp.close();
            ['purchasesTotal','adSpendTotal','currencyPurchasesTotal','tradeSystemCostsTotal',
             'subscriptionsRevshareOutgoingTotal','licensingPaymentClawbackOutgoingTotal'
            ].forEach(k => exp.add(config.transactions, k, 0, 999999999, 1).name(txLabel(k)).onChange(saveConfig));

            const flow = totals.addFolder('Flow & Misc'); flow.close();
            ['pendingRobuxTotal','incomingRobuxTotal','outgoingRobuxTotal','tradeSystemEarningsTotal',
             'individualToGroupTotal','csAdjustmentTotal','groupPremiumPayoutsTotal',
             'groupSubscriptionsRevshareTotal','groupSubscriptionsRevshareOutgoingTotal'
            ].forEach(k => flow.add(config.transactions, k, 0, 999999999, 1).name(txLabel(k)).onChange(saveConfig));

            const rbxBuyFolder = gui.addFolder('Robux Purchase History'); rbxBuyFolder.close();
            const rbxParams = { amount: 800, date: new Date().toISOString().slice(0, 16) };
            const rbxAddFolder = rbxBuyFolder.addFolder('Add Purchase'); rbxAddFolder.close();
            rbxAddFolder.add(rbxParams, 'amount', 1, 99999, 1).name('Robux Amount');
            rbxAddFolder.add(rbxParams, 'date').name('Date (ISO, local)');
            rbxAddFolder.add({
                add: () => {
                    const amt = Math.round(rbxParams.amount);
                    if (!amt) return;
                    let isoDate;
                    try { isoDate = new Date(rbxParams.date).toISOString(); } catch { isoDate = new Date().toISOString(); }
                    addRobuxPurchase({ amount: amt, date: isoDate });
                    switchTab('currency');
                }
            }, 'add').name('Add Entry');

            const existingRbxPurchases = getRobuxPurchases();
            if (existingRbxPurchases.length) {
                rbxBuyFolder.add({ clearAll: () => {
                    if (!confirm('Clear all robux purchase history?')) return;
                    const uid = config.spoofUserId || _cachedUid;
                    const key = uid ? `roblox_spoofer_user_${uid}_robuxPurchases` : null;
                    if (key) GM_setValue(key, []);
                    else config.robuxPurchases = [];
                    switchTab('currency');
                }}, 'clearAll').name('Clear All');
                existingRbxPurchases.slice(0, 20).forEach((p, i) => {
                    const d = new Date(p.created).toLocaleDateString();
                    const f = rbxBuyFolder.addFolder(`+${p.currency.amount} Robux  —  ${d}`); f.close();
                    f.add({ amount: p.currency.amount }, 'amount').name('Amount').disable();
                    f.add({ date: p.created }, 'date').name('Date').disable();
                    f.add({ hash: p.idHash }, 'hash').name('ID Hash').disable();
                    f.add({ remove: () => {
                        const uid = config.spoofUserId || _cachedUid;
                        const key = uid ? `roblox_spoofer_user_${uid}_robuxPurchases` : null;
                        const arr = key ? GM_getValue(key, []) : config.robuxPurchases;
                        arr.splice(i, 1);
                        if (key) GM_setValue(key, arr); else saveConfig();
                        switchTab('currency');
                    }}, 'remove').name('Remove');
                });
                if (existingRbxPurchases.length > 20) {
                    rbxBuyFolder.add({ more: `... and ${existingRbxPurchases.length - 20} more` }, 'more').name('').disable();
                }
            }

        } else if (currentTab === 'inventory') {
            const addF = gui.addFolder('Add Item'); addF.close();
            const invP = { itemId: '', itemType: 'asset' };
            addF.add(invP, 'itemId').name('Item/Bundle ID');
            addF.add(invP, 'itemType', ['asset', 'bundle']).name('Type');
            addF.add({ add: () => {
                const id = invP.itemId.trim(); if (!id) return;
                addItemToInventory(id, invP.itemType); invP.itemId = ''; switchTab('inventory');
            }}, 'add').name('Add Item');
            gui.add({ clearAll: () => {
                if (!confirm('Clear all inventory?')) return;
                config.inventory.purchasedItems = []; config.inventory.limitedItems = [];
                saveInventory(); switchTab('inventory');
            }}, 'clearAll').name('Clear All Items');
            gui.add(config.inventory, 'useRealInventory').name('Use Real Inventory').onChange(saveConfig);

            const ac = config.inventory.purchasedItems.filter(i => i.itemCategory?.itemType === 1).length;
            const bc = config.inventory.purchasedItems.filter(i => i.itemCategory?.itemType === 2).length;
            const lc = config.inventory.limitedItems.length;
            inventoryFolder = gui.addFolder(`Items (${ac} assets, ${bc} bundles, ${lc} limiteds)`);
            inventoryFolder.close(); refreshInventoryList();

        } else if (currentTab === 'avatar') {
            const status = gui.addFolder('Status'); status.close();
            status.add({ outfit: config.avatar.outfit ? '✓ Captured' : '✗ Not captured' }, 'outfit').name('Outfit Status').disable();
            status.add({ avatarData: config.avatar.avatarData ? '✓ Captured' : '✗ Not captured' }, 'avatarData').name('Avatar Data').disable();
            status.add({ thumbnail: config.avatar.customThumbnail3d ? '✓ Generated' : '✗ Not generated' }, 'thumbnail').name('Thumbnail').disable();

            gui.add({ capture: async () => {
                const r = await fetch(`https://avatar.roblox.com/v2/avatar/users/${getTargetUserId()}/avatar`, { credentials: 'include' });
                if (!r.ok) throw new Error(`HTTP ${r.status}`);
                const d = await r.json();
                if (d.bodyColor3s && d.scales && d.playerAvatarType) {
                    config.avatar.avatarData = { bodyColor3s: d.bodyColor3s, scales: d.scales, playerAvatarType: d.playerAvatarType };
                    saveAvatarData(); switchTab('avatar');
                }
            }}, 'capture').name('Force Capture Avatar Data');
            gui.add({ render: async () => {
                if (!config.avatar.outfit || !config.avatar.avatarData) return;
                if (await renderAvatarThumbnail()) switchTab('avatar');
            }}, 'render').name('Render Thumbnail');
            gui.add({ clear: () => {
                if (!confirm('Clear all avatar data?')) return;
                config.avatar = { outfit: null, avatarData: null, customThumbnail3d: null, thumbnailGeneratedAt: null };
                saveAvatarData(); switchTab('avatar');
            }}, 'clear').name('Clear Data');

            if (config.avatar.outfit) {
                const od = gui.addFolder('Outfit Data (Debug)'); od.close();
                const ids = config.avatar.outfit.assetIds || config.avatar.outfit.assets?.map(a => a.id) || (Array.isArray(config.avatar.outfit) ? config.avatar.outfit : []);
                od.add({ count: ids.length }, 'count').name('Asset Count').disable();
                if (ids.length) od.add({ assets: ids.slice(0, 5).join(', ') + (ids.length > 5 ? '...' : '') }, 'assets').name('Assets (first 5)').disable();
                od.add({ view: () => alert('Outfit data logged to console (F12)') }, 'view').name('View in Console');
            }
            if (config.avatar.avatarData) {
                const ad = gui.addFolder('Avatar Data (Debug)'); ad.close();
                if (config.avatar.avatarData.playerAvatarType) ad.add({ type: config.avatar.avatarData.playerAvatarType }, 'type').name('Avatar Type').disable();
                if (config.avatar.avatarData.scales) ad.add({ height: config.avatar.avatarData.scales.height || 0 }, 'height').name('Height Scale').disable();
                ad.add({ view: () => alert('Avatar data logged to console (F12)') }, 'view').name('View in Console');
            }

        } else if (currentTab === 'identity') {
            const email = gui.addFolder('Email'); email.close();
            email.add(config.account, 'emailUsername').name('Email Username').onChange(v => { config.account.emailUsername = v.replace('@', ''); saveAccount(); });
            email.add(config.account, 'emailDomain').name('Email Domain').onChange(saveAccount);
            email.add(config.account, 'emailVerified').name('Email Verified').onChange(saveAccount);
            email.add(config.account, 'isEmailOnFile').name('Email On File').onChange(saveAccount);

            const loc = gui.addFolder('Location'); loc.close();
            loc.add(config.account, 'countryName').name('Country Name').onChange(saveAccount);

            const sec = gui.addFolder('Security'); sec.close();
            sec.add(config.account, 'twoStepEnabled').name('2-Step Auth').onChange(saveAccount);
            sec.add(config.account, 'phoneFeatureEnabled').name('Phone Feature').onChange(saveAccount);
            sec.add(config.account, 'hasValidPasswordSet').name('Valid Password').onChange(saveAccount);

            const perms = gui.addFolder('Permissions & Status'); perms.close();
            perms.add(config.account, 'canTrade').name('Can Trade').onChange(saveAccount);
            perms.add(config.account, 'isPremium').name('Premium').onChange(saveAccount);
            perms.add(config.account, 'canHideInventory').name('Can Hide Inventory').onChange(saveAccount);
            perms.add(config.account, 'userAbove13').name('User Above 13').onChange(saveAccount);
            perms.add(config.account, 'changeUsernameEnabled').name('Can Change Username').onChange(saveAccount);
            perms.add(config.account, 'robuxRemainingForUsernameChange', 0, 10000, 1).name('Robux for Username').onChange(saveAccount);
            perms.add(config.account, 'accountAge', 0, 10000, 1).name('Account Age (days)').onChange(saveAccount);

        } else if (currentTab === 'users') {
            const addUF = gui.addFolder('Add User'); addUF.close();
            const uP = { userId: '' };
            addUF.add(uP, 'userId').name('User ID');
            addUF.add({ add: async () => {
                const uid = uP.userId.trim(); if (!uid) return;
                await addSpoofedUser(uid); uP.userId = ''; switchTab('users');
            }}, 'add').name('Add User');
            gui.add({ clearAll: () => {
                if (!confirm('Clear all spoofed users?')) return;
                saveUsersList([]); saveConfig(); switchTab('users');
            }}, 'clearAll').name('Clear All');

            const storedIds = getAllStoredUserIds();
            if (storedIds.length) {
                const sf = gui.addFolder(`Per-User Storage (${storedIds.length} users)`); sf.close();
                const usersList = getUsersList();
                storedIds.forEach(uid => {
                    const label = usersList.find(u => u.id === uid)?.displayName || uid;
                    const uf = sf.addFolder(`UID: ${label} (${uid})`); uf.close();
                    uf.add({ del: () => { if (confirm(`Delete data for UID ${uid}?`)) { deletePerUserData(uid); switchTab('users'); } }}, 'del').name('Delete Stored Data');
                });
            }

            usersFolder = gui.addFolder(`Spoofed Users (${getUsersList().length})`);
            usersFolder.close(); refreshUsersList();

        } else if (currentTab === 'settings') {
            const bgFolder = gui.addFolder('Background'); bgFolder.close();
            bgFolder.add(config.uiSettings, 'backgroundEnabled').name('Enable Background').onChange(refreshBackgroundStyle);
            bgFolder.add(config.uiSettings, 'backgroundUrl').name('Image URL').onChange(refreshBackgroundStyle);
            bgFolder.add(config.uiSettings, 'backgroundOpacity', 0.01, 1, 0.01).name('Opacity').onChange(refreshBackgroundStyle);
            bgFolder.add({ preview: () => {
                applyBackground();
            }}, 'preview').name('Preview');

            const exportFolder = gui.addFolder('Export / Import'); exportFolder.close();
            const exportParams = { data: '' };
            exportFolder.add({ doExport: () => {
                exportParams.data = exportData();
                exportController.updateDisplay();
            }}, 'doExport').name('Export Data');
            const exportController = exportFolder.add(exportParams, 'data').name('Base64 String');
            exportFolder.add({ doCopy: () => {
                if (!exportParams.data) { exportParams.data = exportData(); exportController.updateDisplay(); }
                navigator.clipboard.writeText(exportParams.data).catch(() => {});
            }}, 'doCopy').name('Copy to Clipboard');

            const importParams = { data: '' };
            const importFolder = exportFolder.addFolder('Import'); importFolder.close();
            importFolder.add(importParams, 'data').name('Paste Base64');
            importFolder.add({ doImport: () => {
                if (!importParams.data.trim()) return;
                try {
                    importData(importParams.data);
                    importParams.data = '';
                    alert('Import successful! Reloading UI...');
                    applyBackground();
                    switchTab('main');
                } catch (e) { alert(`Import failed: ${e.message}`); }
            }}, 'doImport').name('Import');

            const dangerFolder = gui.addFolder('Danger Zone'); dangerFolder.close();
            dangerFolder.add({ wipeAll: () => {
                if (!confirm('Wipe ALL spoofer data? This cannot be undone.')) return;
                for (const k of GM_listValues()) GM_deleteValue(k);
                location.reload();
            }}, 'wipeAll').name('Wipe All Data & Reload');
        }

        if (_pendingFolderStates) {
            restoreFolderStates(gui, _pendingFolderStates);
            _pendingFolderStates = null;
        }

        window.gui = gui;
    }

    document.addEventListener('keydown', e => {
        if (e.ctrlKey || e.altKey || e.shiftKey) return;
        const tag = document.activeElement?.tagName;
        if (tag === 'INPUT' || tag === 'TEXTAREA' || !gui) return;

        if (e.key === 'h') {
            e.preventDefault();
            if (gui._hidden) { gui.show(); GM_setValue('roblox_spoofer_ui_hidden', false); }
            else             { gui.hide(); GM_setValue('roblox_spoofer_ui_hidden', true); }
        }

        if (e.key === 'r') {
            e.preventDefault();
            GM_deleteValue('roblox_spoofer_position');
            for (const prop of ['position', 'left', 'top', 'right']) {
                gui.domElement.style.removeProperty(prop);
            }
        }
    });

    window.addEventListener('spooferReset', () => {
        for (const k of GM_listValues()) GM_deleteValue(k);
        console.log('[spoofer] all data wiped — reload the page');
    });

    const _resetHelper = document.createElement('script');
    _resetHelper.textContent = `function spooferReset() { dispatchEvent(new CustomEvent('spooferReset')); console.log('[spoofer] reset triggered'); }`;
    document.documentElement.appendChild(_resetHelper);
    _resetHelper.remove();

    async function init() {
        if (window.location.hostname.includes('rolimons.com')) {
            const spoofedId   = GM_getValue('spoofed_user_id',   null);
            const spoofedName = GM_getValue('spoofed_user_name', null);
            const isLogged    = document.cookie.split(';').some(c => c.trim().startsWith('_Roli'));
            let _name = null, _id = null;
            Object.defineProperty(unsafeWindow, 'jwt_player_name', {
                get() { return _name; }, set(v) { _name = (isLogged && spoofedName) ? spoofedName : v; }, configurable: true
            });
            Object.defineProperty(unsafeWindow, 'jwt_player_id', {
                get() { return _id; }, set(v) { _id = (isLogged && spoofedId) ? Number(spoofedId) : v; }, configurable: true
            });
            applyBackground();
            return;
        }

        if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', createUI);
        else createUI();

        const removeChat = () => document.getElementById('chat-container')?.remove();
        removeChat();
        new MutationObserver(mutations => {
            let checkChat = false;
            for (const m of mutations) {
                if (m.type !== 'childList') continue;
                for (const node of m.removedNodes) {
                    if (node === _cachedMeta) { _cachedMeta = null; break; }
                }
                for (const node of m.addedNodes) {
                    if (node.nodeType === 1) {
                        if (node.nodeName === 'META' || node.nodeName === 'HEAD') _cachedMeta = null;
                        if (node.id === 'chat-container' || node.querySelector?.('#chat-container')) checkChat = true;
                    }
                }
            }
            if (checkChat) removeChat();
        }).observe(document.documentElement, { childList: true, subtree: true });

        if (config.enabled && config.spoofUserId) {
            _cachedUid = config.spoofUserId;
            loadUserConfig(config.spoofUserId);
            if (!config.fetchedData) await fetchUserData(config.spoofUserId);
        } else {
            const tryLoadReal = () => {
                const id = getCurrentUserId();
                if (id) { _cachedUid = id; loadUserConfig(id); }
            };
            if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', tryLoadReal);
            else tryLoadReal();
        }
        if (config.enabled) { interceptMetaTag(); applySpoofing(); startTextReplacement(); }
        applyBackground();
    }

    init();
})();