Scrap2Mark

Comprehensive inventory management and market posting tool for Torn.com

// ==UserScript==
// @name         Scrap2Mark
// @namespace    http://tampermonkey.net/
// @version      1.3.1
// @description  Comprehensive inventory management and market posting tool for Torn.com
// @author       vALT0r [767373]
// @match        https://www.torn.com/item.php*
// @match        https://www.torn.com/page.php?sid=ItemMarket*
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

/*
 * CHANGELOG:
 * 
 * Version 1.3 (Latest):
 * ----------------------
 * NEW FEATURES:
 * • Added Full Reset button with confirmation to clear all stored data and settings
 * • Enhanced auto-fetch functionality - now runs when API key is saved and checks for missing data
 * • Improved user guidance with detailed error messages pointing to specific solutions
 * • Better API status display with item counts and helpful messaging
 * 
 * BUG FIXES:
 * • Fixed "No tradeable items found" issue when users don't have API data loaded
 * • Enhanced auto-fetch logic to run when data is missing (not just when timestamp is old)
 * • Added comprehensive debugging and logging for troubleshooting tradeable status issues
 * • Improved error handling for auto-fetch scenarios
 * 
 * IMPROVEMENTS:
 * • Added warning during scanning when no API key is present
 * • Enhanced console logging for better debugging of API data loading
 * • Better status messages that guide users to specific solutions
 * • More robust auto-fetch with multiple trigger conditions
 * 
 * Version 1.2:
 * ------------
 * • Smart "Hide below" filtering (only hides items with actual values)
 * • Hidden Fetch API button with auto-fetch implementation
 * • Enhanced button styling with gradients, shadows, and hover effects
 * • Consolidated popup windows for mobile-friendly design
 * • Button text improvements and better grouping
 * 
 * Version 1.1:
 * ------------
 * • Fixed debugMode errors and interface freezing issues
 * • Removed debug features and cleaned up codebase
 * • Added UI yielding to prevent interface freezing during bulk operations
 * • Enhanced ignore button styling and functionality
 */

(function() {
    'use strict';

    // ============================================================================
    // CONFIGURATION CONSTANTS
    // ============================================================================
    
    const SCRIPT_NAME = 'Scrap2Mark';
    
    // Storage keys
    const STORAGE_KEY = 'torn_inventory_data';
    const API_KEY_STORAGE_KEY = 'torn_api_key';
    const API_DATA_STORAGE_KEY = 'torn_api_items_data';
    const API_EXPIRY_STORAGE_KEY = 'torn_api_items_expiry';
    const API_KEY_EXPIRY_STORAGE_KEY = 'torn_api_key_expiry';
    const WINDOW_STATE_STORAGE_KEY = 'torn_scanner_window_state';
    const MARKET_DATA_STORAGE_KEY = 'torn_market_data';
    const MARKET_EXPIRY_STORAGE_KEY = 'torn_market_expiry';
    const IGNORED_ITEMS_STORAGE_KEY = 'scrap2mark_ignored_items';
    const FILTER_STATE_STORAGE_KEY = 'scrap2mark_filter_state';
    
    // Cache durations
    const API_CACHE_DURATION = 30 * 24 * 60 * 60 * 1000; // 30 days
    const API_KEY_EXPIRY_DURATION = 90 * 24 * 60 * 60 * 1000; // 90 days
    const MARKET_CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
    
    // Analysis settings
    const OUTLIER_THRESHOLD = 3.0; // Standard deviations for outlier detection
    
    // Window size configuration
    const WINDOW_MIN_WIDTH = '410px';
    const WINDOW_MIN_HEIGHT = '600px';
    const WINDOW_DEFAULT_WIDTH = '510px';
    const WINDOW_DEFAULT_HEIGHT = '600px';
    const WINDOW_MINIMIZED_WIDTH = '180px';
    const WINDOW_MINIMIZED_HEIGHT = '50px';
    
    // ============================================================================
    // GLOBAL STATE VARIABLES
    // ============================================================================
    
    // Core application state
    let isScanning = false;
    let scannedItems = new Map();
    let ignoredItems = new Set();
    let floatingTable = null;
    
    // API and data management
    let apiItemsData = null;
    let apiKey = null;
    let marketData = new Map();
    
    // API request management
    let apiRequestQueue = [];
    let isProcessingQueue = false;
    let lastApiRequest = 0;
    let apiRequestCount = 0;
    let apiRequestResetTime = 0;
    
    // ============================================================================
    // XHR/FETCH MONITORING FOR ITEM LOADING DETECTION
    // ============================================================================
    
    let itemLoadingState = {
        isLoading: false,
        lastLoadTime: 0,
        loadingRequests: new Set(),
        totalRequests: 0,
        completedRequests: 0
    };
    
    // ============================================================================
    // UTILITY FUNCTIONS
    // ============================================================================
    
    function log(message) {
        console.log(`[${SCRIPT_NAME}] ${message}`);
    }

    // ============================================================================
    // IGNORED ITEMS MANAGEMENT
    // ============================================================================

    // Load ignored items from localStorage
    function loadIgnoredItems() {
        try {
            const stored = localStorage.getItem(IGNORED_ITEMS_STORAGE_KEY);
            if (stored) {
                const ignoredArray = JSON.parse(stored);
                ignoredItems = new Set(ignoredArray);
            } else {
                ignoredItems = new Set();
            }
        } catch (e) {
            log('Error loading ignored items: ' + e.message);
            ignoredItems = new Set();
        }
    }

    // Save ignored items to localStorage
    function saveIgnoredItems() {
        try {
            const ignoredArray = Array.from(ignoredItems);
            localStorage.setItem(IGNORED_ITEMS_STORAGE_KEY, JSON.stringify(ignoredArray));
        } catch (e) {
            log('Error saving ignored items: ' + e.message);
        }
    }

    // Toggle ignore status for an item
    function toggleIgnoreItem(itemId) {
        // Convert to string to ensure consistency
        const itemIdStr = itemId.toString();
        
        if (ignoredItems.has(itemIdStr)) {
            ignoredItems.delete(itemIdStr);
            log(`Removed item ${itemIdStr} from ignored list`);
        } else {
            ignoredItems.add(itemIdStr);
            log(`Added item ${itemIdStr} to ignored list`);
        }
        saveIgnoredItems();
        updateTableDisplay();
        
        // Show visual feedback with toast notification
        const statusDiv = document.getElementById('scan-status');
        const isIgnored = ignoredItems.has(itemIdStr);
        const itemName = Array.from(scannedItems.values()).find(item => item.id.toString() === itemIdStr)?.name || 'Unknown';
        const message = isIgnored ? `"${itemName}" ignored` : `"${itemName}" un-ignored`;
        
        // Create a temporary toast notification
        const toast = document.createElement('div');
        toast.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: ${isIgnored ? '#dc3545' : '#28a745'};
            color: white;
            padding: 10px 15px;
            border-radius: 5px;
            z-index: 100002;
            font-size: 14px;
            font-weight: bold;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            animation: slideIn 0.3s ease-out;
        `;
        toast.textContent = message;
        
        // Add animation keyframes if not already added
        if (!document.querySelector('#toast-animations')) {
            const animationStyle = document.createElement('style');
            animationStyle.id = 'toast-animations';
            animationStyle.textContent = `
                @keyframes slideIn {
                    from { transform: translateX(100%); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }
                @keyframes slideOut {
                    from { transform: translateX(0); opacity: 1; }
                    to { transform: translateX(100%); opacity: 0; }
                }
            `;
            document.head.appendChild(animationStyle);
        }
        
        document.body.appendChild(toast);
        
        // Remove toast after 3 seconds
        setTimeout(() => {
            toast.style.animation = 'slideOut 0.3s ease-out';
            setTimeout(() => {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
            }, 300);
        }, 3000);
        
        // Also update status briefly if available
        if (statusDiv) {
            const originalContent = statusDiv.innerHTML;
            statusDiv.innerHTML = `<span style="color: ${isIgnored ? '#dc3545' : '#28a745'};">${message}</span>`;
            setTimeout(() => {
                statusDiv.innerHTML = originalContent;
            }, 2000);
        }
    }

    // Check if an item is ignored
    function isItemIgnored(itemId) {
        return ignoredItems.has(itemId.toString());
    }

    // ============================================================================
    // FILTER STATE MANAGEMENT
    // ============================================================================
    
    /**
     * Load filter state from localStorage with fallback to default values
     * @returns {Object} Filter state object with showNonTradeable, showIgnored, hideLowValue, minValue
     */
    function loadFilterState() {
        try {
            const saved = localStorage.getItem(FILTER_STATE_STORAGE_KEY);
            if (saved) {
                return JSON.parse(saved);
            }
        } catch (e) {
            log('Error loading filter state: ' + e.message);
        }
        
        // Default state - only hideLowValue is true
        return {
            showNonTradeable: false,
            showIgnored: false,
            hideLowValue: true,
            minValue: 5000000
        };
    }

    function saveFilterState(state) {
        try {
            localStorage.setItem(FILTER_STATE_STORAGE_KEY, JSON.stringify(state));
        } catch (e) {
            log('Error saving filter state: ' + e.message);
        }
    }

    function getCurrentFilterState() {
        const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
        const showIgnoredCheckbox = document.getElementById('show-ignored');
        const hideLowValueCheckbox = document.getElementById('hide-low-value');
        const minValueInput = document.getElementById('min-value-input');
        
        return {
            showNonTradeable: showNonTradeableCheckbox ? showNonTradeableCheckbox.checked : false,
            showIgnored: showIgnoredCheckbox ? showIgnoredCheckbox.checked : false,
            hideLowValue: hideLowValueCheckbox ? hideLowValueCheckbox.checked : true,
            minValue: minValueInput ? parseFloat(minValueInput.value.replace(/[,$]/g, '')) || 5000000 : 5000000
        };
    }

    function applyFilterState(state) {
        const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
        const showIgnoredCheckbox = document.getElementById('show-ignored');
        const hideLowValueCheckbox = document.getElementById('hide-low-value');
        const minValueInput = document.getElementById('min-value-input');
        
        if (showNonTradeableCheckbox) showNonTradeableCheckbox.checked = state.showNonTradeable;
        if (showIgnoredCheckbox) showIgnoredCheckbox.checked = state.showIgnored;
        if (hideLowValueCheckbox) hideLowValueCheckbox.checked = state.hideLowValue;
        if (minValueInput) minValueInput.value = state.minValue.toLocaleString();
    }
    
    function waitForElement(selector, timeout = 10000) {
        return new Promise((resolve, reject) => {
            const element = document.querySelector(selector);
            if (element) {
                resolve(element);
                return;
            }
            
            const observer = new MutationObserver(() => {
                const element = document.querySelector(selector);
                if (element) {
                    observer.disconnect();
                    resolve(element);
                }
            });
            
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
            
            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Element ${selector} not found within ${timeout}ms`));
            }, timeout);
        });
    }
    
    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    // Window state management
    function loadWindowState() {
        try {
            const stateData = localStorage.getItem(WINDOW_STATE_STORAGE_KEY);
            if (stateData) {
                return JSON.parse(stateData);
            }
        } catch (error) {
            log(`Failed to load window state: ${error.message}`);
        }
        return {
            x: 20,
            y: 20,
            width: WINDOW_DEFAULT_WIDTH,
            height: WINDOW_DEFAULT_HEIGHT,
            minimized: false
        };
    }
    
    function saveWindowState(x, y, width, height, minimized) {
        try {
            const state = { x, y, width, height, minimized };
            localStorage.setItem(WINDOW_STATE_STORAGE_KEY, JSON.stringify(state));
        } catch (error) {
            log(`Failed to save window state: ${error.message}`);
        }
    }
    
    // API Key management
    function loadApiKey() {
        try {
            const keyData = localStorage.getItem(API_KEY_STORAGE_KEY);
            const keyExpiry = localStorage.getItem(API_KEY_EXPIRY_STORAGE_KEY);
            
            if (keyData && keyExpiry) {
                const expiryDate = new Date(parseInt(keyExpiry));
                if (new Date() < expiryDate) {
                    apiKey = keyData;
                    log('API key loaded from storage');
                    return true;
                }
            }
        } catch (error) {
            log(`Failed to load API key: ${error.message}`);
        }
        return false;
    }
    
    function saveApiKey(key) {
        try {
            const expiryDate = new Date(Date.now() + API_KEY_EXPIRY_DURATION);
            localStorage.setItem(API_KEY_STORAGE_KEY, key);
            localStorage.setItem(API_KEY_EXPIRY_STORAGE_KEY, expiryDate.getTime().toString());
            apiKey = key;
            log('API key saved to storage');
        } catch (error) {
            log(`Failed to save API key: ${error.message}`);
        }
    }
    
    // API Items data management
    function loadApiItemsData() {
        try {
            const itemsData = localStorage.getItem(API_DATA_STORAGE_KEY);
            const itemsExpiry = localStorage.getItem(API_EXPIRY_STORAGE_KEY);
            
            if (itemsData && itemsExpiry) {
                const expiryDate = new Date(parseInt(itemsExpiry));
                if (new Date() < expiryDate) {
                    apiItemsData = JSON.parse(itemsData);
                    log(`Loaded ${Object.keys(apiItemsData).length} items from API cache`);
                    return true;
                }
            }
        } catch (error) {
            log(`Failed to load API items data: ${error.message}`);
        }
        return false;
    }
    
    function saveApiItemsData(data) {
        try {
            const expiryDate = new Date(Date.now() + API_CACHE_DURATION);
            localStorage.setItem(API_DATA_STORAGE_KEY, JSON.stringify(data));
            localStorage.setItem(API_EXPIRY_STORAGE_KEY, expiryDate.getTime().toString());
            apiItemsData = data;
            log(`Saved ${Object.keys(data).length} items to API cache`);
        } catch (error) {
            log(`Failed to save API items data: ${error.message}`);
        }
    }
    
    // Fetch items from API
    async function fetchItemsFromAPI(key) {
        try {
            const response = await fetch(`https://api.torn.com/v2/torn/items?cat=All&sort=ASC&key=${key}`);
            if (!response.ok) {
                throw new Error(`API request failed: ${response.status} ${response.statusText}`);
            }
            
            const data = await response.json();
            if (data.error) {
                throw new Error(`API error: ${data.error.error}`);
            }
            
            // Convert array to object with ID as key for easier lookup
            const itemsObject = {};
            if (data.items && Array.isArray(data.items)) {
                data.items.forEach(item => {
                    itemsObject[item.id] = item;
                });
            }
            
            saveApiItemsData(itemsObject);
            return itemsObject;
        } catch (error) {
            throw new Error(`Failed to fetch items from API: ${error.message}`);
        }
    }
    
    // Check if item is tradeable
    function isItemTradeable(itemId) {
        if (!apiItemsData || !itemId) return null; // null means unknown
        
        const item = apiItemsData[itemId];
        if (!item) return null;
        
        return item.is_tradable === true;
    }
    
    // Market data management
    function loadMarketData() {
        try {
            const marketDataStr = localStorage.getItem(MARKET_DATA_STORAGE_KEY);
            const marketExpiry = localStorage.getItem(MARKET_EXPIRY_STORAGE_KEY);
            
            if (marketDataStr && marketExpiry) {
                const expiryDate = new Date(parseInt(marketExpiry));
                if (new Date() < expiryDate) {
                    const savedData = JSON.parse(marketDataStr);
                    marketData = new Map(savedData);
                    log(`Loaded market data for ${marketData.size} items from cache`);
                    return true;
                }
            }
        } catch (error) {
            log(`Failed to load market data: ${error.message}`);
        }
        return false;
    }
    
    function saveMarketData() {
        try {
            const expiryDate = new Date(Date.now() + MARKET_CACHE_DURATION);
            const dataArray = Array.from(marketData.entries());
            localStorage.setItem(MARKET_DATA_STORAGE_KEY, JSON.stringify(dataArray));
            localStorage.setItem(MARKET_EXPIRY_STORAGE_KEY, expiryDate.getTime().toString());
            log(`Saved market data for ${marketData.size} items to cache`);
        } catch (error) {
            log(`Failed to save market data: ${error.message}`);
        }
    }
    
    // Calculate estimated value based on lowest market price after outlier removal
    function calculateEstimatedValue(itemId, quantity) {
        const marketInfo = marketData.get(itemId);
        if (!marketInfo || !marketInfo.listings || marketInfo.listings.length === 0) {
            return 0;
        }
        
        // Calculate price stats to get consistent pricing
        const stats = calculatePriceStats(marketInfo.listings);
        if (!stats) {
            return 0;
        }
        
        // Use the same low-end price calculation as the Price column
        const lowPrice = Math.min(stats.p25, stats.min * 1.2);
        
        // Calculate estimated value: low-end price * quantity
        return Math.round(lowPrice * quantity);
    }
    
    // ============================================================================
    // SORTING FUNCTIONS
    // ============================================================================
    
    // Global sorting state
    let currentSortColumn = -1;
    let currentSortDirection = 'asc';
    let currentSortField = null; // Track which field we're sorting by
    
    // Apply current sort to items array
    function applySortToItems(items) {
        if (currentSortColumn === -1 || !currentSortField) {
            // No active sort, use default sorting
            return items.sort((a, b) => {
                const aIgnored = isItemIgnored(a.id);
                const bIgnored = isItemIgnored(b.id);
                
                // If showing ignored items, put ignored items at the top for easy un-ignoring
                const showIgnoredCheckbox = document.getElementById('show-ignored');
                const showIgnored = showIgnoredCheckbox ? showIgnoredCheckbox.checked : false;
                
                if (showIgnored) {
                    if (aIgnored && !bIgnored) return -1;
                    if (bIgnored && !aIgnored) return 1;
                } else {
                    // If not showing ignored items, put ignored items last (they'll be filtered out anyway)
                    if (aIgnored && !bIgnored) return 1;
                    if (bIgnored && !aIgnored) return -1;
                }
                
                // If showing non-tradeable items, put them after ignored items
                const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
                const showNonTradeable = showNonTradeableCheckbox ? showNonTradeableCheckbox.checked : false;
                
                if (showNonTradeable && !aIgnored && !bIgnored) {
                    if (a.tradeable === false && b.tradeable !== false) return -1;
                    if (b.tradeable === false && a.tradeable !== false) return 1;
                }
                
                const categoryA = a.category || '';
                const categoryB = b.category || '';
                const nameA = a.name || '';
                const nameB = b.name || '';
                
                if (categoryA !== categoryB) {
                    return categoryA.localeCompare(categoryB);
                }
                return nameA.localeCompare(nameB);
            });
        }
        
        // Apply active sort
        return items.sort((a, b) => {
            let aValue, bValue;
            
            switch (currentSortField) {
                case 'name':
                    aValue = a.name || '';
                    bValue = b.name || '';
                    break;
                case 'category':
                    aValue = a.category || '';
                    bValue = b.category || '';
                    break;
                case 'qty':
                    aValue = a.quantity || 0;
                    bValue = b.quantity || 0;
                    break;
                case 'trade':
                    aValue = a.tradeable === true ? 1 : 0;
                    bValue = b.tradeable === true ? 1 : 0;
                    break;
                case 'price':
                    // Get market price for comparison
                    const aMarketInfo = marketData.get(a.id.toString());
                    const bMarketInfo = marketData.get(b.id.toString());
                    
                    if (aMarketInfo && a.tradeable === true) {
                        const aStats = calculatePriceStats(aMarketInfo.listings);
                        aValue = aStats ? Math.min(aStats.p25, aStats.min * 1.2) : 0;
                    } else {
                        aValue = 0;
                    }
                    
                    if (bMarketInfo && b.tradeable === true) {
                        const bStats = calculatePriceStats(bMarketInfo.listings);
                        bValue = bStats ? Math.min(bStats.p25, bStats.min * 1.2) : 0;
                    } else {
                        bValue = 0;
                    }
                    break;
                case 'estimated':
                    aValue = calculateEstimatedValue(a.id, a.quantity);
                    bValue = calculateEstimatedValue(b.id, b.quantity);
                    break;
                default:
                    return 0;
            }
            
            // Apply sort direction
            if (currentSortDirection === 'asc') {
                if (typeof aValue === 'string') {
                    return aValue.localeCompare(bValue);
                }
                return aValue > bValue ? 1 : aValue < bValue ? -1 : 0;
            } else {
                if (typeof aValue === 'string') {
                    return bValue.localeCompare(aValue);
                }
                return aValue < bValue ? 1 : aValue > bValue ? -1 : 0;
            }
        });
    }
    
    // Dynamic sort function that adjusts for Trade column visibility
    function sortTableDynamic(columnName) {
        const tradeHeader = document.getElementById('trade-column-header');
        if (!tradeHeader) {
            return;
        }
        
        const tradeColumnVisible = tradeHeader.style.display !== 'none';
        
        let columnIndex;
        
        // Map column names to indices, adjusting for Trade column visibility
        switch (columnName) {
            case 'name': columnIndex = 0; break;
            case 'category': columnIndex = 1; break;
            case 'qty': columnIndex = 2; break;
            case 'trade': columnIndex = tradeColumnVisible ? 3 : -1; break;
            case 'price': columnIndex = tradeColumnVisible ? 4 : 3; break;
            case 'estimated': columnIndex = tradeColumnVisible ? 5 : 4; break;
            default: return;
        }
        
        if (columnIndex === -1) return; // Trade column not visible
        
        // Update sort direction
        if (currentSortColumn === columnIndex && currentSortField === columnName) {
            currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            currentSortDirection = 'asc';
            currentSortColumn = columnIndex;
            currentSortField = columnName;
        }
        
        // Clear all sort indicators
        ['name', 'category', 'qty', 'trade', 'price', 'estimated'].forEach(col => {
            const indicator = document.getElementById(`sort-${col}`);
            if (indicator) indicator.textContent = '↕';
        });
        
        // Set current sort indicator
        const currentIndicator = document.getElementById(`sort-${columnName}`);
        if (currentIndicator) {
            currentIndicator.textContent = currentSortDirection === 'asc' ? '↑' : '↓';
        }
        
        // Re-render table with new sort
        updateTableDisplay();
    }

    // Make sorting functions global immediately after definition
    window.sortTableDynamic = sortTableDynamic;
    window.sortTable = sortTable;
    window.toggleIgnoreItem = toggleIgnoreItem;
    window.resetApiState = resetApiState;

    // Sort table function
    function sortTable(columnIndex) {
        const table = document.getElementById('all-items-table');
        if (!table) {
            return;
        }
        
        const tbody = table.querySelector('tbody');
        if (!tbody) {
            return;
        }
        
        const rows = Array.from(tbody.querySelectorAll('tr'));
        
        if (rows.length === 0) {
            return;
        }
        
        const tradeColumnVisible = document.getElementById('trade-column-header').style.display !== 'none';
        
        // Update sort direction
        if (currentSortColumn === columnIndex) {
            currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            currentSortDirection = 'asc';
            currentSortColumn = columnIndex;
        }
        
        // Clear all sort indicators
        ['name', 'category', 'qty', 'trade', 'price', 'estimated'].forEach(col => {
            const indicator = document.getElementById(`sort-${col}`);
            if (indicator) indicator.textContent = '↕';
        });
        
        // Set current sort indicator
        const columnNames = ['name', 'category', 'qty', 'trade', 'price', 'estimated'];
        
        if (columnNames[columnIndex]) {
            const currentIndicator = document.getElementById(`sort-${columnNames[columnIndex]}`);
            if (currentIndicator) {
                currentIndicator.textContent = currentSortDirection === 'asc' ? '↑' : '↓';
            }
        }
        
        // Sort rows
        rows.sort((a, b) => {
            let aValue = a.cells[columnIndex].textContent.trim();
            let bValue = b.cells[columnIndex].textContent.trim();
            
            // Handle different data types based on fixed column positions
            let qtyColumnIndex = 2;
            let tradeColumnIndex = 3;
            let priceColumnIndex = 4;
            let estimatedColumnIndex = 5;
            
            // Handle different data types
            if (columnIndex === qtyColumnIndex || columnIndex === priceColumnIndex || columnIndex === estimatedColumnIndex) {
                // Remove currency symbols and commas, convert to numbers
                aValue = parseFloat(aValue.replace(/[$,]/g, '')) || 0;
                bValue = parseFloat(bValue.replace(/[$,]/g, '')) || 0;
            } else if (columnIndex === tradeColumnIndex) {
                // Convert ✓/✗ to sortable values
                aValue = aValue === '✓' ? 1 : 0;
                bValue = bValue === '✓' ? 1 : 0;
            }
            
            if (currentSortDirection === 'asc') {
                return aValue > bValue ? 1 : aValue < bValue ? -1 : 0;
            } else {
                return aValue < bValue ? 1 : aValue > bValue ? -1 : 0;
            }
        });
        
        // Re-append sorted rows
        rows.forEach(row => tbody.appendChild(row));
    }
    
    // Check if all items are likely loaded by examining scroll position and item count
    function checkItemLoadingStatus() {
        try {
            // Check if we're at the bottom of the page
            const scrollBottom = window.innerHeight + window.scrollY;
            const pageHeight = Math.max(
                document.body.scrollHeight,
                document.body.offsetHeight,
                document.documentElement.clientHeight,
                document.documentElement.scrollHeight,
                document.documentElement.offsetHeight
            );
            const isNearBottom = scrollBottom >= (pageHeight - 1000);
            
            // Use the SAME logic as scanVisibleItems to count items accurately
            let currentVisibleItems = 0;
            
            // Detect page type (same as scanVisibleItems)
            const isMarketPage = window.location.href.includes('ItemMarket') || 
                               window.location.href.includes('itemmarket') ||
                               window.location.href.includes('addListing');
            
            // Declare containers at function scope for later use
            let allItemsContainer = null;
            
            if (isMarketPage) {
                // Market page: try broader selectors to detect any list items
                const marketSelectors = [
                    '.items-list li, .items-list .item',           // Market inventory items
                    '.item-list li, .item-list .item',             // Alternative market inventory
                    '.inventory-items li, .inventory-items .item', // Market inventory container
                    '.market-items li, .market-items .item',       // Market-specific items
                    '.listing-items li, .listing-items .item',     // Market listing items
                    'ul li[data-item]',                            // Any list items with data-item
                    'li[data-item]',                               // Fallback: any items
                    '.item, .inventory-item',                      // CSS class based items
                    '[class*="item"]',                             // Any element with 'item' in class
                    'li[class*="item"], div[class*="item"]'        // List or div items
                ];
                
                for (const selector of marketSelectors) {
                    const itemElements = document.querySelectorAll(selector);
                    if (itemElements.length > 0) {
                        currentVisibleItems = itemElements.length;
                        break;
                    }
                }
                
                // Special case for market page: if we can't find specific items,
                // but we have scanned items, assume the page is loaded
                if (currentVisibleItems === 0 && scannedItems.size > 0) {
                    currentVisibleItems = scannedItems.size;
                }
            } else {
                // Inventory page: check for all-items container first (same as scanVisibleItems)
                allItemsContainer = document.querySelector('#all-items');
                if (allItemsContainer && allItemsContainer.style.display !== 'none') {
                    const itemElements = allItemsContainer.querySelectorAll('li[data-item][data-category]:not([data-action])');
                    currentVisibleItems = itemElements.length;
                } else {
                    // If not in all-items, look for any visible inventory container with items
                    const inventoryContainers = document.querySelectorAll('.inventory-wrap, .category-wrap, [id*="items"]');
                    
                    for (const container of inventoryContainers) {
                        if (container.style.display !== 'none' && !container.hidden) {
                            const containerItems = container.querySelectorAll('li[data-item][data-category]:not([data-action])');
                            if (containerItems.length > 0) {
                                currentVisibleItems = containerItems.length;
                                break;
                            }
                        }
                    }
                    
                    // Fallback: scan any visible items on the page
                    if (currentVisibleItems === 0) {
                        const itemElements = document.querySelectorAll('li[data-item][data-category]:not([data-action])');
                        currentVisibleItems = itemElements.length;
                    }
                    
                    // Additional fallback: try broader selectors for inventory
                    if (currentVisibleItems === 0) {
                        const broaderSelectors = [
                            'li[data-item]',  // Any list item with data-item
                            '.inventory-item', // CSS class based
                            '[data-category]', // Any element with data-category
                            '.item-wrap li'    // Item wrapper lists
                        ];
                        
                        for (const selector of broaderSelectors) {
                            const elements = document.querySelectorAll(selector);
                            if (elements.length > 0) {
                                currentVisibleItems = elements.length;
                                break;
                            }
                        }
                    }
                }
            }
            
            // Get network loading state
            const loadingState = getItemLoadingState();
            
            // Get the items container to check if there's a loading indicator (be more specific)
            const loadingIndicators = [
                '.loading:not(.hidden)', 
                '.spinner:not(.hidden)', 
                '.ajax-loader:not(.hidden)', 
                '.loading-spinner:not(.hidden)',
                '[class*="loading"]:not(.hidden):not([style*="display: none"])',
                '[class*="spinner"]:not(.hidden):not([style*="display: none"])'
            ];
            
            let hasLoadingIndicator = false;
            let foundIndicators = [];
            
            // Check only within the items container first for more accuracy
            // For market page, look for loading indicators in market-specific containers
            let containerToCheck = null;
            if (isMarketPage) {
                // Look for market-specific containers that might have loading indicators
                containerToCheck = document.querySelector('.items-list, .item-list, .inventory-items, .market-items, .listing-items') ||
                                 document.querySelector('main, .content, .market-content');
            } else {
                containerToCheck = allItemsContainer || document.querySelector('.inventory-wrap, .category-wrap');
            }
            
            // If we have a specific container, check for loading indicators within it
            if (containerToCheck) {
                for (const indicator of loadingIndicators) {
                    const elements = containerToCheck.querySelectorAll(indicator);
                    if (elements.length > 0) {
                        // Check if any of these elements are actually visible
                        for (const el of elements) {
                            const rect = el.getBoundingClientRect();
                            const isVisible = rect.width > 0 && rect.height > 0 && 
                                            window.getComputedStyle(el).display !== 'none' &&
                                            window.getComputedStyle(el).visibility !== 'hidden';
                            if (isVisible) {
                                hasLoadingIndicator = true;
                                foundIndicators.push(indicator);
                                break;
                            }
                        }
                        if (hasLoadingIndicator) break;
                    }
                }
            }
            
            // If no container-specific indicators found, do a global check as fallback
            if (!hasLoadingIndicator) {
                for (const indicator of loadingIndicators) {
                    const elements = document.querySelectorAll(indicator);
                    if (elements.length > 0) {
                        // Check if any of these elements are actually visible
                        for (const el of elements) {
                            const rect = el.getBoundingClientRect();
                            const isVisible = rect.width > 0 && rect.height > 0 && 
                                            window.getComputedStyle(el).display !== 'none' &&
                                            window.getComputedStyle(el).visibility !== 'hidden';
                            if (isVisible) {
                                hasLoadingIndicator = true;
                                foundIndicators.push(indicator);
                                break;
                            }
                        }
                        if (hasLoadingIndicator) break;
                    }
                }
            }
            
            // Override loading detection if it's been too long since last network activity
            const timeSinceLastActivity = Date.now() - Math.max(itemLoadingState.lastLoadTime, 0);
            const loadingTimeout = 10000; // 10 seconds timeout
            const isLoadingTimedOut = timeSinceLastActivity > loadingTimeout && itemLoadingState.totalRequests > 0;
            
            // Combine visual loading indicators with network activity, but allow timeout override
            const isCurrentlyLoading = !isLoadingTimedOut && (hasLoadingIndicator || loadingState.isLoading);
            
            // More intelligent "all loaded" detection using network monitoring:
            // 1. Near bottom of page
            // 2. No loading indicators (visual or network) OR loading has timed out  
            // 3. At least some items found (more lenient for market page)
            // 4. If we have scanned items, the visible count should be close to or greater than scanned count
            // 5. If network requests have been made, they should be completed for at least 2 seconds
            
            // Market page is more lenient - we mainly care about network activity completion
            const minimumItemsRequired = isMarketPage ? 0 : 10;
            let likelyAllLoaded = isNearBottom && (!isCurrentlyLoading || isLoadingTimedOut) && currentVisibleItems >= minimumItemsRequired;
            
            // Market page special case: if we have scanned items and are near bottom, assume loaded
            if (isMarketPage && isNearBottom && scannedItems.size > 0 && !isCurrentlyLoading) {
                likelyAllLoaded = true;
            }
            
            // Additional check: if we have scanned items, visible items should be at least as many
            // (unless user filtered/changed view) - skip this check for market page
            if (likelyAllLoaded && scannedItems.size > 0 && !isMarketPage) {
                // If visible items are significantly less than scanned items, we might be in wrong view or not fully loaded
                if (currentVisibleItems < scannedItems.size * 0.8) {
                    likelyAllLoaded = false;
                }
            }
            
            // Network-based check: if requests were made but not settled for long enough, don't mark as complete
            // UNLESS we've timed out the loading detection
            if (likelyAllLoaded && !isLoadingTimedOut && loadingState.totalRequests > 0 && !loadingState.likelyFinishedLoading) {
                likelyAllLoaded = false;
            }
            
            // Special case: if visible items == scanned items and we're near bottom, likely all loaded
            // even if there are some loading indicators (they might be false positives)
            if (!likelyAllLoaded && isNearBottom && currentVisibleItems >= minimumItemsRequired && 
                scannedItems.size > 0 && (isMarketPage || currentVisibleItems >= scannedItems.size * 0.95)) {
                likelyAllLoaded = true;
            }
            
            return {
                isNearBottom,
                currentVisibleItems,
                hasLoadingIndicator,
                isCurrentlyLoading,
                isLoadingTimedOut,
                likelyAllLoaded,
                pageHeight,
                scrollPosition: scrollBottom,
                scannedItemsCount: scannedItems.size,
                loadingState,
                foundIndicators
            };
        } catch (error) {
            log('Error checking item loading status: ' + error.message);
            return {
                isNearBottom: false,
                currentVisibleItems: 0,
                hasLoadingIndicator: false,
                isCurrentlyLoading: false,
                isLoadingTimedOut: false,
                likelyAllLoaded: false,
                pageHeight: 0,
                scrollPosition: 0,
                scannedItemsCount: scannedItems.size,
                loadingState: getItemLoadingState(),
                foundIndicators: []
            };
        }
    }
    
    // Expose loading state for debugging
    window.getScrap2MarkLoadingState = function() {
        return {
            itemLoadingState,
            currentStatus: checkItemLoadingStatus(),
            scannedItemsCount: scannedItems.size
        };
    };
    
    // Manual override for stuck loading detection
    window.forceScrap2MarkReady = function() {
        log('Manual override: Forcing loading state to complete');
        itemLoadingState.isLoading = false;
        itemLoadingState.loadingRequests.clear();
        itemLoadingState.lastLoadTime = Date.now() - 15000; // Set to 15 seconds ago
        updateScrollReminder();
        return 'Loading state reset - status should update shortly';
    };

    // XHR/Fetch monitoring functions
    function setupNetworkMonitoring() {
        // Monitor XMLHttpRequest
        const originalXHROpen = XMLHttpRequest.prototype.open;
        const originalXHRSend = XMLHttpRequest.prototype.send;
        
        XMLHttpRequest.prototype.open = function(method, url, ...args) {
            this._scrap2mark_url = url;
            this._scrap2mark_method = method;
            return originalXHROpen.apply(this, [method, url, ...args]);
        };
        
        XMLHttpRequest.prototype.send = function(...args) {
            const url = this._scrap2mark_url;
            
            // Check if this looks like an inventory/item loading request
            if (url && isItemLoadingRequest(url)) {
                const requestId = Date.now() + Math.random();
                itemLoadingState.loadingRequests.add(requestId);
                itemLoadingState.isLoading = true;
                itemLoadingState.totalRequests++;
                
                const handleResponse = () => {
                    itemLoadingState.loadingRequests.delete(requestId);
                    itemLoadingState.completedRequests++;
                    itemLoadingState.lastLoadTime = Date.now();
                    
                    if (itemLoadingState.loadingRequests.size === 0) {
                        itemLoadingState.isLoading = false;
                        // Update status after a short delay to allow DOM updates
                        setTimeout(updateScrollReminder, 500);
                    }
                };
                
                this.addEventListener('load', handleResponse);
                this.addEventListener('error', handleResponse);
                this.addEventListener('abort', handleResponse);
            }
            
            return originalXHRSend.apply(this, args);
        };
        
        // Monitor fetch API
        const originalFetch = window.fetch;
        window.fetch = function(url, options = {}) {
            // Check if this looks like an inventory/item loading request
            if (url && isItemLoadingRequest(url.toString())) {
                const requestId = Date.now() + Math.random();
                itemLoadingState.loadingRequests.add(requestId);
                itemLoadingState.isLoading = true;
                itemLoadingState.totalRequests++;
                
                const promise = originalFetch.apply(this, [url, options]);
                
                promise.finally(() => {
                    itemLoadingState.loadingRequests.delete(requestId);
                    itemLoadingState.completedRequests++;
                    itemLoadingState.lastLoadTime = Date.now();
                    
                    if (itemLoadingState.loadingRequests.size === 0) {
                        itemLoadingState.isLoading = false;
                        // Update status after a short delay to allow DOM updates
                        setTimeout(updateScrollReminder, 500);
                    }
                });
                
                return promise;
            }
            
            return originalFetch.apply(this, [url, options]);
        };
    }
    
    // Check if a URL looks like an inventory/item loading request
    function isItemLoadingRequest(url) {
        if (!url || typeof url !== 'string') return false;
        
        // Convert to lowercase for case-insensitive matching
        const lowerUrl = url.toLowerCase();
        
        // Common patterns for inventory/item loading requests in Torn
        const itemLoadingPatterns = [
            '/item.php',           // Direct item page requests
            '/loader.php',         // Generic loader that might load items
            'action=load',         // Action parameter for loading
            'action=get',          // Action parameter for getting data
            'action=getdata',      // Torn's getdata action
            'step=get',            // Step parameter
            'step=load',           // Step load parameter
            'rfcv=',               // Torn's request verification token (likely AJAX)
            'torn_user=',          // Torn user parameter in AJAX
            'sid=inventory',       // Inventory section ID
            'sid=itemmarket',      // Market section ID
            'inventory',           // Contains inventory in URL
            'items',               // Contains items in URL
            'loadmore',            // Load more functionality
            'pagination',          // Pagination requests
            'offset=',             // Offset parameter suggests pagination
            'page=',               // Page parameter
            'limit=',              // Limit parameter
            'category=',           // Category loading
            '/ajax/',              // AJAX requests that might load items
            'ajaxhelpers',         // Torn's AJAX helper functions
            'helpers.php',         // Torn's helper functions
            'itemmarket',          // Market-specific requests
            'addlisting',          // Add listing functionality
        ];
        
        // Check if URL matches any item loading patterns
        const matches = itemLoadingPatterns.some(pattern => lowerUrl.includes(pattern));
        
        // Exclude our own API requests and external resources
        const isOurApiRequest = lowerUrl.includes('api.torn.com') && (
            lowerUrl.includes('itemmarket') || 
            lowerUrl.includes('/torn/items')
        );
        
        const isExternalResource = lowerUrl.includes('cloudflare') || 
                                  lowerUrl.includes('google') || 
                                  lowerUrl.includes('facebook') ||
                                  lowerUrl.includes('.css') ||
                                  lowerUrl.includes('.js') ||
                                  lowerUrl.includes('.png') ||
                                  lowerUrl.includes('.jpg') ||
                                  lowerUrl.includes('.gif') ||
                                  lowerUrl.includes('.webp');
        
        return matches && !isOurApiRequest && !isExternalResource;
    }
    
    // Get current loading state for display
    function getItemLoadingState() {
        const now = Date.now();
        const timeSinceLastLoad = now - itemLoadingState.lastLoadTime;
        
        return {
            isLoading: itemLoadingState.isLoading,
            activeRequests: itemLoadingState.loadingRequests.size,
            totalRequests: itemLoadingState.totalRequests,
            completedRequests: itemLoadingState.completedRequests,
            timeSinceLastLoad,
            likelyFinishedLoading: !itemLoadingState.isLoading && timeSinceLastLoad > 2000 && itemLoadingState.totalRequests > 0
        };
    }
    
    // Update scroll reminder based on loading status
    function updateScrollReminder() {
        const reminder = document.getElementById('scroll-reminder');
        const progressSpan = document.getElementById('loading-progress');
        if (!reminder) return;
        
        // Detect page type for contextual messaging
        const isMarketPage = window.location.href.includes('ItemMarket') || 
                           window.location.href.includes('itemmarket') ||
                           window.location.href.includes('addListing');
        
        const status = checkItemLoadingStatus();
        
        // Update progress indicator
        if (progressSpan) {
            if (status.likelyAllLoaded) {
                progressSpan.innerHTML = `<span style="color: #28a745; font-weight: bold;">✅ Complete (${status.currentVisibleItems} items visible, ${status.scannedItemsCount} scanned)</span>`;
            } else if (status.isLoadingTimedOut) {
                progressSpan.innerHTML = `<span style="color: #ffc107; font-weight: bold;">⏰ Loading timed out (${status.currentVisibleItems} visible)</span>`;
            } else if (status.isCurrentlyLoading) {
                const loadingText = status.loadingState.isLoading ? 
                    `🔄 Loading via network (${status.loadingState.activeRequests} active)` : 
                    `🔄 Loading... (${status.currentVisibleItems} visible)`;
                progressSpan.innerHTML = `<span style="color: #ffc107; font-weight: bold;">${loadingText}</span>`;
            } else if (status.isNearBottom) {
                progressSpan.innerHTML = `<span style="color: #17a2b8; font-weight: bold;">📍 Near bottom (${status.currentVisibleItems} visible, ${status.scannedItemsCount} scanned)</span>`;
            } else {
                progressSpan.innerHTML = `<span style="color: #dc3545; font-weight: bold;">📜 Keep scrolling (${status.currentVisibleItems} visible)</span>`;
            }
        }
        
        if (status.likelyAllLoaded) {
            reminder.style.cssText = `
                margin-bottom: 10px; 
                font-size: 11px; 
                background: #d4edda; 
                border: 1px solid #c3e6cb; 
                border-radius: 4px; 
                padding: 8px;
            `;
            
            const nextStepText = isMarketPage ? 
                'You can now add items to market listings' :
                'Use "Get Market Prices" then "Fill Market Forms"';
            
            reminder.innerHTML = `
                <div style="display: flex; align-items: center; gap: 8px;">
                    <span style="color: #155724; font-weight: bold;">✅ READY:</span>
                    <span style="color: #155724;">All items likely loaded - You can safely proceed!</span>
                </div>
                <div style="margin-top: 4px; font-size: 10px; color: #6c6c6c;">
                    <strong>Status:</strong> <span style="color: #28a745; font-weight: bold;">✅ Complete (${status.currentVisibleItems} visible, ${status.scannedItemsCount} scanned)</span> | 
                    <strong>Next:</strong> ${nextStepText}
                </div>
            `;
        } else {
            reminder.style.cssText = `
                margin-bottom: 10px; 
                font-size: 11px; 
                background: #fff3cd; 
                border: 1px solid #ffeaa7; 
                border-radius: 4px; 
                padding: 8px;
            `;
            
            let statusText, progressColor, actionAdvice;
            if (status.isCurrentlyLoading) {
                if (status.loadingState.isLoading) {
                    statusText = `Network loading in progress (${status.loadingState.activeRequests} active requests)`;
                    actionAdvice = "Wait for loading to complete";
                } else if (status.hasLoadingIndicator) {
                    statusText = "Visual loading indicators detected";
                    actionAdvice = "Wait for loading indicators to disappear";
                } else {
                    statusText = "Items are still loading - please wait";
                    actionAdvice = "Wait for loading indicators to disappear";
                }
                progressColor = "#ffc107";
            } else if (status.isLoadingTimedOut) {
                statusText = "Loading detection timed out - assuming complete";
                progressColor = "#28a745";
                actionAdvice = "Try scanning items now or refresh if needed";
            } else if (!status.isNearBottom) {
                statusText = "Keep scrolling to load more items";
                progressColor = "#dc3545";
                actionAdvice = isMarketPage ? "Scroll to see all available items" : "Scroll down to the bottom of the page";
            } else {
                statusText = "Loading detection may be incomplete";
                progressColor = "#6c757d";
                actionAdvice = "Try refreshing the page or manual scan";
            }
            
            // Add a manual override button if loading seems stuck
            const manualOverrideButton = (status.isCurrentlyLoading && !status.isLoadingTimedOut) ? 
                `<button onclick="window.forceScrap2MarkReady && window.forceScrap2MarkReady()" style="background: #dc3545; color: white; border: none; padding: 2px 6px; border-radius: 3px; cursor: pointer; font-size: 10px; margin-left: 8px;">Force Ready</button>` : '';
            
            reminder.innerHTML = `
                <div style="display: flex; align-items: center; gap: 8px;">
                    <span style="color: #856404; font-weight: bold;">${status.isLoadingTimedOut ? '⏰ TIMEOUT:' : '⚠️ SCROLL NEEDED:'}</span>
                    <span style="color: #856404;">${statusText}</span>
                    ${manualOverrideButton}
                </div>
                <div style="margin-top: 4px; font-size: 10px; color: #6c6c6c;">
                    <strong>Status:</strong> <span style="color: ${progressColor}; font-weight: bold;">${status.currentVisibleItems} visible${status.scannedItemsCount > 0 ? `, ${status.scannedItemsCount} scanned` : ''}</span> | 
                    <strong>Action:</strong> ${actionAdvice}
                </div>
            `;
        }
    }
    
    // Check if market data is fresh (less than 1 hour old)
    function isMarketDataFresh(marketInfo) {
        if (!marketInfo || !marketInfo.timestamp) return false;
        const oneHour = 60 * 60 * 1000; // 1 hour in milliseconds
        return (Date.now() - marketInfo.timestamp) < oneHour;
    }
    
    // Remove price outliers using statistical methods (only high outliers and extreme prices)
    function filterOutliers(listings) {
        if (!listings || listings.length < 3) return listings; // Need at least 3 listings for meaningful filtering
        
        // Get all prices and sort them
        const prices = listings.map(l => l.price).sort((a, b) => a - b);
        const minPrice = prices[0];
        const maxPrice = prices[prices.length - 1];
        
        // Rule 1: Remove prices more than 10x the minimum price
        const priceCapFilter = listings.filter(listing => listing.price <= minPrice * 10);
        
        // Rule 2: Statistical outlier detection for high outliers only
        if (priceCapFilter.length < 3) return priceCapFilter; // Not enough data for statistical analysis
        
        const uniquePrices = [...new Set(priceCapFilter.map(l => l.price))];
        if (uniquePrices.length < 3) return priceCapFilter; // Need at least 3 unique prices
        
        const mean = uniquePrices.reduce((a, b) => a + b, 0) / uniquePrices.length;
        const variance = uniquePrices.reduce((sum, price) => sum + Math.pow(price - mean, 2), 0) / uniquePrices.length;
        const stdDev = Math.sqrt(variance);
        
        if (stdDev === 0) return priceCapFilter; // All prices are the same
        
        // Only filter high outliers (prices significantly above the mean)
        const finalFiltered = priceCapFilter.filter(listing => {
            const zScore = (listing.price - mean) / stdDev;
            return zScore <= OUTLIER_THRESHOLD; // Only remove high outliers
        });
        
        // More conservative approach: only apply statistical filtering if we don't remove too many sellers
        const uniqueOriginalPrices = [...new Set(priceCapFilter.map(l => l.price))];
        const uniqueFinalPrices = [...new Set(finalFiltered.map(l => l.price))];
        const sellerRetainRatio = uniqueFinalPrices.length / uniqueOriginalPrices.length;
        
        // Keep statistical filtering only if we retain at least 70% of sellers
        if (sellerRetainRatio >= 0.7) {
            return finalFiltered;
        } else {
            return priceCapFilter; // Fall back to just the 10x price cap filter
        }
    }
    
    // API rate limiting and error handling
    function shouldThrottleApiRequest() {
        const now = Date.now();
        
        // Reset counter every minute
        if (now - apiRequestResetTime > 60000) {
            apiRequestCount = 0;
            apiRequestResetTime = now;
        }
        
        // Check if we've exceeded rate limit
        if (apiRequestCount >= 95) { // Leave some buffer below 100
            return true;
        }
        
        // Check minimum time between requests (600ms = ~100 requests per minute)
        if (now - lastApiRequest < 600) {
            return true;
        }
        
        return false;
    }
    
    async function fetchMarketData(itemId) {
        if (!apiKey) {
            throw new Error('API key not available');
        }
        
        try {
            // Add timeout for throttling wait but make it more generous
            let throttleAttempts = 0;
            const maxThrottleAttempts = 180; // Max 3 minutes of waiting (increased from 1 minute)
            
            // Wait for throttling if needed
            while (shouldThrottleApiRequest() && throttleAttempts < maxThrottleAttempts) {
                await sleep(1000);
                throttleAttempts++;
            }
            
            if (throttleAttempts >= maxThrottleAttempts) {
                throw new Error('Throttling timeout - API requests are being rate limited too heavily');
            }
            
            lastApiRequest = Date.now();
            apiRequestCount++;
            
            // Add timeout to the fetch request
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout
            
            try {
                const response = await fetch(`https://api.torn.com/v2/market/${itemId}/itemmarket?offset=0&key=${apiKey}`, {
                    signal: controller.signal
                });
                
                clearTimeout(timeoutId);
                
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
                }
                
                const data = await response.json();
                
                if (data.error) {
                    const errorCode = data.error.code;
                    const errorMessage = data.error.error;
                    
                    // Handle specific API errors
                    if (errorCode === 5) { // Too many requests
                        throw new Error('Rate limit exceeded. Please wait before making more requests.');
                    } else if (errorCode === 8) { // IP block
                        throw new Error('IP temporarily blocked due to abuse. Please wait.');
                    } else if (errorCode === 14) { // Daily limit
                        throw new Error('Daily API read limit reached.');
                    } else if (errorCode === 17) { // Backend error
                        throw new Error('Torn backend error. Please try again later.');
                    }
                    
                    throw new Error(`API Error ${errorCode}: ${errorMessage}`);
                }
                
                if (data.itemmarket && data.itemmarket.listings) {
                    const marketInfo = {
                        item: data.itemmarket.item,
                        listings: data.itemmarket.listings,
                        timestamp: Date.now()
                    };
                    
                    marketData.set(itemId.toString(), marketInfo);
                    saveMarketData();
                    return marketInfo;
                }
                
                return null;
                
            } catch (fetchError) {
                clearTimeout(timeoutId);
                if (fetchError.name === 'AbortError') {
                    throw new Error('Request timeout - API request took too long');
                }
                throw fetchError;
            }
            
        } catch (error) {
            // If it's a rate limiting error, we should back off
            if (error.message.includes('Rate limit') || error.message.includes('Too many requests')) {
                // Exponential backoff
                await sleep(Math.min(30000, 1000 * Math.pow(2, apiRequestCount % 5)));
            }
            throw error;
        }
    }
    
    // Process API request queue
    async function processApiQueue() {
        if (isProcessingQueue || apiRequestQueue.length === 0) {
            return;
        }
        
        isProcessingQueue = true;
        
        while (apiRequestQueue.length > 0) {
            const { itemId, resolve, reject } = apiRequestQueue.shift();
            
            try {
                const result = await fetchMarketData(itemId);
                resolve(result);
            } catch (error) {
                reject(error);
            }
            
            // Small delay between requests
            await sleep(100);
        }
        
        isProcessingQueue = false;
    }
    
    // Queue API request
    function queueMarketDataRequest(itemId, forceFresh = false) {
        return new Promise((resolve, reject) => {
            // Check if we already have cached data that's fresh (unless forcing fresh data)
            if (!forceFresh) {
                const cached = marketData.get(itemId.toString());
                if (cached && isMarketDataFresh(cached)) {
                    resolve(cached);
                    return;
                }
            }
            
            // Add to queue
            apiRequestQueue.push({ itemId, resolve, reject });
            
            // Start processing if not already running
            processApiQueue();
        });
    }
    
    // Calculate price statistics
    function calculatePriceStats(listings, filterOutliersEnabled = true) {
        if (!listings || listings.length === 0) {
            return null;
        }
        
        // Filter outliers if enabled
        const processedListings = filterOutliersEnabled ? filterOutliers(listings) : listings;
        
        if (processedListings.length === 0) {
            return null;
        }
        
        const prices = processedListings.map(l => l.price).sort((a, b) => a - b);
        const quantities = processedListings.reduce((sum, l) => sum + l.amount, 0);
        
        // Calculate weighted average
        const totalValue = processedListings.reduce((sum, l) => sum + (l.price * l.amount), 0);
        const weightedAverage = totalValue / quantities;
        
        // Calculate percentiles
        const p25Index = Math.floor(prices.length * 0.25);
        const p75Index = Math.floor(prices.length * 0.75);
        
        // Calculate seller-based statistics
        const uniquePrices = [...new Set(processedListings.map(l => l.price))];
        const originalUniquePrices = [...new Set(listings.map(l => l.price))];
        
        // Calculate filtering breakdown
        const priceCapFiltered = listings.filter(l => l.price > Math.min(...listings.map(p => p.price)) * 10);
        const statFiltered = listings.length - processedListings.length - priceCapFiltered.length;
        
        return {
            min: Math.min(...prices),
            max: Math.max(...prices),
            median: prices[Math.floor(prices.length / 2)],
            average: weightedAverage,
            p25: prices[p25Index],
            p75: prices[p75Index],
            listings: processedListings.length,
            totalListings: listings.length,
            uniqueSellers: uniquePrices.length,
            totalUniqueSellers: originalUniquePrices.length,
            totalQuantity: quantities,
            outliersFiltered: listings.length - processedListings.length,
            sellersFiltered: originalUniquePrices.length - uniquePrices.length,
            priceCapFiltered: priceCapFiltered.length,
            statFiltered: Math.max(0, statFiltered)
        };
    }
    
    // Create price distribution mini chart
    function createPriceChart(listings, width = 60, height = 20) {
        if (!listings || listings.length === 0) {
            return `<div style="width: ${width}px; height: ${height}px; background: #f0f0f0; display: flex; align-items: center; justify-content: center; font-size: 8px; color: #999;">No data</div>`;
        }
        
        const filteredListings = filterOutliers(listings);
        const stats = calculatePriceStats(filteredListings, false);
        if (!stats) return '';
        
        // Group prices into bins for visualization
        const bins = Math.min(10, filteredListings.length);
        const binWidth = (stats.max - stats.min) / bins;
        const binCounts = new Array(bins).fill(0);
        
        filteredListings.forEach(listing => {
            const binIndex = Math.min(Math.floor((listing.price - stats.min) / binWidth), bins - 1);
            binCounts[binIndex] += listing.amount;
        });
        
        const maxBinCount = Math.max(...binCounts);
        
        // Create SVG chart
        const bars = binCounts.map((count, index) => {
            const barHeight = maxBinCount > 0 ? (count / maxBinCount) * (height - 2) : 0;
            const x = (index * width) / bins;
            const barWidth = width / bins - 1;
            const y = height - barHeight;
            
            return `<rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" fill="#007bff" opacity="0.7"/>`;
        }).join('');
        
        return `
            <svg width="${width}" height="${height}" style="border: 1px solid #ddd;">
                ${bars}
            </svg>
        `;
    }
    
    // Create high-resolution popup chart with zoom functionality
    function createDetailedPriceChart(listings, itemName, zoomMin = null, zoomMax = null) {
        if (!listings || listings.length === 0) {
            return '<div style="padding: 20px;">No market data available</div>';
        }
        
        const filteredListings = filterOutliers(listings);
        const stats = calculatePriceStats(filteredListings, false);
        if (!stats) return '<div style="padding: 20px;">No valid price data</div>';
        
        const width = 300;
        const height = 200;
        const padding = 40;
        const chartWidth = width - padding * 2;
        const chartHeight = height - padding * 2;
        
        // Determine price range for chart (zoom or full range)
        const displayMin = zoomMin || stats.min;
        const displayMax = zoomMax || stats.max;
        const displayRange = displayMax - displayMin;
        const isZoomed = zoomMin !== null || zoomMax !== null;
        
        // Filter listings to zoom range if zooming
        const displayListings = isZoomed ? 
            filteredListings.filter(l => l.price >= displayMin && l.price <= displayMax) :
            filteredListings;
        
        if (displayListings.length === 0) {
            return '<div style="padding: 20px;">No data in selected range</div>';
        }
        
        // Create bins for histogram
        const bins = Math.min(20, displayListings.length);
        const binWidth = displayRange / bins;
        const binData = [];
        
        for (let i = 0; i < bins; i++) {
            binData.push({
                min: displayMin + i * binWidth,
                max: displayMin + (i + 1) * binWidth,
                count: 0,
                totalAmount: 0
            });
        }
        
        displayListings.forEach(listing => {
            const binIndex = Math.min(Math.floor((listing.price - displayMin) / binWidth), bins - 1);
            if (binIndex >= 0) {
                binData[binIndex].count++;
                binData[binIndex].totalAmount += listing.amount;
            }
        });
        
        const maxCount = Math.max(...binData.map(b => b.totalAmount));
        
        // Create SVG bars with selection capability
        const bars = binData.map((bin, index) => {
            const barHeight = maxCount > 0 ? (bin.totalAmount / maxCount) * chartHeight : 0;
            const x = padding + (index * chartWidth) / bins;
            const barWidth = chartWidth / bins - 2;
            const y = padding + chartHeight - barHeight;
            
            const midPrice = (bin.min + bin.max) / 2;
            
            return `
                <rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" 
                      fill="#007bff" opacity="0.7" stroke="#0056b3" stroke-width="1"
                      data-price-min="${bin.min}" data-price-max="${bin.max}" data-amount="${bin.totalAmount}"
                      class="chart-bar">
                </rect>
            `;
        }).join('');
        
        // Create axes
        const xAxis = `<line x1="${padding}" y1="${padding + chartHeight}" x2="${padding + chartWidth}" y2="${padding + chartHeight}" stroke="#666" stroke-width="1"/>`;
        const yAxis = `<line x1="${padding}" y1="${padding}" x2="${padding}" y2="${padding + chartHeight}" stroke="#666" stroke-width="1"/>`;
        
        // Create price labels
        const priceLabels = [displayMin, (displayMin + displayMax) / 2, displayMax].map((price, index) => {
            const x = padding + (index * chartWidth) / 2;
            return `<text x="${x}" y="${padding + chartHeight + 15}" text-anchor="middle" font-size="10" fill="#666">$${Math.round(price).toLocaleString()}</text>`;
        }).join('');
        
        // Create quantity labels
        const quantityLabels = [0, maxCount / 2, maxCount].map((qty, index) => {
            const y = padding + chartHeight - (index * chartHeight) / 2;
            return `<text x="${padding - 5}" y="${y + 3}" text-anchor="end" font-size="10" fill="#666">${Math.round(qty)}</text>`;
        }).join('');
        
        const outliersText = stats.outliersFiltered > 0 ? ` (${stats.outliersFiltered} high outliers filtered)` : '';
        const sellersText = stats.sellersFiltered > 0 ? ` | ${stats.sellersFiltered} sellers filtered` : '';
        const filterBreakdown = stats.priceCapFiltered > 0 || stats.statFiltered > 0 ? 
            ` (${stats.priceCapFiltered} 10x+ prices, ${stats.statFiltered} statistical outliers)` : '';
        const zoomText = (zoomMin !== null || zoomMax !== null) ? 
            ` | Zoomed: $${Math.round(displayMin).toLocaleString()}-$${Math.round(displayMax).toLocaleString()}` : '';
        
        // Selection overlay rectangle (initially hidden)
        const selectionOverlay = `
            <rect id="selection-rect" x="0" y="0" width="0" height="0" 
                  fill="rgba(0,123,255,0.2)" stroke="rgba(0,123,255,0.8)" 
                  stroke-width="1" style="display: none; pointer-events: none;"/>
        `;
        
        return `
            <div style="padding: 15px; background: white; border: 1px solid #ddd; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); position: relative; min-width: 320px; max-width: 400px;">
                <div>
                    <h4 style="margin: 0 0 10px 0; font-size: 14px; color: #333;">${itemName} - Market Analysis${zoomText}</h4>
                    <svg id="price-chart-svg" width="${width}" height="${height + 20}" style="background: white; cursor: crosshair; width: 100%; max-width: ${width}px;" 
                         data-padding="${padding}" data-chart-width="${chartWidth}" data-chart-height="${chartHeight}"
                         data-display-min="${displayMin}" data-display-max="${displayMax}">
                        ${bars}
                        ${xAxis}
                        ${yAxis}
                        ${priceLabels}
                        ${quantityLabels}
                        ${selectionOverlay}
                        <text x="${width / 2}" y="15" text-anchor="middle" font-size="12" font-weight="bold" fill="#333">Price Distribution</text>
                        <text x="15" y="${height / 2}" text-anchor="middle" font-size="10" fill="#666" transform="rotate(-90, 15, ${height / 2})">Quantity</text>
                        <text x="${width / 2}" y="${height + 15}" text-anchor="middle" font-size="10" fill="#666">Price ($)</text>
                    </svg>
                    <div style="font-size: 11px; margin-top: 10px; color: #666;">
                        <div><strong>Statistics:</strong></div>
                        <div>Average: $${Math.round(stats.average).toLocaleString()} | Median: $${stats.median.toLocaleString()}</div>
                        <div>Range: $${stats.min.toLocaleString()} - $${stats.max.toLocaleString()}</div>
                        <div>Q1: $${stats.p25.toLocaleString()} | Q3: $${stats.p75.toLocaleString()}</div>
                        <div>Listings: ${stats.listings}${outliersText} | Total Quantity: ${stats.totalQuantity.toLocaleString()}</div>
                        <div>Sellers: ${stats.uniqueSellers}/${stats.totalUniqueSellers}${sellersText}${filterBreakdown}</div>
                        ${(zoomMin !== null || zoomMax !== null) ? '<div style="margin-top: 5px;"><button id="reset-zoom" style="background: #6c757d; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 10px;">Reset Zoom</button></div>' : ''}
                    </div>
                    
                    <!-- Integrated listings section (always visible, mobile-friendly) -->
                    <div style="margin-top: 15px; border-top: 1px solid #eee; padding-top: 10px;">
                        <div style="font-weight: bold; font-size: 12px; margin-bottom: 8px; color: #333;">
                            Market Listings ${isZoomed ? '(Filtered)' : ''}
                        </div>
                        <div style="max-height: 200px; overflow-y: auto; border: 1px solid #eee; border-radius: 4px;">
                            ${createListingsTable(displayListings)}
                        </div>
                    </div>
                </div>
            </div>
        `;
    }
    
    // Create listings table for the expanded panel
    function createListingsTable(listings) {
        if (!listings || listings.length === 0) {
            return '<div style="padding: 10px; text-align: center; color: #666;">No listings available</div>';
        }
        
        // Sort listings by price (lowest first)
        const sortedListings = [...listings].sort((a, b) => a.price - b.price);
        
        // Group identical prices
        const priceGroups = new Map();
        sortedListings.forEach(listing => {
            const price = listing.price;
            if (!priceGroups.has(price)) {
                priceGroups.set(price, { 
                    price: price, 
                    totalQuantity: 0, 
                    listings: [] 
                });
            }
            const group = priceGroups.get(price);
            group.totalQuantity += listing.amount || 1; // Use amount, fallback to 1 if not available
            group.listings.push(listing);
        });
        
        let tableContent = `
            <table style="width: 100%; font-size: 11px; border-collapse: collapse;">
                <thead>
                    <tr style="background: #f8f9fa; border-bottom: 1px solid #dee2e6;">
                        <th style="padding: 4px 6px; text-align: left; border-right: 1px solid #dee2e6;">Qty</th>
                        <th style="padding: 4px 6px; text-align: right; border-right: 1px solid #dee2e6;">Price</th>
                        <th style="padding: 4px 6px; text-align: center;">Target</th>
                    </tr>
                </thead>
                <tbody>
        `;
        
        // Convert map to array and limit to first 50 entries for performance
        const groupArray = Array.from(priceGroups.values()).slice(0, 50);
        
        groupArray.forEach((group, index) => {
            const targetPrice = Math.max(1, group.price - 1); // $1 less, minimum $1
            const rowId = `price-row-${group.price}`;
            
            tableContent += `
                <tr style="border-bottom: 1px solid #eee; ${index % 2 === 0 ? 'background: #f9f9f9;' : ''}">
                    <td style="padding: 3px 6px; border-right: 1px solid #eee;">${group.totalQuantity.toLocaleString()}</td>
                    <td style="padding: 3px 6px; text-align: right; border-right: 1px solid #eee; font-weight: bold;">$${group.price.toLocaleString()}</td>
                    <td style="padding: 3px 6px; text-align: center;">
                        <div style="display: flex; align-items: center; justify-content: center; gap: 4px;">
                            <input type="checkbox" id="${rowId}" data-original-price="${group.price}" data-target-price="${targetPrice}" class="price-target-checkbox" style="margin: 0;">
                            <span style="font-size: 10px; color: #666;">$${targetPrice.toLocaleString()}</span>
                        </div>
                    </td>
                </tr>
            `;
        });
        
        if (priceGroups.size > 50) {
            tableContent += `
                <tr>
                    <td colspan="3" style="padding: 6px; text-align: center; color: #666; font-style: italic;">
                        ... and ${priceGroups.size - 50} more price points
                    </td>
                </tr>
            `;
        }
        
        tableContent += `
                </tbody>
            </table>
            <div style="padding: 8px; background: #f8f9fa; border-top: 1px solid #dee2e6; font-size: 10px; color: #666;">
                <div><strong>Selected Targets:</strong></div>
                <div id="selected-targets" style="margin-top: 4px; min-height: 16px;">None selected</div>
                <div style="margin-top: 6px;">
                    <button id="clear-targets-btn" style="background: #dc3545; color: white; border: none; padding: 2px 6px; border-radius: 2px; cursor: pointer; font-size: 10px;">Clear All</button>
                </div>
            </div>
        `;
        
        return tableContent;
    }
    
    // Extract item data from HTML element
    function extractItemData(element) {
        const itemId = element.getAttribute('data-item');
        const category = element.getAttribute('data-category') || 'Unknown';
        const qty = element.getAttribute('data-qty') || '1';
        const sort = element.getAttribute('data-sort') || '';
        const equipped = element.getAttribute('data-equipped') === 'true';
        const armoryId = element.getAttribute('data-armoryid');
        
        // Try to get item name from various sources
        let itemName = sort;
        if (!itemName) {
            const nameElement = element.querySelector('.name-wrap span, .item-name, .tooltip-wrap');
            if (nameElement) {
                itemName = nameElement.textContent.trim();
            }
        }
        
        // Clean up item name - remove quantity prefix if present (e.g., "1 Xanax" -> "Xanax")
        if (itemName) {
            // Remove leading numbers and spaces (quantity information)
            itemName = itemName.replace(/^\d+\s+/, '').trim();
        }
        
        // Fallback if still no name found
        if (!itemName) {
            itemName = `Item ${itemId}`;
        }
        
        return {
            id: itemId,
            name: itemName,
            category: category,
            quantity: parseInt(qty) || 1,
            equipped: equipped,
            armoryId: armoryId,
            tradeable: isItemTradeable(itemId),
            element: element
        };
    }
    
    // Scan currently visible items
    function scanVisibleItems() {
        // Try to find the currently active inventory tab/container
        let itemElements = [];
        const isMarketPage = window.location.href.includes('/page.php?sid=ItemMarket');
        
        if (isMarketPage) {
            // Market page: look for inventory items in the add listing interface
            const marketSelectors = [
                '.items-list li[data-item]',           // Market inventory list
                '.item-list li[data-item]',            // Alternative market inventory
                '.inventory-items li[data-item]',      // Market inventory container
                '[data-reactroot] li[data-item]',      // React-based inventory
                '.market-inventory li[data-item]',     // Market-specific inventory
                'li[data-item][data-category]:not([data-action])'  // Fallback to standard
            ];
            
            for (const selector of marketSelectors) {
                const elements = document.querySelectorAll(selector);
                if (elements.length > 0) {
                    itemElements = elements;
                    break;
                }
            }
        } else {
            // Inventory page: use existing logic
            // Check for all-items container first
            const allItemsContainer = document.querySelector('#all-items');
            if (allItemsContainer && allItemsContainer.style.display !== 'none') {
                itemElements = allItemsContainer.querySelectorAll('li[data-item][data-category]:not([data-action])');
            } else {
                // If not in all-items, look for any visible inventory container with items
                const inventoryContainers = document.querySelectorAll('.inventory-wrap, .category-wrap, [id*="items"]');
                for (const container of inventoryContainers) {
                    if (container.style.display !== 'none' && !container.hidden) {
                        const containerItems = container.querySelectorAll('li[data-item][data-category]:not([data-action])');
                        if (containerItems.length > 0) {
                            itemElements = containerItems;
                            break;
                        }
                    }
                }
                
                // Fallback: scan any visible items on the page
                if (itemElements.length === 0) {
                    itemElements = document.querySelectorAll('li[data-item][data-category]:not([data-action])');
                }
            }
        }
        
        if (itemElements.length === 0) {
            const pageType = isMarketPage ? 'market page' : 'inventory page';
            log(`No items found in current view on ${pageType}`);
            return 0;
        }
        
        let newItems = 0;
        itemElements.forEach(element => {
            const itemData = extractItemData(element);
            if (itemData.id) {
                const key = `${itemData.id}_${itemData.armoryId || 'base'}`;
                if (!scannedItems.has(key)) {
                    scannedItems.set(key, itemData);
                    newItems++;
                }
            }
        });
        
        const pageType = isMarketPage ? 'market page' : 'inventory page';
        log(`Scanned ${newItems} new items from ${pageType} (Total: ${scannedItems.size})`);
        
        // Save scanned items to localStorage
        if (newItems > 0) {
            saveScannedItems();
        }
        
        return newItems;
    }
    
    // Save scanned items to localStorage
    function saveScannedItems() {
        try {
            const itemsArray = Array.from(scannedItems.values());
            localStorage.setItem(STORAGE_KEY, JSON.stringify(itemsArray));
            log(`Saved ${itemsArray.length} items to localStorage`);
        } catch (error) {
            log(`Failed to save items: ${error.message}`);
        }
    }
    
    // Navigate to all-items tab
    async function navigateToAllItems() {
        const allItemsTab = document.querySelector('a[href="#all-items"], #ui-id-1');
        if (allItemsTab) {
            log('Clicking all-items tab');
            allItemsTab.click();
            await sleep(1000);
            return true;
        }
        
        // Check if we're already on all-items
        const allItemsContainer = document.querySelector('#all-items');
        if (allItemsContainer && allItemsContainer.classList.contains('ui-tabs-panel')) {
            log('Already on all-items tab');
            return true;
        }
        
        log('Could not find all-items tab');
        return false;
    }
    
    // Check for new items without auto-scrolling
    function checkForNewItems() {
        // Just scan what's currently visible without scrolling
        return scanVisibleItems();
    }
    
    // Handle market price fetching for filtered items only
    async function handleFetchFilteredPrices() {
        const marketBtn = document.getElementById('fetch-filtered-prices');
        const statusDiv = document.getElementById('scan-status');
        
        // Safety check - make sure button exists
        if (!marketBtn) {
            log('Error: fetch-filtered-prices button not found');
            return;
        }
        
        // Reset button state if it was stuck
        if (marketBtn.disabled && marketBtn.textContent !== 'Fetching...') {
            log('Resetting stuck button state');
            marketBtn.disabled = false;
            marketBtn.textContent = 'Get Filtered Prices';
        }
        
        // Prevent multiple simultaneous executions
        if (marketBtn.disabled) {
            log('Filtered price fetch already in progress');
            return;
        }
        
        if (!apiKey) {
            if (statusDiv) {
                statusDiv.innerHTML = '<span style="color: #dc3545;">Please save an API key first</span>';
            }
            return;
        }

        if (scannedItems.size === 0) {
            if (statusDiv) {
                statusDiv.innerHTML = '<span style="color: #dc3545;">No items to fetch prices for. Scan items first.</span>';
            }
            return;
        }

        // Get only the filtered/visible items
        const filteredItems = getFilteredItems();
        const tradeableFilteredItems = filteredItems.filter(item => item.tradeable === true);
        
        // Debug logging to help identify the issue
        const tradeableTrue = filteredItems.filter(item => item.tradeable === true);
        const tradeableFalse = filteredItems.filter(item => item.tradeable === false);
        const tradeableNull = filteredItems.filter(item => item.tradeable === null);
        
        log(`Filtered price fetch debug: Filtered items: ${filteredItems.length}, Tradeable (true): ${tradeableTrue.length}, Non-tradeable (false): ${tradeableFalse.length}, Unknown (null): ${tradeableNull.length}`);
        log(`API Items Data available: ${apiItemsData ? 'Yes (' + Object.keys(apiItemsData).length + ' items)' : 'No'}`);
        log(`API Key available: ${apiKey ? 'Yes' : 'No'}`);
        
        if (tradeableFilteredItems.length === 0) {
            let errorMessage = 'No tradeable items in current filter to fetch prices for';
            
            if (!apiKey) {
                errorMessage = 'No API key found. Please save your API key first, then scan items again.';
            } else if (!apiItemsData) {
                errorMessage = 'API item data not loaded. Please check your API key and try scanning items again.';
            } else if (tradeableNull.length > 0) {
                errorMessage = `Found ${tradeableNull.length} filtered items with unknown tradeable status. API data may be incomplete.`;
            }
            
            if (statusDiv) {
                statusDiv.innerHTML = `<span style="color: #ffc107;">${errorMessage}</span>`;
            }
            return;
        }

        log(`Starting filtered price fetch for ${tradeableFilteredItems.length} items`);
        log('First few items to process:', tradeableFilteredItems.slice(0, 3).map(item => `${item.name} (${item.id})`));

        // Reset any stuck queue state
        if (isProcessingQueue) {
            log('Resetting stuck queue state');
            isProcessingQueue = false;
            apiRequestQueue.length = 0;
        }

        // Reset API counters if they seem stuck
        const now = Date.now();
        if (now - apiRequestResetTime > 120000) { // If reset time is more than 2 minutes old
            log('Resetting API request counters');
            apiRequestCount = 0;
            apiRequestResetTime = now;
        }

        marketBtn.disabled = true;
        marketBtn.textContent = 'Fetching...';

        let processed = 0;
        let errors = 0;
        const total = tradeableFilteredItems.length;
        const startTime = Date.now();
        const maxProcessingTime = 1800000; // 30 minutes maximum

        try {
            for (const item of tradeableFilteredItems) {
                // Check for timeout
                if (Date.now() - startTime > maxProcessingTime) {
                    log('Filtered market price fetching timed out after 30 minutes');
                    if (statusDiv) {
                        statusDiv.innerHTML = '<span style="color: #ffc107;">Filtered market price fetching timed out after 30 minutes. Processed ' + processed + '/' + total + ' items.</span>';
                    }
                    break;
                }

                try {
                    if (statusDiv) {
                        statusDiv.innerHTML = `Fetching filtered market data: ${processed + 1}/${total} (${item.name})`;
                    }

                    log(`Making fresh API request for item ${processed + 1}/${total}: ${item.name} (ID: ${item.id})`);

                    // Always force fresh API requests for filtered prices
                    const requestPromise = queueMarketDataRequest(item.id, true); // Force fresh
                    const timeoutPromise = new Promise((_, reject) => {
                        setTimeout(() => reject(new Error('Individual request timeout')), 120000); // 2 minutes per request
                    });

                    await Promise.race([requestPromise, timeoutPromise]);
                    processed++;
                    
                    log(`Successfully fetched fresh data for: ${item.name}`);

                } catch (error) {
                    errors++;
                    log(`Error fetching market data for ${item.name}: ${error.message}`);
                    
                    // Continue processing other items even if one fails
                    if (error.message.includes('rate limit') || error.message.includes('timeout')) {
                        // For rate limit errors, wait longer before continuing
                        await sleep(5000); // 5 second pause for rate limits
                    } else {
                        await sleep(1000); // 1 second pause for other errors
                    }
                }
            }

            if (statusDiv) {
                const successRate = Math.round((processed / total) * 100);
                statusDiv.innerHTML = `<span style="color: #28a745;">Filtered market data fetch complete: ${processed}/${total} items (${successRate}% success)</span>`;
            }

            // Update table to show the new market data
            updateTableDisplay();

        } catch (error) {
            log(`Filtered market data fetch failed: ${error.message}`);
            if (statusDiv) {
                statusDiv.innerHTML = `<span style="color: #dc3545;">Filtered market data fetch failed: ${error.message}</span>`;
            }
        } finally {
            // Ensure button is always re-enabled
            if (marketBtn) {
                marketBtn.disabled = false;
                marketBtn.textContent = 'Get Filtered Prices';
            }
            
            // Reset queue state to prevent hanging
            isProcessingQueue = false;
            
            // Clear any pending status after 5 seconds
            setTimeout(() => {
                if (statusDiv && statusDiv.innerHTML.includes('Filtered market data fetch')) {
                    updateTableDisplay(); // This will restore the normal status display
                }
            }, 5000);
            
            log('Filtered price fetch completed and button state reset');
        }
    }
    
    // Function to update market button text with item count
    function updateMarketButtonText() {
        const marketBtn = document.getElementById('fetch-market-prices');
        if (marketBtn && scannedItems.size > 0) {
            marketBtn.textContent = `Get ${scannedItems.size} item prices`;
        } else if (marketBtn) {
            marketBtn.textContent = 'Get Market Prices';
        }
    }
    
    // Create floating table to display results
    function createFloatingTable() {
        if (floatingTable) {
            floatingTable.remove();
        }
        
        // Load saved window state
        const windowState = loadWindowState();
        
        floatingTable = document.createElement('div');
        floatingTable.id = 'inventory-scanner-table';
        floatingTable.style.position = 'fixed';
        floatingTable.style.left = windowState.x + 'px';
        floatingTable.style.top = windowState.y + 'px';
        floatingTable.style.width = windowState.width || WINDOW_DEFAULT_WIDTH;
        floatingTable.style.height = windowState.height || WINDOW_DEFAULT_HEIGHT;
        floatingTable.style.minWidth = WINDOW_MIN_WIDTH;
        floatingTable.style.minHeight = WINDOW_MIN_HEIGHT;
        floatingTable.style.maxWidth = '90vw';
        floatingTable.style.maxHeight = '90vh';
        floatingTable.style.background = '#fff';
        floatingTable.style.border = '2px solid #333';
        floatingTable.style.borderRadius = '8px';
        floatingTable.style.zIndex = '100000';
        floatingTable.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)';
        floatingTable.style.resize = 'both';
        floatingTable.style.overflow = 'hidden';
        floatingTable.innerHTML = `
                <div id="header-drag" class="title-bar" style="background: #333; color: #fff; padding: 10px; border-radius: 6px 6px 0 0; cursor: move; display: flex; justify-content: space-between; align-items: center; user-select: none;">
                    <span id="window-title" style="cursor: pointer; flex: 1;">Scrap2Mark</span>
                    <div style="display: flex; align-items: center;">
                        <button id="close-table" style="background: none; border: none; color: #fff; cursor: pointer; font-size: 14px; line-height: 1; padding: 2px;">✕</button>
                    </div>
                </div>
                <div id="table-content" style="padding: 10px; height: calc(100% - 50px); overflow-y: auto; display: flex; flex-direction: column;">
                    <div id="api-controls" style="margin-bottom: 10px; padding: 6px; background: #f8f9fa; border-radius: 4px;">
                        <div style="display: flex; gap: 6px; align-items: center;">
                            <input type="text" id="api-key-input" placeholder="Insert API key here" autocomplete="on" name="api_key" style="flex: 1; padding: 4px 6px; border: 1px solid #ccc; border-radius: 3px; font-size: 11px; font-family: monospace;">
                            <button id="save-api-key" style="background: #17a2b8; color: white; border: none; padding: 4px 12px; border-radius: 3px; cursor: pointer; font-size: 11px; white-space: nowrap;">Save</button>
                            <button id="logout-api-key" style="background: #dc3545; color: white; border: none; padding: 4px 12px; border-radius: 3px; cursor: pointer; font-size: 11px; white-space: nowrap; display: none;">Logout</button>
                            <button id="fetch-api-data" style="background: #ffc107; color: black; border: none; padding: 4px 12px; border-radius: 3px; cursor: pointer; font-size: 11px; white-space: nowrap; display: none;">Fetch</button>
                        </div>
                        <div style="font-size: 9px; color: #666; min-height: 12px; margin-top: 4px;">
                            <span id="api-status"></span>
                        </div>
                    </div>
                    <div style="margin-bottom: 10px; padding: 6px; background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 4px;">
                        <div style="display: flex; gap: 6px; align-items: center; justify-content: space-between;">
                            <span style="font-size: 10px; color: #856404;">
                                <strong>Having issues?</strong> Reset clears all stored data and settings.
                            </span>
                            <button id="full-reset-btn" style="background: #dc3545; color: white; border: none; padding: 4px 10px; border-radius: 3px; cursor: pointer; font-size: 10px; font-weight: bold;">Full Reset</button>
                        </div>
                    </div>
                    <div id="scan-controls" style="margin-bottom: 10px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center;">
                        <!-- Scanning Group -->
                        <div style="display: flex; gap: 3px; margin-right: 12px;">
                            <button id="start-scan" style="background: linear-gradient(135deg, #28a745, #20c997); color: white; border: none; padding: 6px 14px; border-radius: 6px; cursor: pointer; height: 32px; font-weight: 500; box-shadow: 0 2px 4px rgba(40,167,69,0.3); transition: all 0.2s ease;">Scan Inventory</button>
                            <button id="clear-data" style="background: linear-gradient(135deg, #dc3545, #e74c3c); color: white; border: none; padding: 6px 8px; border-radius: 6px; cursor: pointer; height: 32px; font-size: 12px; box-shadow: 0 2px 4px rgba(220,53,69,0.3); transition: all 0.2s ease;">✗</button>
                        </div>
                        
                        <!-- Price Fetching Group -->
                        <div style="display: flex; gap: 3px; margin-right: 12px;">
                            <button id="fetch-market-prices" style="background: linear-gradient(135deg, #ffc107, #ffb300); color: black; border: none; padding: 6px 14px; border-radius: 6px; cursor: pointer; height: 32px; font-weight: 500; box-shadow: 0 2px 4px rgba(255,193,7,0.3); transition: all 0.2s ease;">Get Market Prices</button>
                            <button id="clear-market-cache" style="background: linear-gradient(135deg, #dc3545, #e74c3c); color: white; border: none; padding: 6px 8px; border-radius: 6px; cursor: pointer; height: 32px; font-size: 12px; box-shadow: 0 2px 4px rgba(220,53,69,0.3); transition: all 0.2s ease;">✗</button>
                        </div>
                        
                        <div style="display: flex; gap: 3px; margin-right: 12px;">
                            <button id="fetch-filtered-prices" style="background: linear-gradient(135deg, #6f42c1, #8e44ad); color: white; border: none; padding: 6px 14px; border-radius: 6px; cursor: pointer; height: 32px; font-weight: 500; box-shadow: 0 2px 4px rgba(111,66,193,0.3); transition: all 0.2s ease;">Get Filtered Prices</button>
                        </div>
                        
                        <!-- Market Actions Group -->
                        <div style="display: flex; gap: 3px;">
                            <button id="post-market-button" style="background: linear-gradient(135deg, #17a2b8, #3498db); color: white; border: none; padding: 6px 14px; border-radius: 6px; cursor: pointer; height: 32px; font-weight: 500; box-shadow: 0 2px 4px rgba(23,162,184,0.3); transition: all 0.2s ease;">Fill Market Forms</button>
                            <button id="clear-price-targets" style="background: linear-gradient(135deg, #dc3545, #e74c3c); color: white; border: none; padding: 6px 8px; border-radius: 6px; cursor: pointer; height: 32px; font-size: 12px; box-shadow: 0 2px 4px rgba(220,53,69,0.3); transition: all 0.2s ease;">✗</button>
                        </div>
                    </div>
                    <div id="scroll-reminder" style="margin-bottom: 10px; font-size: 11px; color: #666; background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 4px; padding: 8px;">
                        <div style="display: flex; align-items: center; gap: 8px;">
                            <span style="color: #856404; font-weight: bold;">⚠️ IMPORTANT:</span>
                            <span style="color: #856404;">Scroll down to load ALL items before using the tools</span>
                        </div>
                        <div style="margin-top: 4px; font-size: 10px; color: #6c6c6c;">
                            <strong>Loading Progress:</strong> <span id="loading-progress">Checking...</span> | 
                            <strong>Tip:</strong> Keep scrolling until no new items appear.
                        </div>
                    </div>
                    <div id="scan-status" style="margin-bottom: 10px; font-weight: bold;"></div>
                    <div style="flex: 1; overflow-y: auto; min-height: 200px;">
                        <div style="margin-bottom: 10px;">
                            <div style="display: flex; align-items: center; margin-bottom: 5px; flex-wrap: wrap; gap: 10px;">
                                <h4 style="margin: 0; font-size: 14px;">Scanned Items:</h4>
                                <label style="display: flex; align-items: center; font-size: 12px; cursor: pointer;">
                                    <input type="checkbox" id="show-non-tradeable" style="margin-right: 4px;">
                                    Show non-tradeable
                                </label>
                                <label style="display: flex; align-items: center; font-size: 12px; cursor: pointer;">
                                    <input type="checkbox" id="show-ignored" style="margin-right: 4px;">
                                    Show ignored items
                                </label>
                                <label style="display: flex; align-items: center; font-size: 12px; cursor: pointer;">
                                    <input type="checkbox" id="hide-low-value" checked style="margin-right: 4px;">
                                    Hide below $
                                    <input type="text" id="min-value-input" value="5,000,000" style="width: 70px; margin-left: 4px; padding: 2px; border: 1px solid #ccc; border-radius: 2px; font-size: 11px;" placeholder="5000000">
                                </label>
                            </div>
                            <table id="all-items-table" style="width: 100%; border-collapse: collapse; font-size: 12px; table-layout: fixed;">
                                <thead>
                                    <tr style="background: #f8f9fa; border-bottom: 2px solid #dee2e6;">
                                        <th id="header-name" style="padding: 8px; text-align: left; border: 1px solid #dee2e6; cursor: pointer; user-select: none;">
                                            Name <span id="sort-name" style="float: right;">↕</span>
                                        </th>
                                        <th id="header-category" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 80px; cursor: pointer; user-select: none;">
                                            Category <span id="sort-category" style="float: right;">↕</span>
                                        </th>
                                        <th id="header-qty" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 40px; cursor: pointer; user-select: none;">
                                            Qty <span id="sort-qty" style="float: right;">↕</span>
                                        </th>
                                        <th id="trade-column-header" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 50px; cursor: pointer; user-select: none; display: none;">
                                            Trade <span id="sort-trade" style="float: right;">↕</span>
                                        </th>
                                        <th id="header-price" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 70px; cursor: pointer; user-select: none;">
                                            Price <span id="sort-price" style="float: right;">↕</span>
                                        </th>
                                        <th id="header-estimated" style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 90px; cursor: pointer; user-select: none;">
                                            Est. Value <span id="sort-estimated" style="float: right;">↕</span>
                                        </th>
                                        <th style="padding: 8px; text-align: center; border: 1px solid #dee2e6; width: 50px;">
                                            Action
                                        </th>
                                    </tr>
                                </thead>
                                <tbody id="items-table-body">
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        `;
        
        document.body.appendChild(floatingTable);
        
        // Make draggable
        let isDragging = false;
        let dragStarted = false;
        let currentX, currentY, initialX, initialY;
        const header = document.getElementById('header-drag');
        const windowTitle = document.getElementById('window-title');
        
        function startDrag(e) {
            e.preventDefault();
            isDragging = true;
            dragStarted = false;
            
            // Get the current position of the floating table
            const rect = floatingTable.getBoundingClientRect();
            
            // Calculate offset from mouse to top-left corner of element
            initialX = e.clientX - rect.left;
            initialY = e.clientY - rect.top;
            
            document.addEventListener('mousemove', doDrag);
            document.addEventListener('mouseup', stopDrag);
        }
        
        function doDrag(e) {
            if (!isDragging) return;
            e.preventDefault();
            
            // Mark that dragging has actually started (mouse moved)
            if (!dragStarted) {
                dragStarted = true;
            }
            
            currentX = e.clientX - initialX;
            currentY = e.clientY - initialY;
            
            // Keep the window within the viewport
            const maxX = window.innerWidth - floatingTable.offsetWidth;
            const maxY = window.innerHeight - floatingTable.offsetHeight;
            
            currentX = Math.max(0, Math.min(currentX, maxX));
            currentY = Math.max(0, Math.min(currentY, maxY));
            
            floatingTable.style.left = currentX + 'px';
            floatingTable.style.top = currentY + 'px';
            floatingTable.style.right = 'auto'; // Remove right positioning when dragging
        }
        
        function stopDrag() {
            isDragging = false;
            document.removeEventListener('mousemove', doDrag);
            document.removeEventListener('mouseup', stopDrag);
            
            // Save position when drag ends (but keep existing dimensions)
            const rect = floatingTable.getBoundingClientRect();
            saveWindowState(rect.left, rect.top, windowState.width || WINDOW_DEFAULT_WIDTH, windowState.height || WINDOW_DEFAULT_HEIGHT, isMinimized());
        }
        
        function handleTitleClick(e) {
            // Only toggle minimize if we didn't actually drag
            if (!dragStarted) {
                toggleMinimize();
            }
        }
        
        function handleHeaderClick(e) {
            // Only toggle minimize if we didn't actually drag and didn't click on close button
            if (!dragStarted && e.target.id !== 'close-table') {
                toggleMinimize();
            }
        }
        
        function toggleMinimize() {
            const content = document.getElementById('table-content');
            if (content.style.display === 'none') {
                // Restoring from minimized state
                content.style.display = 'flex';
                floatingTable.style.width = windowState.width || WINDOW_DEFAULT_WIDTH;
                floatingTable.style.height = windowState.height || WINDOW_DEFAULT_HEIGHT;
                floatingTable.style.resize = 'both';
                // Restore normal constraints
                floatingTable.style.minWidth = WINDOW_MIN_WIDTH;
                floatingTable.style.minHeight = WINDOW_MIN_HEIGHT;
                floatingTable.style.maxWidth = '90vw';
                floatingTable.style.maxHeight = '90vh';
            } else {
                // Minimizing
                content.style.display = 'none';
                floatingTable.style.width = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.height = WINDOW_MINIMIZED_HEIGHT;
                floatingTable.style.resize = 'none';
                // Override constraints for minimized state
                floatingTable.style.minWidth = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.minHeight = WINDOW_MINIMIZED_HEIGHT;
                floatingTable.style.maxWidth = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.maxHeight = WINDOW_MINIMIZED_HEIGHT;
            }
            
            // Save minimized state
            const rect = floatingTable.getBoundingClientRect();
            saveWindowState(rect.left, rect.top, windowState.width || WINDOW_DEFAULT_WIDTH, windowState.height || WINDOW_DEFAULT_HEIGHT, content.style.display === 'none');
        }
        
        function isMinimized() {
            const content = document.getElementById('table-content');
            return content && content.style.display === 'none';
        }
        
        header.addEventListener('mousedown', startDrag);
        header.addEventListener('mouseup', handleHeaderClick);
        
        // Restore minimized state
        if (windowState.minimized) {
            const content = document.getElementById('table-content');
            if (content) {
                content.style.display = 'none';
                floatingTable.style.width = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.height = WINDOW_MINIMIZED_HEIGHT;
                floatingTable.style.resize = 'none';
                // Force the size override any stored dimensions
                floatingTable.style.minWidth = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.minHeight = WINDOW_MINIMIZED_HEIGHT;
                floatingTable.style.maxWidth = WINDOW_MINIMIZED_WIDTH;
                floatingTable.style.maxHeight = WINDOW_MINIMIZED_HEIGHT;
            }
        } else {
            // Ensure resize is enabled for normal state and restore constraints
            floatingTable.style.resize = 'both';
            floatingTable.style.minWidth = WINDOW_MIN_WIDTH;
            floatingTable.style.minHeight = WINDOW_MIN_HEIGHT;
            floatingTable.style.maxWidth = '90vw';
            floatingTable.style.maxHeight = '90vh';
        }
        
        // Add resize observer to save window state when resized
        if (window.ResizeObserver) {
            const resizeObserver = new ResizeObserver((entries) => {
                for (let entry of entries) {
                    const rect = entry.target.getBoundingClientRect();
                    
                    // Only update dimensions if not minimized
                    if (!isMinimized()) {
                        windowState.width = rect.width + 'px';
                        windowState.height = rect.height + 'px';
                        // Debounce the save operation
                        clearTimeout(floatingTable.resizeTimeout);
                        floatingTable.resizeTimeout = setTimeout(() => {
                            saveWindowState(rect.left, rect.top, rect.width + 'px', rect.height + 'px', isMinimized());
                        }, 500);
                    }
                }
            });
            resizeObserver.observe(floatingTable);
        }
        
        // Event listeners
        document.getElementById('close-table').addEventListener('click', () => {
            floatingTable.remove();
            floatingTable = null;
        });
        
        document.getElementById('start-scan').addEventListener('click', startInventoryScan);
        document.getElementById('clear-data').addEventListener('click', () => {
            if (confirm('Clear all scanned inventory data? This cannot be undone.')) {
                clearInventoryData();
            }
        });
        document.getElementById('post-market-button').addEventListener('click', handlePostToMarket);
        document.getElementById('save-api-key').addEventListener('click', handleSaveApiKey);
        document.getElementById('logout-api-key').addEventListener('click', handleLogoutApiKey);
        document.getElementById('fetch-api-data').addEventListener('click', handleFetchApiData);
        document.getElementById('show-non-tradeable').addEventListener('change', () => {
            updateTableDisplay();
            saveFilterState(getCurrentFilterState());
        });
        document.getElementById('show-ignored').addEventListener('change', () => {
            updateTableDisplay();
            saveFilterState(getCurrentFilterState());
        });
        document.getElementById('hide-low-value').addEventListener('change', () => {
            updateTableDisplay();
            saveFilterState(getCurrentFilterState());
        });
        document.getElementById('min-value-input').addEventListener('input', () => {
            updateTableDisplay();
            saveFilterState(getCurrentFilterState());
        });
        document.getElementById('fetch-market-prices').addEventListener('click', handleFetchMarketPrices);
        
        // Add event listener for filtered prices button with retry mechanism
        const attachFilteredPricesListener = () => {
            const filteredPricesBtn = document.getElementById('fetch-filtered-prices');
            if (filteredPricesBtn) {
                filteredPricesBtn.addEventListener('click', handleFetchFilteredPrices);
                log('Event listener attached to fetch-filtered-prices button');
                return true;
            } else {
                log('fetch-filtered-prices button not found, retrying...');
                return false;
            }
        };
        
        // Try to attach immediately, then retry if needed
        if (!attachFilteredPricesListener()) {
            // If button not found, try again after a short delay
            setTimeout(() => {
                if (!attachFilteredPricesListener()) {
                    log('Warning: Could not attach event listener to fetch-filtered-prices button');
                }
            }, 100);
        }
        
        document.getElementById('clear-market-cache').addEventListener('click', () => {
            if (confirm('Clear market price cache? This will remove all cached market data.')) {
                handleClearMarketCache();
            }
        });
        document.getElementById('clear-price-targets').addEventListener('click', () => {
            if (confirm('Clear all price targets? This will remove all selected prices for market posting.')) {
                clearAllPriceTargets();
            }
        });
        
        // Full reset button
        document.getElementById('full-reset-btn').addEventListener('click', handleFullReset);
        
        // Add column header event listeners for sorting
        document.getElementById('header-name').addEventListener('click', () => sortTableDynamic('name'));
        document.getElementById('header-category').addEventListener('click', () => sortTableDynamic('category'));
        document.getElementById('header-qty').addEventListener('click', () => sortTableDynamic('qty'));
        document.getElementById('trade-column-header').addEventListener('click', () => sortTableDynamic('trade'));
        document.getElementById('header-price').addEventListener('click', () => sortTableDynamic('price'));
        document.getElementById('header-estimated').addEventListener('click', () => sortTableDynamic('estimated'));
        
        // Load ignored items from storage
        loadIgnoredItems();
        
        // Load and apply filter state
        const filterState = loadFilterState();
        applyFilterState(filterState);
        
        // Update scroll reminder initially
        updateScrollReminder();
        
        // Set up periodic reminder updates when user scrolls
        let scrollTimeout;
        window.addEventListener('scroll', () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = setTimeout(updateScrollReminder, 1000); // Update 1 second after scrolling stops
        });
        
        updateTableDisplay();
    }
    
    // Handle API key saving
    function handleSaveApiKey() {
        const apiKeyInput = document.getElementById('api-key-input');
        const apiStatus = document.getElementById('api-status');
        
        const key = apiKeyInput.value.trim();
        if (!key) {
            apiStatus.textContent = 'Please enter an API key';
            apiStatus.style.color = '#dc3545';
            return;
        }
        
        saveApiKey(key);
        apiKeyInput.value = '';
        apiStatus.textContent = 'API key saved!';
        apiStatus.style.color = '#28a745';
        
        // Update button visibility
        updateApiButtonVisibility();
        
        // Auto-fetch API data if needed (after saving new API key)
        setTimeout(() => {
            const lastFetch = localStorage.getItem('scrap2mark_last_api_fetch');
            const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);
            
            if (!lastFetch || parseInt(lastFetch) < oneDayAgo || !apiItemsData) {
                log('Auto-fetching API data after API key save');
                handleFetchApiData().then(() => {
                    localStorage.setItem('scrap2mark_last_api_fetch', Date.now().toString());
                }).catch(error => {
                    log('Auto-fetch failed after API key save: ' + error.message);
                });
            } else {
                log('API data is recent, skipping auto-fetch');
            }
        }, 1000); // Small delay to let the UI update
        
        setTimeout(() => {
            updateApiStatusDisplay();
        }, 3000);
    }
    
    // Handle API key logout
    function handleLogoutApiKey() {
        // Clear API key from storage
        localStorage.removeItem(API_KEY_STORAGE_KEY);
        localStorage.removeItem(API_KEY_EXPIRY_STORAGE_KEY);
        apiKey = null;
        
        const apiStatus = document.getElementById('api-status');
        apiStatus.textContent = 'Logged out successfully';
        apiStatus.style.color = '#28a745';
        
        // Update button visibility
        updateApiButtonVisibility();
        
        setTimeout(() => {
            updateApiStatusDisplay();
        }, 3000);
    }
    
    // Update API button visibility based on key status
    function updateApiButtonVisibility() {
        const saveBtn = document.getElementById('save-api-key');
        const logoutBtn = document.getElementById('logout-api-key');
        const apiKeyInput = document.getElementById('api-key-input');
        
        if (apiKey) {
            saveBtn.style.display = 'none';
            logoutBtn.style.display = 'block';
            if (apiKeyInput) {
                apiKeyInput.placeholder = 'API key is saved';
                apiKeyInput.disabled = true;
                apiKeyInput.style.backgroundColor = '#f8f9fa';
                apiKeyInput.style.color = '#6c757d';
            }
        } else {
            saveBtn.style.display = 'block';
            logoutBtn.style.display = 'none';
            if (apiKeyInput) {
                apiKeyInput.placeholder = 'Insert API key here';
                apiKeyInput.disabled = false;
                apiKeyInput.style.backgroundColor = '#fff';
                apiKeyInput.style.color = '#000';
            }
        }
    }
    
    // Handle API data fetching
    async function handleFetchApiData() {
        const apiStatus = document.getElementById('api-status');
        const fetchBtn = document.getElementById('fetch-api-data');
        
        if (!apiKey) {
            if (apiStatus) {
                apiStatus.textContent = 'Please save an API key first';
                apiStatus.style.color = '#dc3545';
            }
            log('Cannot fetch API data: No API key available');
            return;
        }
        
        // Only disable button if it exists (auto-fetch may not have button)
        if (fetchBtn) {
            fetchBtn.disabled = true;
        }
        
        if (apiStatus) {
            apiStatus.textContent = 'Fetching items data...';
            apiStatus.style.color = '#17a2b8';
        }
        
        log('Starting API data fetch...');
        
        try {
            await fetchItemsFromAPI(apiKey);
            
            const itemCount = apiItemsData ? Object.keys(apiItemsData).length : 0;
            log(`API data fetch successful: ${itemCount} items loaded`);
            
            if (apiStatus) {
                apiStatus.textContent = `Items data fetched successfully! (${itemCount} items)`;
                apiStatus.style.color = '#28a745';
            }
            
            // Update display if we have scanned items
            updateTableDisplay();
            
        } catch (error) {
            log(`Failed to fetch API data: ${error.message}`);
            if (apiStatus) {
                apiStatus.textContent = `Error: ${error.message}`;
                apiStatus.style.color = '#dc3545';
            }
        } finally {
            if (fetchBtn) {
                fetchBtn.disabled = false;
            }
            
            // Clear status message after delay (only if it's not an error)
            if (apiStatus && !apiStatus.textContent.includes('Error:')) {
                setTimeout(() => {
                    updateApiStatusDisplay();
                }, 5000);
            }
        }
    }
    
    // Handle market price fetching
    async function handleFetchMarketPrices() {
        const marketBtn = document.getElementById('fetch-market-prices');
        const statusDiv = document.getElementById('scan-status');
        
        if (!apiKey) {
            if (statusDiv) {
                statusDiv.innerHTML = '<span style="color: #dc3545;">Please save an API key first</span>';
            }
            return;
        }
        
        if (scannedItems.size === 0) {
            if (statusDiv) {
                statusDiv.innerHTML = '<span style="color: #dc3545;">No items to fetch prices for. Scan items first.</span>';
            }
            return;
        }
        
        // Reset any stuck queue state
        if (isProcessingQueue) {
            log('Resetting stuck queue state');
            isProcessingQueue = false;
            apiRequestQueue.length = 0; // Clear any pending requests
        }
        
        // Reset API counters if they seem stuck
        const now = Date.now();
        if (now - apiRequestResetTime > 120000) { // If reset time is more than 2 minutes old
            log('Resetting API request counters');
            apiRequestCount = 0;
            apiRequestResetTime = now;
        }
        
        marketBtn.disabled = true;
        marketBtn.textContent = 'Fetching...';
        
        const tradeableItems = Array.from(scannedItems.values()).filter(item => item.tradeable === true);
        
        // Debug logging to help identify the issue
        const allItems = Array.from(scannedItems.values());
        const tradeableTrue = allItems.filter(item => item.tradeable === true);
        const tradeableFalse = allItems.filter(item => item.tradeable === false);
        const tradeableNull = allItems.filter(item => item.tradeable === null);
        
        log(`Market price fetch debug: Total items: ${allItems.length}, Tradeable (true): ${tradeableTrue.length}, Non-tradeable (false): ${tradeableFalse.length}, Unknown (null): ${tradeableNull.length}`);
        log(`API Items Data available: ${apiItemsData ? 'Yes (' + Object.keys(apiItemsData).length + ' items)' : 'No'}`);
        log(`API Key available: ${apiKey ? 'Yes' : 'No'}`);
        
        if (tradeableItems.length === 0) {
            let errorMessage = 'No tradeable items found to fetch prices for';
            
            if (!apiKey) {
                errorMessage = 'No API key found. Please save your API key first, then scan items again.';
            } else if (!apiItemsData) {
                errorMessage = 'API item data not loaded. Please check your API key and try scanning items again.';
            } else if (tradeableNull.length > 0) {
                errorMessage = `Found ${tradeableNull.length} items with unknown tradeable status. API data may be incomplete.`;
            }
            
            if (statusDiv) {
                statusDiv.innerHTML = `<span style="color: #ffc107;">${errorMessage}</span>`;
            }
            marketBtn.disabled = false;
            updateMarketButtonText();
            return;
        }
        
        let processed = 0;
        let errors = 0;
        const total = tradeableItems.length;
        const startTime = Date.now();
        const maxProcessingTime = 1800000; // 30 minutes maximum (increased from 5 minutes)
        
        try {
            for (let i = 0; i < tradeableItems.length; i++) {
                const item = tradeableItems[i];
                
                // Check for timeout (but much more generous now)
                if (Date.now() - startTime > maxProcessingTime) {
                    log('Market price fetching timed out after 30 minutes');
                    if (statusDiv) {
                        statusDiv.innerHTML = '<span style="color: #ffc107;">Market price fetching timed out after 30 minutes. Processed ' + processed + '/' + total + ' items.</span>';
                    }
                    break;
                }
                
                try {
                    if (statusDiv) {
                        statusDiv.innerHTML = `Fetching market data: ${processed + 1}/${total} (${item.name})`;
                    }
                    
                    // Add timeout for individual requests (increased to 2 minutes)
                    const requestPromise = queueMarketDataRequest(item.id);
                    const timeoutPromise = new Promise((_, reject) => {
                        setTimeout(() => reject(new Error('Individual request timeout')), 120000); // 2 minutes per request
                    });
                    
                    await Promise.race([requestPromise, timeoutPromise]);
                    processed++;
                    
                    // Yield control to UI every 10 items to prevent freezing
                    if (i % 10 === 0 && i > 0) {
                        await new Promise(resolve => setTimeout(resolve, 10));
                    }
                    
                } catch (error) {
                    errors++;
                    log(`Failed to fetch market data for item ${item.id}: ${error.message}`);
                    
                    // Only stop on critical errors that won't recover
                    if (error.message.includes('Daily API read limit') ||
                        error.message.includes('IP temporarily blocked')) {
                        log('Critical API error encountered, stopping fetch process');
                        if (statusDiv) {
                            statusDiv.innerHTML = `<span style="color: #dc3545;">Critical API error: ${error.message}. Processed ${processed}/${total} items.</span>`;
                        }
                        break;
                    }
                    
                    // For rate limiting, wait a bit longer but continue
                    if (error.message.includes('Rate limit') || error.message.includes('Throttling timeout')) {
                        log('Rate limit hit, waiting 30 seconds before continuing...');
                        if (statusDiv) {
                            statusDiv.innerHTML = `Rate limited. Waiting 30s... (${processed}/${total} completed)`;
                        }
                        await sleep(30000); // Wait 30 seconds
                    }
                }
                
                // Update table periodically
                if (processed % 5 === 0) {
                    updateTableDisplay();
                }
            }
            
            updateTableDisplay();
            
            if (statusDiv) {
                if (errors > 0) {
                    statusDiv.innerHTML = `<span style="color: #ffc107;">Market data fetch completed: ${processed}/${total} successful, ${errors} errors</span>`;
                } else {
                    statusDiv.innerHTML = `<span style="color: #28a745;">Market data fetch completed: ${processed}/${total} items processed</span>`;
                }
            }
            
        } catch (error) {
            log(`Market data fetch failed: ${error.message}`);
            if (statusDiv) {
                statusDiv.innerHTML = `<span style="color: #dc3545;">Market data fetch failed: ${error.message}</span>`;
            }
        } finally {
            marketBtn.disabled = false;
            updateMarketButtonText();
            
            // Reset queue state to prevent hanging
            isProcessingQueue = false;
            
            setTimeout(() => {
                if (statusDiv && statusDiv.innerHTML.includes('Market data fetch')) {
                    updateTableDisplay(); // This will restore the normal status display
                }
            }, 5000);
        }
    }

    // Reset API state (useful for debugging hanging issues)
    function resetApiState() {
        log('Resetting API state');
        isProcessingQueue = false;
        apiRequestQueue.length = 0;
        apiRequestCount = 0;
        apiRequestResetTime = Date.now();
        lastApiRequest = 0;
    }
    
    // Clear sort state
    function clearSortState() {
        currentSortColumn = -1;
        currentSortDirection = 'asc';
        currentSortField = null;
        
        // Clear all sort indicators
        ['name', 'category', 'qty', 'trade', 'price', 'estimated'].forEach(col => {
            const indicator = document.getElementById(`sort-${col}`);
            if (indicator) indicator.textContent = '↕';
        });
    }
    
    // Handle clear market cache
    function handleClearMarketCache() {
        const clearBtn = document.getElementById('clear-market-cache');
        const statusDiv = document.getElementById('scan-status');
        
        // Clear market data from memory and storage
        marketData.clear();
        localStorage.removeItem(MARKET_DATA_STORAGE_KEY);
        localStorage.removeItem(MARKET_EXPIRY_STORAGE_KEY);
        
        // Also reset API state to prevent any stuck conditions
        resetApiState();
        
        // Update display
        updateTableDisplay();
        
        if (statusDiv) {
            statusDiv.innerHTML = '<span style="color: #28a745;">Market cache cleared successfully</span>';
            setTimeout(() => {
                updateTableDisplay(); // This will restore the normal status display
            }, 3000);
        }
        
        log('Market cache cleared and API state reset');
    }
    
    // Handle full reset - clears all stored data and settings
    function handleFullReset() {
        const confirmMessage = `⚠️ FULL RESET WARNING ⚠️

This will permanently delete ALL stored data:
• Scanned items and inventory data
• API key and cached API data  
• Market price cache and settings
• Ignored items list
• Window position and filter settings
• Price targets and selections

You will need to:
1. Re-enter your API key
2. Re-scan your inventory
3. Re-configure your preferences

This action CANNOT be undone!

Type "RESET" (all caps) to confirm:`;

        const userInput = prompt(confirmMessage);
        
        if (userInput === "RESET") {
            try {
                // Clear all localStorage data related to the script
                const keysToRemove = [
                    STORAGE_KEY,
                    API_KEY_STORAGE_KEY,
                    API_DATA_STORAGE_KEY,
                    API_EXPIRY_STORAGE_KEY,
                    API_KEY_EXPIRY_STORAGE_KEY,
                    WINDOW_STATE_STORAGE_KEY,
                    MARKET_DATA_STORAGE_KEY,
                    MARKET_EXPIRY_STORAGE_KEY,
                    IGNORED_ITEMS_STORAGE_KEY,
                    FILTER_STATE_STORAGE_KEY
                ];
                
                keysToRemove.forEach(key => {
                    localStorage.removeItem(key);
                });
                
                // Also clear any price target data (they use dynamic keys with item IDs)
                const allKeys = Object.keys(localStorage);
                allKeys.forEach(key => {
                    if (key.startsWith('scrap2mark_price_targets_')) {
                        localStorage.removeItem(key);
                    }
                });
                
                // Clear memory variables
                scannedItems.clear();
                ignoredItems.clear();
                marketData.clear();
                apiItemsData = null;
                apiKey = null;
                currentSortColumn = -1;
                currentSortDirection = 'asc';
                currentSortField = null;
                
                // Reset API state
                resetApiState();
                
                // Close and recreate the floating table with default settings
                if (floatingTable) {
                    floatingTable.style.display = 'none';
                    floatingTable.remove();
                    floatingTable = null;
                }
                
                // Show success message
                alert('✅ Full reset completed successfully!\n\nThe script has been reset to its initial state. The window will now reload with default settings.');
                
                // Reinitialize the script
                setTimeout(() => {
                    createFloatingTable();
                    updateApiStatusDisplay();
                    updateApiButtonVisibility();
                    log('Script reinitialized after full reset');
                }, 500);
                
            } catch (error) {
                alert(`❌ Reset failed: ${error.message}\n\nPlease refresh the page and try again.`);
                log('Full reset failed: ' + error.message);
            }
        } else if (userInput !== null) {
            alert('Reset cancelled. You must type "RESET" exactly to confirm.');
        }
        // If userInput is null, user clicked Cancel, so do nothing
    }
    
    // Update table display
    function updateTableDisplay() {
        if (!floatingTable) {
            log('Warning: floatingTable not found in updateTableDisplay');
            return;
        }
        
        const tbody = document.getElementById('items-table-body');
        const statusDiv = document.getElementById('scan-status');
        const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
        const showIgnoredCheckbox = document.getElementById('show-ignored');
        const hideLowValueCheckbox = document.getElementById('hide-low-value');
        const minValueInput = document.getElementById('min-value-input');
        
        if (!tbody) {
            log('Warning: items-table-body not found in updateTableDisplay');
            return;
        }
        
        tbody.innerHTML = '';
        
        const showNonTradeable = showNonTradeableCheckbox ? showNonTradeableCheckbox.checked : false;
        const showIgnored = showIgnoredCheckbox ? showIgnoredCheckbox.checked : false;
        const hideLowValue = hideLowValueCheckbox ? hideLowValueCheckbox.checked : false;
        const minValue = minValueInput ? parseFloat(minValueInput.value.replace(/[,$]/g, '')) || 0 : 0;
        
        // Toggle Trade column visibility based on checkbox state
        const tradeColumnHeader = document.getElementById('trade-column-header');
        
        if (tradeColumnHeader) {
            tradeColumnHeader.style.display = showNonTradeable ? 'table-cell' : 'none';
        }
        
        // Sort items using current sort state or default sorting
        const sortedItems = applySortToItems(Array.from(scannedItems.values()));
        
        let tradeableCount = 0;
        let nonTradeableCount = 0;
        let ignoredCount = 0;
        let displayedCount = 0;
        let hiddenLowValueCount = 0;
        
        sortedItems.forEach(item => {
            const itemId = item.id || 'N/A';
            const itemName = item.name || 'Unknown';
            const itemCategory = item.category || 'Unknown';
            const itemQuantity = item.quantity || 0;
            const itemTradeable = item.tradeable;
            const itemIgnored = isItemIgnored(itemId);
            
            // Count all items
            if (itemTradeable === true) {
                tradeableCount++;
            } else if (itemTradeable === false) {
                nonTradeableCount++;
            }
            
            if (itemIgnored) {
                ignoredCount++;
            }
            
            // Filter display based on checkboxes
            if (!showNonTradeable && itemTradeable === false) {
                return; // Skip non-tradeable items when checkbox is unchecked
            }
            
            if (!showIgnored && itemIgnored) {
                return; // Skip ignored items when checkbox is unchecked
            }
            
            // Calculate estimated value for low-value filtering
            const estimatedValueForFilter = calculateEstimatedValue(itemId, itemQuantity);
            
            // Filter by minimum value if enabled (only hide items that have a calculated value)
            if (hideLowValue && estimatedValueForFilter > 0 && estimatedValueForFilter < minValue) {
                hiddenLowValueCount++;
                return; // Skip low-value items when checkbox is checked
            }
            
            displayedCount++;
            
            // Determine tradeable status display and row styling
            let tradeableDisplay = '';
            let tradeableColor = '#666';
            let rowStyle = '';
            
            if (itemIgnored) {
                rowStyle = 'background-color: #f0f0f0; opacity: 0.7;'; // Gray background for ignored items
            } else if (itemTradeable === true) {
                tradeableDisplay = '✓';
                tradeableColor = '#28a745';
            } else if (itemTradeable === false) {
                tradeableDisplay = '✗';
                tradeableColor = '#dc3545';
                rowStyle = 'background-color: #f8d7da;'; // Light red background for non-tradeable
            } else {
                tradeableDisplay = '?';
                tradeableColor = '#ffc107';
            }
            
            // Override trade display for ignored items
            if (itemIgnored) {
                tradeableDisplay = itemTradeable === true ? '✓' : itemTradeable === false ? '✗' : '?';
            }
            
            // Get market data for price display
            let priceDisplay = '';
            const marketInfo = marketData.get(itemId.toString());
            
            if (marketInfo && itemTradeable === true) {
                const stats = calculatePriceStats(marketInfo.listings);
                if (stats) {
                    const chart = createPriceChart(marketInfo.listings);
                    const age = Math.round((Date.now() - marketInfo.timestamp) / (1000 * 60)); // age in minutes
                    const ageText = age < 60 ? `${age}m` : `${Math.round(age / 60)}h`;
                    const outliersText = stats.outliersFiltered > 0 ? ` (${stats.outliersFiltered} high outliers, ${stats.sellersFiltered} sellers filtered)` : '';
                    
                    // Use a price closer to low end - take the lower of P25 or 20% above minimum
                    const lowPrice = Math.min(stats.p25, stats.min * 1.2);
                    const displayPrice = Math.round(lowPrice);
                    
                    const tooltip = `Display: $${displayPrice.toLocaleString()} (Low-end price)\nMin: $${stats.min.toLocaleString()}\nMax: $${stats.max.toLocaleString()}\nAvg: $${Math.round(stats.average).toLocaleString()}\nMedian: $${stats.median.toLocaleString()}\nQ1-Q3: $${stats.p25.toLocaleString()}-$${stats.p75.toLocaleString()}\nSellers: ${stats.uniqueSellers}/${stats.totalUniqueSellers}\nListings: ${stats.listings}${outliersText}\nFiltered: ${stats.priceCapFiltered} 10x+ prices, ${stats.statFiltered} statistical\nTotal Qty: ${stats.totalQuantity.toLocaleString()}\nAge: ${ageText}`;
                    
                    priceDisplay = `
                        <div style="display: flex; flex-direction: column; align-items: center;">
                            <div style="font-size: 9px; color: #666;">$${displayPrice.toLocaleString()}</div>
                            <div class="price-chart-hover" data-item-id="${itemId}" data-item-name="${itemName}" style="cursor: help;">${chart}</div>
                        </div>
                    `;
                }
            } else if (itemTradeable === true) {
                priceDisplay = '<div style="font-size: 9px; color: #999; text-align: center;">No data</div>';
            } else {
                priceDisplay = '<div style="font-size: 9px; color: #999; text-align: center;">-</div>';
            }
            
            // Calculate estimated value
            const estimatedValue = calculateEstimatedValue(itemId, itemQuantity);
            const estimatedValueDisplay = estimatedValue > 0 ? `$${Math.round(estimatedValue).toLocaleString()}` : '-';
            
            // Create action button (ignore)
            const ignoreButtonText = itemIgnored ? 'Unignore' : 'Ignore';
            const ignoreButtonClass = itemIgnored ? 'ignore-btn unignore' : 'ignore-btn ignore';
            
            // Create row
            const row = document.createElement('tr');
            row.style.borderBottom = '1px solid #dee2e6';
            row.style.cssText += rowStyle;
            row.innerHTML = `
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${itemName}">${itemName}</td>
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: center;">${itemCategory}</td>
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: center;">${itemQuantity}</td>
                <td class="trade-column-cell" style="padding: 4px; border: 1px solid #dee2e6; color: ${tradeableColor}; font-weight: bold; text-align: center; display: none;">${tradeableDisplay}</td>
                <td style="padding: 2px; border: 1px solid #dee2e6; text-align: center;">${priceDisplay}</td>
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: center; font-weight: bold; color: ${estimatedValue > 0 ? '#28a745' : '#666'};">${estimatedValueDisplay}</td>
                <td style="padding: 4px; border: 1px solid #dee2e6; text-align: center;">
                    <button class="${ignoreButtonClass}" data-item-id="${itemId}">${ignoreButtonText}</button>
                </td>
            `;
            
            // Add event listener to the ignore button
            const ignoreButton = row.querySelector('.ignore-btn');
            if (ignoreButton) {
                ignoreButton.addEventListener('click', () => {
                    toggleIgnoreItem(itemId);
                });
            }
            
            tbody.appendChild(row);
        });
        
        // Update Trade column cell visibility for all rows
        const tradeColumnCells = document.querySelectorAll('.trade-column-cell');
        tradeColumnCells.forEach(cell => {
            cell.style.display = showNonTradeable ? 'table-cell' : 'none';
        });
        
        const unknownCount = sortedItems.length - tradeableCount - nonTradeableCount;
        const filterText = showNonTradeable ? '' : ' (filtered)';
        
        // Status line: Total, Tradeable, and conditionally Non-Tradeable/Unknown/Ignored/Low-Value
        let statusHtml = `Total: ${displayedCount}${filterText} | <span style="color: #28a745;">Tradeable: ${tradeableCount}</span>`;
        
        if (showNonTradeable && (nonTradeableCount > 0 || unknownCount > 0)) {
            statusHtml += ` | <span style="color: #dc3545;">Non-Tradeable: ${nonTradeableCount}</span> | <span style="color: #ffc107;">Unknown: ${unknownCount}</span>`;
        }
        
        if (ignoredCount > 0) {
            statusHtml += ` | <span style="color: #6c757d;">Ignored: ${ignoredCount}</span>`;
        }
        
        if (hiddenLowValueCount > 0) {
            statusHtml += ` | <span style="color: #17a2b8;">Low-Value Hidden: ${hiddenLowValueCount}</span>`;
        }
        
        if (statusDiv) {
            statusDiv.innerHTML = statusHtml;
        } else {
            log('Warning: scan-status element not found, unable to update status display');
        }
        
        // Add hover popup functionality for price charts (with small delay to ensure DOM is updated)
        setTimeout(() => {
            addPriceChartHoverHandlers();
            
            // Restore sort indicators after table update
            if (currentSortField && currentSortColumn !== -1) {
                // Clear all sort indicators
                ['name', 'category', 'qty', 'trade', 'price', 'estimated'].forEach(col => {
                    const indicator = document.getElementById(`sort-${col}`);
                    if (indicator) indicator.textContent = '↕';
                });
                
                // Set current sort indicator
                const currentIndicator = document.getElementById(`sort-${currentSortField}`);
                if (currentIndicator) {
                    currentIndicator.textContent = currentSortDirection === 'asc' ? '↑' : '↓';
                }
            }
        }, 10);
    }
    
    // Global timeout for popup hiding
    let globalHideTimeout;
    
    // Add hover popup functionality
    function addPriceChartHoverHandlers() {
        const priceCharts = document.querySelectorAll('.price-chart-hover');
        
        priceCharts.forEach(chart => {
            let showTimeout;
            
            chart.addEventListener('mouseenter', (e) => {
                // Clear any existing hide timeout
                if (globalHideTimeout) {
                    clearTimeout(globalHideTimeout);
                    globalHideTimeout = null;
                }
                
                // Capture the data immediately before the timeout
                const itemId = e.currentTarget.getAttribute('data-item-id');
                const itemName = e.currentTarget.getAttribute('data-item-name');
                const mouseEvent = { clientX: e.clientX, clientY: e.clientY };
                
                // Show popup after 300ms delay (reduced for faster response)
                showTimeout = setTimeout(() => {
                    const marketInfo = marketData.get(itemId);
                    
                    if (marketInfo && marketInfo.listings) {
                        showPricePopup(mouseEvent, marketInfo.listings, itemName, itemId);
                    }
                }, 300);
            });
            
            chart.addEventListener('mouseleave', () => {
                // Clear show timeout if still pending
                if (showTimeout) {
                    clearTimeout(showTimeout);
                    showTimeout = null;
                }
                
                // Hide popup after 2 seconds delay (increased to allow easier access to popup)
                globalHideTimeout = setTimeout(() => {
                    hidePricePopup();
                }, 2000);
            });
        });
    }
    
    // Update existing popup with new chart content
    function updatePricePopup(listings, itemName, zoomMin = null, zoomMax = null) {
        const popup = document.getElementById('price-popup');
        if (!popup) return;
        
        // Update the content with new chart
        popup.innerHTML = createDetailedPriceChart(listings, itemName, zoomMin, zoomMax);
        
        // Re-add chart interaction handlers after content update
        addChartInteraction(listings, itemName);
        
        // Re-add panel handlers after content update
        addPanelHandlers();
        
        // Keep popup in same general position but adjust if needed
        const rect = popup.getBoundingClientRect();
        let left = rect.left;
        let top = rect.top;
        
        // Adjust if popup would go off screen after content change
        const newRect = popup.getBoundingClientRect();
        if (left + newRect.width > window.innerWidth) {
            left = window.innerWidth - newRect.width - 10;
        }
        if (top + newRect.height > window.innerHeight) {
            top = window.innerHeight - newRect.height - 10;
        }
        if (left < 0) left = 10;
        if (top < 0) top = 10;
        
        popup.style.left = left + 'px';
        popup.style.top = top + 'px';
    }
    
    // Show detailed price popup
    function showPricePopup(event, listings, itemName, itemId, zoomMin = null, zoomMax = null) {
        // Load stored price targets for this item
        loadPriceTargets(itemId);
        
        // Remove existing popup
        hidePricePopup();
        
        const popup = document.createElement('div');
        popup.id = 'price-popup';
        popup.innerHTML = createDetailedPriceChart(listings, itemName, zoomMin, zoomMax);
        popup.style.position = 'fixed';
        popup.style.zIndex = '1000000';
        popup.style.maxWidth = '350px';
        popup.style.pointerEvents = 'auto';
        
        // Make popup interactive
        popup.addEventListener('mouseenter', () => {
            // Cancel any pending hide timeouts when hovering over popup
            if (globalHideTimeout) {
                clearTimeout(globalHideTimeout);
                globalHideTimeout = null;
            }
        });
        
        popup.addEventListener('mouseleave', () => {
            // Hide popup when leaving popup area
            globalHideTimeout = setTimeout(() => {
                hidePricePopup();
            }, 500);
        });
        
        document.body.appendChild(popup);
        
        // Position popup near mouse but keep it in viewport
        const rect = popup.getBoundingClientRect();
        let left = event.clientX + 10;
        let top = event.clientY + 10;
        
        // Adjust if popup would go off screen
        if (left + rect.width > window.innerWidth) {
            left = event.clientX - rect.width - 10;
        }
        if (top + rect.height > window.innerHeight) {
            top = event.clientY - rect.height - 10;
        }
        if (left < 0) left = 10;
        if (top < 0) top = 10;
        
        popup.style.left = left + 'px';
        popup.style.top = top + 'px';
        
        // Add chart interaction handlers after popup is added to DOM
        addChartInteraction(listings, itemName);
        
        // Add panel expansion handlers
        addPanelHandlers();
    }
    
    // Add panel interaction handlers (simplified for integrated layout)
    function addPanelHandlers() {
        // No expand/collapse buttons needed anymore since listings are integrated
        const clearBtn = document.getElementById('clear-targets-btn');
        
        // Add event listeners for price target checkboxes
        const checkboxes = document.querySelectorAll('.price-target-checkbox');
        checkboxes.forEach(checkbox => {
            const originalPrice = parseFloat(checkbox.getAttribute('data-original-price'));
            
            // Restore checkbox state from stored data
            if (selectedPriceTargets.has(originalPrice)) {
                checkbox.checked = true;
            }
            
            checkbox.addEventListener('change', (e) => {
                const originalPrice = parseFloat(e.target.getAttribute('data-original-price'));
                const targetPrice = parseFloat(e.target.getAttribute('data-target-price'));
                const isChecked = e.target.checked;
                
                togglePriceTarget(originalPrice, targetPrice, isChecked);
            });
        });
        
        // Update the display with loaded targets
        updateSelectedTargetsDisplay();
        
        // Add event listeners for control buttons
        if (clearBtn) {
            clearBtn.addEventListener('click', () => {
                clearAllTargets();
            });
        }
    }
    
    // Global storage for selected price targets (persistent across sessions)
    let selectedPriceTargets = new Map();
    let currentItemId = null;
    
    // Load price targets from localStorage
    function loadPriceTargets(itemId) {
        currentItemId = itemId;
        const storageKey = `scrap2mark_price_targets_${itemId}`;
        const stored = localStorage.getItem(storageKey);
        
        if (stored) {
            try {
                const parsed = JSON.parse(stored);
                selectedPriceTargets = new Map(Object.entries(parsed).map(([k, v]) => [parseFloat(k), v]));
            } catch (e) {
                selectedPriceTargets = new Map();
            }
        } else {
            selectedPriceTargets = new Map();
        }
    }
    
    // Save price targets to localStorage
    function savePriceTargets() {
        if (!currentItemId) return;
        
        const storageKey = `scrap2mark_price_targets_${currentItemId}`;
        const toStore = Object.fromEntries(selectedPriceTargets.entries());
        localStorage.setItem(storageKey, JSON.stringify(toStore));
    }
    
    // Toggle price target selection
    function togglePriceTarget(originalPrice, targetPrice, isSelected) {
        if (isSelected) {
            selectedPriceTargets.set(originalPrice, targetPrice);
        } else {
            selectedPriceTargets.delete(originalPrice);
        }
        updateSelectedTargetsDisplay();
        savePriceTargets();
    }
    
    // Update the display of selected targets
    function updateSelectedTargetsDisplay() {
        const targetDiv = document.getElementById('selected-targets');
        if (!targetDiv) return;
        
        if (selectedPriceTargets.size === 0) {
            targetDiv.innerHTML = 'None selected';
            targetDiv.style.color = '#666';
        } else {
            const targets = Array.from(selectedPriceTargets.entries())
                .sort((a, b) => a[1] - b[1]) // Sort by target price
                .map(([original, target]) => `$${target.toLocaleString()}`)
                .join(', ');
            targetDiv.innerHTML = targets;
            targetDiv.style.color = '#28a745';
        }
    }
    
    // Clear all selected targets
    function clearAllTargets() {
        selectedPriceTargets.clear();
        // Uncheck all checkboxes
        const checkboxes = document.querySelectorAll('.price-target-checkbox');
        checkboxes.forEach(cb => {
            cb.checked = false;
        });
        updateSelectedTargetsDisplay();
        savePriceTargets();
    }
    
    // Make functions global so they can be called from HTML
    window.togglePriceTarget = togglePriceTarget;
    window.clearAllTargets = clearAllTargets;
    
    // Add chart interaction functionality
    function addChartInteraction(listings, itemName) {
        const svg = document.getElementById('price-chart-svg');
        const selectionRect = document.getElementById('selection-rect');
        const resetZoomBtn = document.getElementById('reset-zoom');
        
        if (!svg || !selectionRect) return;
        
        let isSelecting = false;
        let startX = 0;
        let currentX = 0;
        
        // Get chart parameters
        const padding = parseInt(svg.getAttribute('data-padding'));
        const chartWidth = parseInt(svg.getAttribute('data-chart-width'));
        const chartHeight = parseInt(svg.getAttribute('data-chart-height'));
        const displayMin = parseFloat(svg.getAttribute('data-display-min'));
        const displayMax = parseFloat(svg.getAttribute('data-display-max'));
        
        // Add bar hover effects
        const bars = svg.querySelectorAll('.chart-bar');
        bars.forEach(bar => {
            bar.addEventListener('mouseenter', (e) => {
                const priceMin = parseFloat(e.target.getAttribute('data-price-min'));
                const priceMax = parseFloat(e.target.getAttribute('data-price-max'));
                const amount = parseInt(e.target.getAttribute('data-amount'));
                
                // Show tooltip for individual bar
                e.target.setAttribute('opacity', '1');
                e.target.style.cursor = 'pointer';
                
                // Create temporary tooltip
                const tooltip = document.createElement('div');
                tooltip.id = 'bar-tooltip';
                tooltip.innerHTML = `$${Math.round(priceMin).toLocaleString()}-$${Math.round(priceMax).toLocaleString()}<br>Quantity: ${amount.toLocaleString()}`;
                tooltip.style.position = 'absolute';
                tooltip.style.background = 'rgba(0,0,0,0.8)';
                tooltip.style.color = 'white';
                tooltip.style.padding = '4px 8px';
                tooltip.style.borderRadius = '4px';
                tooltip.style.fontSize = '11px';
                tooltip.style.pointerEvents = 'none';
                tooltip.style.zIndex = '1000001';
                
                const rect = e.target.getBoundingClientRect();
                tooltip.style.left = (rect.left + rect.width / 2 + window.pageXOffset) + 'px';
                tooltip.style.top = (rect.top - 40 + window.pageYOffset) + 'px';
                tooltip.style.transform = 'translateX(-50%)';
                
                document.body.appendChild(tooltip);
            });
            
            bar.addEventListener('mouseleave', (e) => {
                e.target.setAttribute('opacity', '0.7');
                const tooltip = document.getElementById('bar-tooltip');
                if (tooltip) tooltip.remove();
            });
        });
        
        // Selection functionality
        svg.addEventListener('mousedown', (e) => {
            const rect = svg.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;
            
            // Only start selection if within chart area
            if (x >= padding && x <= padding + chartWidth && y >= padding && y <= padding + chartHeight) {
                isSelecting = true;
                startX = x;
                currentX = x;
                
                selectionRect.style.display = 'block';
                selectionRect.setAttribute('x', startX);
                selectionRect.setAttribute('y', padding);
                selectionRect.setAttribute('width', 0);
                selectionRect.setAttribute('height', chartHeight);
                
                e.preventDefault();
            }
        });
        
        svg.addEventListener('mousemove', (e) => {
            if (!isSelecting) return;
            
            const rect = svg.getBoundingClientRect();
            currentX = Math.max(padding, Math.min(padding + chartWidth, e.clientX - rect.left));
            
            const width = Math.abs(currentX - startX);
            const x = Math.min(startX, currentX);
            
            selectionRect.setAttribute('x', x);
            selectionRect.setAttribute('width', width);
        });
        
        svg.addEventListener('mouseup', (e) => {
            if (!isSelecting) return;
            
            isSelecting = false;
            selectionRect.style.display = 'none';
            
            // Calculate price range for zoom
            const minX = Math.min(startX, currentX);
            const maxX = Math.max(startX, currentX);
            
            // Only zoom if selection is wide enough
            if (maxX - minX > 10) {
                const priceRange = displayMax - displayMin;
                const zoomMin = displayMin + ((minX - padding) / chartWidth) * priceRange;
                const zoomMax = displayMin + ((maxX - padding) / chartWidth) * priceRange;
                
                // Update popup with zoomed chart (keep existing popup)
                updatePricePopup(listings, itemName, zoomMin, zoomMax);
            }
        });
        
        // Reset zoom functionality
        if (resetZoomBtn) {
            resetZoomBtn.addEventListener('click', () => {
                // Reset zoom (keep existing popup)
                updatePricePopup(listings, itemName);
            });
        }
        
        // Prevent selection outside chart area
        document.addEventListener('mouseup', () => {
            if (isSelecting) {
                isSelecting = false;
                selectionRect.style.display = 'none';
            }
        });
    }
    
    // Hide price popup
    function hidePricePopup() {
        const existingPopup = document.getElementById('price-popup');
        if (existingPopup) {
            existingPopup.remove();
        }
    }
    
    // Clear inventory data
    function clearInventoryData() {
        scannedItems.clear();
        localStorage.removeItem(STORAGE_KEY);
        updateTableDisplay();
        updateMarketButtonText();
        log('Inventory data cleared');
    }
    
    // Clear all price targets
    function clearAllPriceTargets() {
        // Get all localStorage keys that start with our price target prefix
        const keysToRemove = [];
        for (let i = 0; i < localStorage.length; i++) {
            const key = localStorage.key(i);
            if (key && key.startsWith('scrap2mark_price_targets_')) {
                keysToRemove.push(key);
            }
        }
        
        // Remove all price target keys
        keysToRemove.forEach(key => localStorage.removeItem(key));
        
        // Update the display to reflect changes
        updateTableDisplay();
        log(`Cleared ${keysToRemove.length} price targets`);
    }
    
    // Export inventory data
    // Get selected price targets for an item
    function getSelectedPriceTargets(itemId) {
        const storageKey = `scrap2mark_price_targets_${itemId}`;
        const stored = localStorage.getItem(storageKey);
        
        if (stored) {
            try {
                const parsed = JSON.parse(stored);
                return Object.entries(parsed).map(([price, data]) => ({
                    price: parseFloat(price),
                    seller: data.seller,
                    amount: data.amount
                }));
            } catch (e) {
                return [];
            }
        }
        return [];
    }

    // Export inventory data with enhanced information
    function exportInventoryData() {
        const data = Array.from(scannedItems.values()).map(item => {
            const itemData = {
                id: item.id,
                name: item.name,
                category: item.category,
                quantity: item.quantity,
                equipped: item.equipped,
                armoryId: item.armoryId,
                tradeable: item.tradeable
            };
            
            // Add market data if available
            const marketInfo = marketData.get(item.id.toString());
            if (marketInfo) {
                const stats = calculatePriceStats(marketInfo.listings);
                if (stats) {
                    // Calculate the low-end price used in the Price column
                    const lowPrice = Math.min(stats.p25, stats.min * 1.2);
                    const displayPrice = Math.round(lowPrice);
                    
                    // Calculate estimated value
                    const estimatedValue = calculateEstimatedValue(item.id, item.quantity);
                    
                    itemData.marketData = {
                        displayPrice: displayPrice,  // The low-end price shown in Price column
                        averagePrice: Math.round(stats.average),
                        minPrice: stats.min,
                        maxPrice: stats.max,
                        medianPrice: stats.median,
                        q1Price: stats.p25,
                        q3Price: stats.p75,
                        estimatedTotalValue: estimatedValue,
                        totalListings: stats.totalListings,
                        filteredListings: stats.listings,
                        uniqueSellers: stats.uniqueSellers,
                        totalUniqueSellers: stats.totalUniqueSellers,
                        outliersFiltered: stats.outliersFiltered,
                        sellersFiltered: stats.sellersFiltered,
                        priceCapFiltered: stats.priceCapFiltered,
                        statFiltered: stats.statFiltered,
                        totalQuantity: stats.totalQuantity,
                        fetchedAt: new Date(marketInfo.timestamp).toISOString(),
                        ageInMinutes: Math.round((Date.now() - marketInfo.timestamp) / (1000 * 60))
                    };
                }
            }
            
            // Add selected price targets if any
            const selectedTargets = getSelectedPriceTargets(item.id);
            if (selectedTargets.length > 0) {
                itemData.selectedPriceTargets = selectedTargets;
                
                // Calculate total value of selected targets
                const targetValue = selectedTargets.reduce((sum, target) => 
                    sum + (target.price * target.amount), 0);
                itemData.selectedTargetsTotalValue = targetValue;
                
                // Calculate how many of this item's quantity could be sold at target prices
                const targetQuantity = selectedTargets.reduce((sum, target) => 
                    sum + target.amount, 0);
                itemData.selectedTargetsQuantity = targetQuantity;
                
                if (targetQuantity > 0) {
                    itemData.averageTargetPrice = Math.round(targetValue / targetQuantity);
                }
            }
            
            return itemData;
        });
        
        // Save to localStorage
        localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
        
        // Create export summary
        const totalItems = data.length;
        const itemsWithMarketData = data.filter(item => item.marketData).length;
        const itemsWithTargets = data.filter(item => item.selectedPriceTargets).length;
        const totalEstimatedValue = data.reduce((sum, item) => sum + (item.marketData?.estimatedTotalValue || 0), 0);
        const totalTargetValue = data.reduce((sum, item) => sum + (item.selectedTargetsTotalValue || 0), 0);
        
        // Add summary to export data
        const exportData = {
            exportInfo: {
                exportedAt: new Date().toISOString(),
                scriptVersion: "Scrap2Mark v2.0",
                totalItems: totalItems,
                itemsWithMarketData: itemsWithMarketData,
                itemsWithPriceTargets: itemsWithTargets,
                totalEstimatedValue: totalEstimatedValue,
                totalSelectedTargetValue: totalTargetValue,
                priceCalculationMethod: "Low-end pricing (min of P25 or 20% above minimum)"
            },
            items: data
        };
        
        // Create downloadable file
        const dataStr = JSON.stringify(exportData, null, 2);
        const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
        
        const exportFileDefaultName = `scrap2mark_export_${new Date().toISOString().split('T')[0]}_${totalItems}items.json`;
        
        const linkElement = document.createElement('a');
        linkElement.setAttribute('href', dataUri);
        linkElement.setAttribute('download', exportFileDefaultName);
        linkElement.click();
        
        log(`Exported ${totalItems} items (${itemsWithMarketData} with market data, ${itemsWithTargets} with price targets) to ${exportFileDefaultName}. Total estimated value: $${totalEstimatedValue.toLocaleString()}`);
    }
    
    // Main scanning function (now only scans visible items)
    async function startInventoryScan() {
        if (isScanning) {
            log('Scan already in progress');
            return;
        }
        
        // Check for API key and warn user if not present
        if (!apiKey) {
            const statusDiv = document.getElementById('scan-status');
            if (statusDiv) {
                statusDiv.innerHTML = '<span style="color: #ffc107;">⚠️ No API key detected. Items will scan but tradeable status will be unknown. Save your API key first for full functionality.</span>';
            }
            log('Warning: Scanning without API key - tradeable status will be unknown');
        }
        
        isScanning = true;
        
        const startBtn = document.getElementById('start-scan');
        const statusDiv = document.getElementById('scan-status');
        
        if (startBtn) startBtn.disabled = true;
        if (statusDiv) statusDiv.textContent = 'Scanning visible items...';
        
        try {
            log('Starting inventory scan of visible items');
            
            // Wait for items to be loaded in current category
            await waitForElement('#all-items li[data-item], li[data-item]', 5000);
            
            // Scan currently visible items only
            const newItemsFound = checkForNewItems();
            updateTableDisplay();
            
            // Update scroll reminder based on current loading status
            updateScrollReminder();
            
            // Save scanned items after scanning
            saveScannedItems();
            
            log(`Scan completed. Found ${newItemsFound} new items (Total: ${scannedItems.size})`);
            updateMarketButtonText(); // Update button with new item count
            if (statusDiv) {
                // Show scan result temporarily, then update with full breakdown
                statusDiv.textContent = `Found ${newItemsFound} new items. Total: ${scannedItems.size} items`;
                setTimeout(() => {
                    updateTableDisplay(); // This will restore the detailed breakdown
                }, 2000);
            }
            
        } catch (error) {
            log(`Scan failed: ${error.message}`);
            if (statusDiv) statusDiv.textContent = `Scan failed: ${error.message}`;
        } finally {
            isScanning = false;
            if (startBtn) startBtn.disabled = false;
        }
    }
    
    // Load previously saved data
    function loadSavedData() {
        try {
            const savedData = localStorage.getItem(STORAGE_KEY);
            if (savedData) {
                const items = JSON.parse(savedData);
                items.forEach(item => {
                    const key = `${item.id}_${item.armoryId || 'base'}`;
                    scannedItems.set(key, item);
                });
                log(`Loaded ${items.length} saved items`);
                
                // Update table display and button text after loading data
                setTimeout(() => {
                    updateTableDisplay();
                    updateMarketButtonText();
                }, 100);
            } else {
                log('No saved data found');
            }
        } catch (error) {
            log(`Failed to load saved data: ${error.message}`);
        }
    }
    
    // Initialize script
    function init() {
        log('Initializing Scrap2Mark');
        
        // Check if we're on a supported page
        const isInventoryPage = window.location.href.includes('/item.php');
        const isMarketPage = window.location.href.includes('/page.php?sid=ItemMarket');
        
        if (!isInventoryPage && !isMarketPage) {
            log('Not on supported page, script will not run');
            return;
        }
        
        // Set up network monitoring for item loading detection
        setupNetworkMonitoring();
        
        // Load API key and items data
        loadApiKey();
        loadApiItemsData();
        loadMarketData();
        
        // Auto-fetch API data once per day if we have an API key
        if (apiKey) {
            const lastFetch = localStorage.getItem('scrap2mark_last_api_fetch');
            const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);
            
            if (!lastFetch || parseInt(lastFetch) < oneDayAgo || !apiItemsData) {
                log('Auto-fetching API data (daily update or missing data)');
                log(`Last fetch: ${lastFetch ? new Date(parseInt(lastFetch)).toLocaleString() : 'Never'}`);
                log(`API Items Data available: ${apiItemsData ? 'Yes' : 'No'}`);
                
                setTimeout(() => {
                    handleFetchApiData().then(() => {
                        localStorage.setItem('scrap2mark_last_api_fetch', Date.now().toString());
                        log('Auto-fetch completed successfully');
                    }).catch(error => {
                        log('Auto-fetch failed: ' + error.message);
                    });
                }, 2000); // Delay to let page load
            } else {
                log('API data is recent and available, skipping auto-fetch');
                log(`Last fetch: ${new Date(parseInt(lastFetch)).toLocaleString()}`);
            }
        } else {
            log('No API key available for auto-fetch');
        }
        
        // Load saved data and ignored items
        loadSavedData();
        loadIgnoredItems();
        
        // Update page-specific functionality
        if (isInventoryPage) {
            log('Running on inventory page - full scanning functionality available');
        } else if (isMarketPage) {
            log('Running on market page - posting functionality available');
            // Hide inventory-specific controls on market page
            setTimeout(() => {
                const scanControls = document.getElementById('scan-controls');
                if (scanControls) {
                    // Hide scan button but keep other controls
                    const startScanBtn = document.getElementById('start-scan');
                    if (startScanBtn) {
                        startScanBtn.style.display = 'none';
                    }
                    
                    // Update the manual instruction text
                    const manualText = floatingTable.querySelector('div[style*="font-size: 11px"]');
                    if (manualText && manualText.textContent.includes('Scroll down')) {
                        manualText.textContent = 'Market page: Use data from inventory scanning and price targets to post items.';
                    }
                }
            }, 1000);
        }
        
        // Create floating table (always available for data management)
        createFloatingTable();
        
        // Update API status display
        updateApiStatusDisplay();
        
        // Update button visibility
        updateApiButtonVisibility();
        
        // Add CSS for better styling
        const style = document.createElement('style');
        style.textContent = `
            #inventory-scanner-table button:hover {
                opacity: 0.8;
            }
            #inventory-scanner-table table th {
                position: sticky;
                top: 0;
                background: #f8f9fa;
            }
            /* Enhanced button hover effects for all styled buttons */
            button[style*="linear-gradient"]:hover {
                transform: translateY(-1px) !important;
                box-shadow: 0 4px 8px rgba(0,0,0,0.2) !important;
                filter: brightness(1.1) !important;
            }
            /* Title bar cursor styling */
            #inventory-scanner-table .title-bar,
            #inventory-scanner-table .title-bar * {
                cursor: pointer !important;
            }
            #inventory-scanner-table table tr:hover {
                background-color: #f8f9fa;
            }
            #api-key-input {
                font-family: monospace;
            }
            #inventory-scanner-table {
                resize: both;
                overflow: hidden;
            }
            #inventory-scanner-table table {
                table-layout: fixed;
                width: 100%;
            }
            #inventory-scanner-table table td {
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
            }
            #inventory-scanner-table table td:nth-child(2) {
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }
            .price-chart-hover {
                transition: transform 0.2s ease;
            }
            .price-chart-hover:hover {
                transform: scale(1.1);
            }
            #price-popup {
                background: white;
                border-radius: 8px;
                box-shadow: 0 4px 20px rgba(0,0,0,0.15);
                border: 1px solid #ddd;
                animation: fadeIn 0.2s ease-out;
            }
            @keyframes fadeIn {
                from { opacity: 0; transform: scale(0.9); }
                to { opacity: 1; transform: scale(1); }
            }
            .ignore-btn {
                font-size: 10px !important;
                padding: 2px 6px !important;
                border: 1px solid #ccc !important;
                border-radius: 3px !important;
                cursor: pointer !important;
                transition: all 0.2s !important;
                font-weight: bold !important;
                min-width: 60px !important;
            }
            .ignore-btn.ignore {
                background: #6c757d !important;
                color: white !important;
                border-color: #6c757d !important;
            }
            .ignore-btn.ignore:hover {
                background: #5a6268 !important;
                border-color: #545b62 !important;
                transform: scale(1.05) !important;
            }
            .ignore-btn.unignore {
                background: #dc3545 !important;
                color: white !important;
                border-color: #dc3545 !important;
                animation: pulse 2s infinite !important;
            }
            .ignore-btn.unignore:hover {
                background: #c82333 !important;
                border-color: #bd2130 !important;
                transform: scale(1.05) !important;
                animation: none !important;
            }
            @keyframes pulse {
                0% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.4); }
                70% { box-shadow: 0 0 0 6px rgba(220, 53, 69, 0); }
                100% { box-shadow: 0 0 0 0 rgba(220, 53, 69, 0); }
            }
        `;
        document.head.appendChild(style);
        
        log('Scrap2Mark initialized');
    }
    
    // Show toast notification
    function showToast(message, type = 'info') {
        const toast = document.createElement('div');
        const colors = {
            success: '#28a745',
            error: '#dc3545',
            warning: '#ffc107',
            info: '#17a2b8'
        };
        
        toast.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: ${colors[type] || colors.info};
            color: ${type === 'warning' ? '#212529' : 'white'};
            padding: 12px 16px;
            border-radius: 5px;
            z-index: 100002;
            font-size: 14px;
            font-weight: bold;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            max-width: 400px;
            word-wrap: break-word;
            animation: slideIn 0.3s ease-out;
        `;
        toast.textContent = message;
        
        // Add animation keyframes if not already added
        if (!document.querySelector('#toast-animations')) {
            const animationStyle = document.createElement('style');
            animationStyle.id = 'toast-animations';
            animationStyle.textContent = `
                @keyframes slideIn {
                    from { transform: translateX(100%); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }
                @keyframes slideOut {
                    from { transform: translateX(0); opacity: 1; }
                    to { transform: translateX(100%); opacity: 0; }
                }
            `;
            document.head.appendChild(animationStyle);
        }
        
        document.body.appendChild(toast);
        
        // Remove toast after 4 seconds
        setTimeout(() => {
            toast.style.animation = 'slideOut 0.3s ease-out';
            setTimeout(() => {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
            }, 300);
        }, 4000);
    }
    
    // Load all price targets by item name
    function loadAllPriceTargets() {
        const allTargets = {};
        
        // Go through all scanned items and check for saved price targets
        for (const [key, item] of scannedItems) {
            // Extract item ID from the key (format: "itemId_armoryId")
            const itemId = key.split('_')[0];
            const storageKey = `scrap2mark_price_targets_${itemId}`;
            const stored = localStorage.getItem(storageKey);
            
            if (stored) {
                try {
                    const parsed = JSON.parse(stored);
                    const targets = new Map(Object.entries(parsed).map(([k, v]) => [parseFloat(k), v]));
                    
                    // Find the selected price target
                    for (const [price, isSelected] of targets) {
                        if (isSelected) {
                            allTargets[item.name] = {
                                selectedPrice: price,
                                itemId: itemId
                            };
                            break;
                        }
                    }
                } catch (e) {
                    // Silently ignore price target loading errors
                }
            }
        }
        
        return allTargets;
    }
    
    // Get filtered items based on current filter settings
    function getFilteredItems() {
        const showNonTradeableCheckbox = document.getElementById('show-non-tradeable');
        const showIgnoredCheckbox = document.getElementById('show-ignored');
        const hideLowValueCheckbox = document.getElementById('hide-low-value');
        const minValueInput = document.getElementById('min-value-input');
        
        const showNonTradeable = showNonTradeableCheckbox ? showNonTradeableCheckbox.checked : false;
        const showIgnored = showIgnoredCheckbox ? showIgnoredCheckbox.checked : false;
        const hideLowValue = hideLowValueCheckbox ? hideLowValueCheckbox.checked : false;
        const minValue = minValueInput ? parseFloat(minValueInput.value.replace(/[,$]/g, '')) || 0 : 0;
        
        // Get all items and apply filters
        const allItems = Array.from(scannedItems.values());
        
        return allItems.filter(item => {
            const itemId = item.id || 'N/A';
            const itemTradeable = item.tradeable;
            const itemIgnored = isItemIgnored(itemId);
            
            // Filter non-tradeable items
            if (!showNonTradeable && itemTradeable === false) {
                return false;
            }
            
            // Filter ignored items
            if (!showIgnored && itemIgnored) {
                return false;
            }
            
            // Filter low value items
            if (hideLowValue && itemTradeable === true) {
                const estimatedValue = calculateEstimatedValue(itemId, item.quantity);
                if (estimatedValue < minValue) {
                    return false;
                }
            }
            
            return true;
        });
    }
    
    // Market posting functionality
    async function handlePostToMarket() {
        // Check if all items are likely loaded first
        const loadingStatus = checkItemLoadingStatus();
        if (!loadingStatus.likelyAllLoaded) {
            const proceedAnyway = confirm(
                `⚠️ WARNING: Only ${loadingStatus.currentVisibleItems} items visible. ` +
                `You may not have loaded all your items yet.\n\n` +
                `For best results:\n` +
                `1. Scroll down to load ALL items\n` +
                `2. Scan visible items again\n` +
                `3. Get market prices\n` +
                `4. Then fill market forms\n\n` +
                `Do you want to proceed anyway with current items?`
            );
            
            if (!proceedAnyway) {
                return;
            }
        }
        
        // Auto-minimize the window when filling market forms
        const content = document.getElementById('table-content');
        if (content && content.style.display !== 'none') {
            log('Auto-minimizing Scrap2Mark window for better workflow');
            const windowState = loadWindowState();
            // Minimizing
            content.style.display = 'none';
            floatingTable.style.width = WINDOW_MINIMIZED_WIDTH;
            floatingTable.style.height = WINDOW_MINIMIZED_HEIGHT;
            floatingTable.style.resize = 'none';
            // Override constraints for minimized state
            floatingTable.style.minWidth = WINDOW_MINIMIZED_WIDTH;
            floatingTable.style.minHeight = WINDOW_MINIMIZED_HEIGHT;
            floatingTable.style.maxWidth = WINDOW_MINIMIZED_WIDTH;
            floatingTable.style.maxHeight = WINDOW_MINIMIZED_HEIGHT;
            
            // Save minimized state
            const rect = floatingTable.getBoundingClientRect();
            saveWindowState(rect.left, rect.top, windowState.width || WINDOW_DEFAULT_WIDTH, windowState.height || WINDOW_DEFAULT_HEIGHT, true);
            
            // Show a brief notification
            const toast = document.createElement('div');
            toast.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                background: #17a2b8;
                color: white;
                padding: 10px 15px;
                border-radius: 5px;
                z-index: 100002;
                font-size: 12px;
                font-weight: bold;
                box-shadow: 0 4px 12px rgba(0,0,0,0.3);
                max-width: 250px;
            `;
            toast.textContent = 'Scrap2Mark minimized - Click header to restore';
            document.body.appendChild(toast);
            
            setTimeout(() => {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
            }, 4000);
        }
        
        const filteredItems = getFilteredItems();
        const priceTargets = loadAllPriceTargets();
        
        const targetedItems = filteredItems.filter(item => {
            return priceTargets[item.name] && priceTargets[item.name].selectedPrice;
        });
        
        if (targetedItems.length === 0) {
            const totalItems = filteredItems.length;
            const priceTargetCount = Object.keys(priceTargets).length;
            showToast(`No items ready for posting. Found ${totalItems} filtered items, ${priceTargetCount} with price targets, but none match both criteria.`, 'warning');
            return;
        }
        
        // Check if we're on the market page
        if (!window.location.href.includes('/page.php?sid=ItemMarket')) {
            const result = confirm(`You need to be on the Item Market page to post items. Would you like to navigate there now?\n\nFound ${targetedItems.length} items ready to post.`);
            if (result) {
                window.location.href = '/page.php?sid=ItemMarket#/addListing';
            }
            return;
        }
        
        showPostToMarketDialog(targetedItems);
    }
    
    function showPostToMarketDialog(items) {
        // Create modal dialog
        const modalOverlay = document.createElement('div');
        modalOverlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 10001;
            display: flex;
            align-items: center;
            justify-content: center;
        `;
        
        const modal = document.createElement('div');
        modal.style.cssText = `
            background: #2b2b2b;
            border-radius: 8px;
            max-width: 800px;
            max-height: 80vh;
            overflow-y: auto;
            padding: 20px;
            color: #e0e0e0;
            border: 1px solid #555;
        `;
        
        const priceTargets = loadAllPriceTargets();
        
        modal.innerHTML = `
            <h3 style="margin-top: 0; color: #17a2b8;">Fill Market Posting Forms</h3>
            <p>This will automatically fill the quantity and price fields for ${items.length} items with price targets.</p>
            <p style="font-size: 12px; color: #ffc107; background: #333; padding: 8px; border-radius: 4px; margin: 10px 0;">
                ⚠️ <strong>Note:</strong> This tool fills the forms but does NOT automatically submit them. 
                You will need to manually review and submit each listing for compliance with Torn.com's terms.
            </p>
            
            <div style="max-height: 400px; overflow-y: auto; margin: 20px 0;">
                ${items.map(item => {
                    const target = priceTargets[item.name];
                    const itemQty = item.quantity || item.qty || 1;
                    return `
                        <div style="display: flex; align-items: center; padding: 10px; border: 1px solid #444; margin-bottom: 5px; border-radius: 4px;">
                            <div style="flex: 1;">
                                <strong>${item.name}</strong><br>
                                <small>Available: ${itemQty} | Target Price: $${target.selectedPrice?.toLocaleString()}</small>
                            </div>
                            <div style="margin-left: 10px; min-width: 140px;">
                                <label style="font-size: 12px; display: block; margin-bottom: 5px;">Quantity to post:</label>
                                <div style="display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: 5px;">
                                    <button class="qty-btn-add" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="1" style="padding: 4px 8px; font-size: 11px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">+1</button>
                                    <button class="qty-btn-add" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="10" style="padding: 4px 8px; font-size: 11px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">+10</button>
                                    <button class="qty-btn-add" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="100" style="padding: 4px 8px; font-size: 11px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">+100</button>
                                    <button class="qty-btn-add" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="1000" style="padding: 4px 8px; font-size: 11px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">+1k</button>
                                    <button class="qty-btn" data-item="${item.name.replace(/"/g, '&quot;')}" data-qty="${itemQty}" style="padding: 4px 8px; font-size: 11px; background: #28a745; color: white; border: none; border-radius: 3px; cursor: pointer;">Max</button>
                                    <button class="qty-btn-reset" data-item="${item.name.replace(/"/g, '&quot;')}" style="padding: 4px 8px; font-size: 11px; background: #dc3545; color: white; border: none; border-radius: 3px; cursor: pointer;">✗</button>
                                </div>
                                <input type="number" id="qty-${item.name.replace(/"/g, '&quot;').replace(/[^a-zA-Z0-9]/g, '_')}" min="0" max="${itemQty}" value="0" style="width: 100%; padding: 4px; background: #444; color: #e0e0e0; border: 1px solid #666; border-radius: 3px; font-size: 12px;">
                            </div>
                        </div>
                    `;
                }).join('')}
            </div>
            
            <div style="text-align: center; margin-top: 20px;">
                <button id="confirm-post" style="background: #28a745; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; margin-right: 10px;">Fill Forms</button>
                <button id="cancel-post" style="background: #6c757d; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;">Cancel</button>
            </div>
        `;
        
        modalOverlay.appendChild(modal);
        document.body.appendChild(modalOverlay);
        
        // Add event listeners for quantity buttons
        modal.querySelectorAll('.qty-btn').forEach(btn => {
            btn.addEventListener('click', () => {
                const itemName = btn.dataset.item;
                const qty = parseInt(btn.dataset.qty);
                const sanitizedName = itemName.replace(/[^a-zA-Z0-9]/g, '_');
                const input = document.getElementById(`qty-${sanitizedName}`);
                if (input) {
                    input.value = qty;
                    // Add visual feedback
                    input.style.background = '#28a745';
                    setTimeout(() => {
                        input.style.background = '#444';
                    }, 200);
                } else {
                    console.warn(`Could not find input for item: ${itemName} (ID: qty-${sanitizedName})`);
                }
            });
        });

        // Add event listeners for quantity increment buttons
        modal.querySelectorAll('.qty-btn-add').forEach(btn => {
            btn.addEventListener('click', () => {
                const itemName = btn.dataset.item;
                const addQty = parseInt(btn.dataset.qty);
                const sanitizedName = itemName.replace(/[^a-zA-Z0-9]/g, '_');
                const input = document.getElementById(`qty-${sanitizedName}`);
                if (input) {
                    const currentQty = parseInt(input.value) || 0;
                    const maxQty = parseInt(input.max);
                    const newQty = Math.min(currentQty + addQty, maxQty);
                    input.value = newQty;
                    // Add visual feedback
                    input.style.background = '#007bff';
                    setTimeout(() => {
                        input.style.background = '#444';
                    }, 200);
                } else {
                    console.warn(`Could not find input for item: ${itemName} (ID: qty-${sanitizedName})`);
                }
            });
        });

        // Add event listeners for quantity reset buttons
        modal.querySelectorAll('.qty-btn-reset').forEach(btn => {
            btn.addEventListener('click', () => {
                const itemName = btn.dataset.item;
                const sanitizedName = itemName.replace(/[^a-zA-Z0-9]/g, '_');
                const input = document.getElementById(`qty-${sanitizedName}`);
                if (input) {
                    input.value = 0;
                    // Add visual feedback
                    input.style.background = '#dc3545';
                    setTimeout(() => {
                        input.style.background = '#444';
                    }, 200);
                } else {
                    console.warn(`Could not find input for item: ${itemName} (ID: qty-${sanitizedName})`);
                }
            });
        });
        
        // Handle confirm posting
        document.getElementById('confirm-post').addEventListener('click', () => {
            const postingData = items.map(item => {
                const sanitizedName = item.name.replace(/[^a-zA-Z0-9]/g, '_');
                const qtyInput = document.getElementById(`qty-${sanitizedName}`);
                const quantity = qtyInput ? parseInt(qtyInput.value) : 1;
                
                return {
                    name: item.name,
                    itemId: item.id,
                    quantity: quantity,
                    price: priceTargets[item.name].selectedPrice
                };
            }).filter(data => data.quantity > 0);
            
            modalOverlay.remove();
            executeMarketPosting(postingData);
        });
        
        document.getElementById('cancel-post').addEventListener('click', () => {
            modalOverlay.remove();
        });
        
        // Close on overlay click
        modalOverlay.addEventListener('click', (e) => {
            if (e.target === modalOverlay) {
                modalOverlay.remove();
            }
        });
    }
    
    async function executeMarketPosting(postingData) {
        if (postingData.length === 0) {
            showToast('No items to post', 'warning');
            return;
        }
        
        showToast(`Starting to fill forms for ${postingData.length} items...`, 'info');
        
        let successCount = 0;
        let errorCount = 0;
        
        for (const [index, item] of postingData.entries()) {
            try {
                showToast(`Filling form for ${item.name} (${index + 1}/${postingData.length})...`, 'info');
                
                const success = await postSingleItem(item);
                
                if (success) {
                    successCount++;
                } else {
                    errorCount++;
                    showToast(`✗ Failed to fill form for ${item.name}`, 'error');
                }
                
                // Wait between posts to avoid overwhelming the interface
                if (index < postingData.length - 1) {
                    await new Promise(resolve => setTimeout(resolve, 1500));
                }
                
            } catch (error) {
                errorCount++;
                console.error(`Error filling form for ${item.name}:`, error);
                showToast(`✗ Error filling form for ${item.name}: ${error.message}`, 'error');
            }
        }
        
        // Final summary with instructions
        const summary = `Form filling complete! Filled: ${successCount}, Errors: ${errorCount}`;
        showToast(summary, successCount > 0 ? 'success' : 'error');
        
        if (successCount > 0) {
            // Show manual submission instruction
            setTimeout(() => {
                showToast('📝 Forms filled! Please review and manually submit each listing.', 'info');
            }, 2000);
        }
    }
    
    async function postSingleItem(item) {
        // Wait for the page to be ready
        await waitForMarketPageReady();
        
        // Find the item row in the market interface
        const itemRow = findMarketItemRow(item.itemId, item.name);
        if (!itemRow) {
            throw new Error(`Could not find item ${item.name} in market interface`);
        }
        
        // Fill in quantity using the visible input (not the hidden one)
        const qtyInputs = itemRow.querySelectorAll('input[placeholder="Qty"]');
        const visibleQtyInput = Array.from(qtyInputs).find(input => input.type !== 'hidden');
        if (visibleQtyInput) {
            // Clear the field first
            visibleQtyInput.value = '';
            visibleQtyInput.focus();
            
            // Set the value and trigger events
            visibleQtyInput.value = item.quantity;
            visibleQtyInput.dispatchEvent(new Event('input', { bubbles: true }));
            visibleQtyInput.dispatchEvent(new Event('change', { bubbles: true }));
            
            // Simulate typing for better compatibility
            const inputEvent = new InputEvent('input', {
                bubbles: true,
                cancelable: true,
                data: item.quantity.toString()
            });
            visibleQtyInput.dispatchEvent(inputEvent);
        }
        
        // Fill in price using the visible input (not the hidden one)
        const priceInputs = itemRow.querySelectorAll('input[placeholder="Price"]');
        const visiblePriceInput = Array.from(priceInputs).find(input => input.type !== 'hidden');
        if (visiblePriceInput) {
            // Clear the field first
            visiblePriceInput.value = '';
            visiblePriceInput.focus();
            
            // Set the value and trigger events
            visiblePriceInput.value = item.price;
            visiblePriceInput.dispatchEvent(new Event('input', { bubbles: true }));
            visiblePriceInput.dispatchEvent(new Event('change', { bubbles: true }));
            
            // Simulate typing for better compatibility
            const inputEvent = new InputEvent('input', {
                bubbles: true,
                cancelable: true,
                data: item.price.toString()
            });
            visiblePriceInput.dispatchEvent(inputEvent);
        }
        
        // Show a confirmation message
        showToast(`✓ Filled ${item.name}: Qty ${item.quantity}, Price $${item.price.toLocaleString()}`, 'success');
        
        // Note: Manual submission required - the script fills the forms but doesn't auto-submit
        // This is safer and follows Torn.com's terms of service better
        
        return true;
    }
    
    async function waitForMarketPageReady() {
        return new Promise((resolve) => {
            const checkReady = () => {
                const itemRows = document.querySelectorAll('.itemRowWrapper___cFs4O');
                if (itemRows.length > 0) {
                    resolve();
                } else {
                    setTimeout(checkReady, 500);
                }
            };
            checkReady();
        });
    }
    
    function findMarketItemRow(itemId, itemName) {
        // Look for the item row by item name or ID
        const itemRows = document.querySelectorAll('.itemRowWrapper___cFs4O, .itemRow___Mf7bO');
        
        for (const row of itemRows) {
            // Check for item name in various elements
            const viewButton = row.querySelector('.viewInfoButton___jOjRg, button[aria-label*="View"]');
            if (viewButton) {
                const ariaLabel = viewButton.getAttribute('aria-label');
                if (ariaLabel) {
                    // Try exact match first
                    if (ariaLabel.includes(itemName)) {
                        return row.closest('.itemRowWrapper___cFs4O') || row;
                    }
                    
                    // Try partial match for items with long names
                    const cleanItemName = itemName.replace(/[^\w\s]/g, '').toLowerCase();
                    const cleanAriaLabel = ariaLabel.replace(/[^\w\s]/g, '').toLowerCase();
                    if (cleanAriaLabel.includes(cleanItemName) || cleanItemName.includes(cleanAriaLabel.split(' ')[0])) {
                        return row.closest('.itemRowWrapper___cFs4O') || row;
                    }
                }
            }
            
            // Alternative: check for item ID in controls attribute
            const controls = viewButton?.getAttribute('aria-controls');
            if (controls && controls.includes(`itemInfo-${itemId}-`)) {
                return row.closest('.itemRowWrapper___cFs4O') || row;
            }
            
            // Check for item name in text content as fallback
            const titleElement = row.querySelector('.title___Xo6Pm, .itemName, [class*="title"]');
            if (titleElement && titleElement.textContent.includes(itemName)) {
                return row.closest('.itemRowWrapper___cFs4O') || row;
            }
        }
        
        // If no exact match, try a broader search by scanning all text content
        for (const row of itemRows) {
            if (row.textContent.includes(itemName)) {
                // Double-check this isn't a false positive by looking for input fields
                const hasInputs = row.querySelectorAll('input[placeholder="Qty"], input[placeholder="Price"]').length >= 2;
                if (hasInputs) {
                    return row.closest('.itemRowWrapper___cFs4O') || row;
                }
            }
        }
        
        return null;
    }
    
    // Update API status display
    function updateApiStatusDisplay() {
        const apiStatus = document.getElementById('api-status');
        
        if (!apiStatus) return;
        
        if (apiKey) {
            apiStatus.style.color = '#28a745';
            
            if (apiItemsData) {
                const itemCount = Object.keys(apiItemsData).length;
                apiStatus.textContent = `✅ API ready | ${itemCount} items cached`;
            } else {
                apiStatus.textContent = '✅ API key loaded | Click "Fetch" to load item data';
            }
        } else {
            apiStatus.style.color = '#dc3545';
            apiStatus.textContent = '❌ No API key | Required for tradeable item detection and market prices';
        }
    }
    
    // Wait for page to be ready and initialize
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
    
})();