Junk Seller

Quickly sell unwanted items in your inventory to various shops

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

Advertisement:

// ==UserScript==
// @name         Junk Seller
// @namespace    http://tampermonkey.net/
// @version      0.2.0
// @description  Quickly sell unwanted items in your inventory to various shops
// @author       DF__Lykos [4070584]
// @match        https://www.torn.com/item.php*
// @match        https://www.torn.com/shops.php*
// @match        https://www.torn.com/bigalgunshop.php*
// @grant        none
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // ==================== Junk List (from price_comparison.txt) ====================
    const JUNK_LIST = [
      { id: 181,  name: "Bottle of Champagne",    type: "Alcohol",   seller: "Bits 'n' Bobs",      sellerLink: "https://www.torn.com/shops.php?step=bitsnbobs" },
      { id: 645,  name: "Safety Boots",            type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 176,  name: "Chain Mail",              type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 178,  name: "Flak Jacket",             type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 640,  name: "Kevlar Gloves",           type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 646,  name: "Hiking Boots",            type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 50,   name: "Outer Tactical Vest",     type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 49,   name: "Full Body Armor",         type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 647,  name: "Leather Helmet",          type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 32,   name: "Leather Vest",            type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 650,  name: "Leather Gloves",          type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 649,  name: "Leather Boots",           type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 648,  name: "Leather Pants",           type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 34,   name: "Bulletproof Vest",        type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 643,  name: "Construction Helmet",     type: "Armor",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 517,  name: "Weston Marlin 177",       type: "Car",       seller: "Docks",              sellerLink: "https://www.torn.com/shops.php?step=docks" },
      { id: 83,   name: "Dart Rampager",           type: "Car",       seller: "Docks",              sellerLink: "https://www.torn.com/shops.php?step=docks" },
      { id: 78,   name: "Edomondo NSX",            type: "Car",       seller: "Docks",              sellerLink: "https://www.torn.com/shops.php?step=docks" },
      { id: 523,  name: "Mercia SLR",              type: "Car",       seller: "Docks",              sellerLink: "https://www.torn.com/shops.php?step=docks" },
      { id: 520,  name: "Lolo 458",                type: "Car",       seller: "Docks",              sellerLink: "https://www.torn.com/shops.php?step=docks" },
      { id: 80,   name: "Bavaria M5",              type: "Car",       seller: "Docks",              sellerLink: "https://www.torn.com/shops.php?step=docks" },
      { id: 89,   name: "Edomondo ACD",            type: "Car",       seller: "Docks",              sellerLink: "https://www.torn.com/shops.php?step=docks" },
      { id: 79,   name: "Echo Quadrato",           type: "Car",       seller: "Docks",              sellerLink: "https://www.torn.com/shops.php?step=docks" },
      { id: 95,   name: "Oceania SS",              type: "Car",       seller: "Docks",              sellerLink: "https://www.torn.com/shops.php?step=docks" },
      { id: 404,  name: "Bandana",                 type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 625,  name: "Wetsuit",                 type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 621,  name: "Snorkel",                 type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 107,  name: "Trench Coat",             type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 430,  name: "Coconut Bra",             type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 626,  name: "Diving Gloves",           type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 414,  name: "Proda Sunglasses",        type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 412,  name: "Sports Shades",           type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 624,  name: "Bikini",                  type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 622,  name: "Flippers",                type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 413,  name: "Mountie Hat",             type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 623,  name: "Speedo",                  type: "Clothing",  seller: "TC Clothing",        sellerLink: "https://www.torn.com/shops.php?step=clothes" },
      { id: 54,   name: "Diamond Ring",            type: "Jewelry",   seller: "Jewelry Store",      sellerLink: "https://www.torn.com/shops.php?step=jewelry" },
      { id: 227,  name: "Spear",                   type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 173,  name: "Butterfly Knife",         type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 8,    name: "Axe",                     type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 224,  name: "Swiss Army Knife",        type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 110,  name: "Leather Bullwhip",        type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 1231, name: "Golf Club",               type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 401,  name: "Lead Pipe",               type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 4,    name: "Knuckle Dusters",         type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 3,    name: "Crowbar",                 type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 439,  name: "Frying Pan",              type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 1,    name: "Hammer",                  type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 5,    name: "Pen Knife",               type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 7,    name: "Dagger",                  type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 217,  name: "Claymore Sword",          type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 236,  name: "Kama",                    type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 402,  name: "Ice Pick",                type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 247,  name: "Katana",                  type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 11,   name: "Samurai Sword",           type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 400,  name: "Guandao",                 type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 438,  name: "Cricket Bat",             type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 9,    name: "Scimitar",                type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 6,    name: "Kitchen Knife",           type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 2,    name: "Baseball Bat",            type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 10,   name: "Chainsaw",                type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 111,  name: "Ninja Claws",             type: "Melee",     seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 1495, name: "Ambergris Lump",          type: "Other",     seller: "Jewelry Store",      sellerLink: "https://www.torn.com/shops.php?step=jewelry" },
      { id: 411,  name: "Model Space Ship",        type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 1498, name: "Tiger Bone Powder",       type: "Other",     seller: "Nikeh Performance",  sellerLink: "https://www.torn.com/shops.php?step=nikeh" },
      { id: 1497, name: "Uncut Diamonds",          type: "Other",     seller: "Jewelry Store",      sellerLink: "https://www.torn.com/shops.php?step=jewelry" },
      { id: 1483, name: "Insulin",                 type: "Other",     seller: "Pharmacy",           sellerLink: "https://www.torn.com/shops.php?step=pharmacy" },
      { id: 434,  name: "Yakitori Lantern",        type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 894,  name: "Cosmetics Case",          type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 1482, name: "Bearer Bond",             type: "Other",     seller: "Pawn Shop",          sellerLink: "https://www.torn.com/shops.php?step=pawnshop" },
      { id: 275,  name: "Jade Buddha",             type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 1251, name: "Boat Engine",             type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 409,  name: "Yucca Plant",             type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 896,  name: "Subway Pass",             type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 1211, name: "Fishing Rod",             type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 890,  name: "Headphones",              type: "Other",     seller: "Super Store",        sellerLink: "https://www.torn.com/shops.php?step=super" },
      { id: 410,  name: "Fire Hydrant",            type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 1345, name: "Prescription",            type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 1240, name: "Perfume",                 type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 436,  name: "Snowboard",               type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 1335, name: "Parking Permit",          type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 1336, name: "Birth Certificate",       type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 1086, name: "Driver's License",        type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 418,  name: "Tailor's Dummy",          type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 1341, name: "Concert Ticket",          type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 1253, name: "Tractor Part",            type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 279,  name: "Maneki Neko",             type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 1342, name: "Travel Visa",             type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 1256, name: "Machine Part",            type: "Other",     seller: "Bits 'n' Bobs",      sellerLink: "https://www.torn.com/shops.php?step=bitsnbobs" },
      { id: 358,  name: "Raw Ivory",               type: "Other",     seller: "Pawn Shop",          sellerLink: "https://www.torn.com/shops.php?step=pawnshop" },
      { id: 1491, name: "Ergotamine Ampoule",      type: "Other",     seller: "Pharmacy",           sellerLink: "https://www.torn.com/shops.php?step=pharmacy" },
      { id: 1337, name: "Diploma",                 type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 1493, name: "Whale Meat",              type: "Other",     seller: "Nikeh Performance",  sellerLink: "https://www.torn.com/shops.php?step=nikeh" },
      { id: 1496, name: "Natural Pearls",          type: "Other",     seller: "Jewelry Store",      sellerLink: "https://www.torn.com/shops.php?step=jewelry" },
      { id: 1486, name: "Turtle Shell",            type: "Other",     seller: "Jewelry Store",      sellerLink: "https://www.torn.com/shops.php?step=jewelry" },
      { id: 1494, name: "Pangolin Scales",         type: "Other",     seller: "Nikeh Performance",  sellerLink: "https://www.torn.com/shops.php?step=nikeh" },
      { id: 1485, name: "Shark Fin",               type: "Other",     seller: "Nikeh Performance",  sellerLink: "https://www.torn.com/shops.php?step=nikeh" },
      { id: 1492, name: "Counterfeit Manga",       type: "Other",     seller: "Pawn Shop",          sellerLink: "https://www.torn.com/shops.php?step=pawnshop" },
      { id: 1343, name: "Passport",                type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 1489, name: "Ephedrine Powder",        type: "Other",     seller: "Pharmacy",           sellerLink: "https://www.torn.com/shops.php?step=pharmacy" },
      { id: 1490, name: "Safrole Oil",             type: "Other",     seller: "Pharmacy",           sellerLink: "https://www.torn.com/shops.php?step=pharmacy" },
      { id: 1484, name: "Bear Gall",               type: "Other",     seller: "Nikeh Performance",  sellerLink: "https://www.torn.com/shops.php?step=nikeh" },
      { id: 1349, name: "License Plate",           type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 1339, name: "Bank Check",              type: "Other",     seller: "Print Shop",         sellerLink: "https://www.torn.com/shops.php?step=printstore" },
      { id: 406,  name: "Afro Comb",               type: "Other",     seller: "Recycling Center",   sellerLink: "https://www.torn.com/shops.php?step=recyclingcenter" },
      { id: 487,  name: "Thompson",                type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 249,  name: "SKS Carbine",             type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 26,   name: "AK-47",                   type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 231,  name: "Heckler & Koch SL8",      type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 241,  name: "Bushmaster Carbon 15",    type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 108,  name: "9mm Uzi",                 type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 252,  name: "Ithaca 37",               type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 25,   name: "P90",                     type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 232,  name: "SIG 550",                 type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 30,   name: "Steyr AUG",               type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 29,   name: "M16 A2 Rifle",            type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 23,   name: "Benelli M1 Tactical",     type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 22,   name: "Sawed-Off Shotgun",       type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 27,   name: "M4A1 Colt Carbine",       type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 174,  name: "XM8 Rifle",               type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 28,   name: "Benelli M4 Super",        type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 24,   name: "MP5 Navy",                type: "Primary",   seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 613,  name: "Harpoon",                 type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 175,  name: "Taser",                   type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 486,  name: "TMP",                     type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 230,  name: "Flare Gun",               type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 16,   name: "USP",                     type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 15,   name: "Beretta M9",              type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 489,  name: "Luger",                   type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 13,   name: "Raven MP25",              type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 485,  name: "Skorpion",                type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 189,  name: "S&W Revolver",            type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 244,  name: "Blowgun",                 type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 14,   name: "Ruger 57",                type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 19,   name: "Magnum",                  type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 20,   name: "Desert Eagle",            type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 18,   name: "Fiveseven",               type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 218,  name: "Crossbow",                type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 243,  name: "Taurus",                  type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 240,  name: "Type 98 Anti Tank",       type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 248,  name: "Qsz-92",                  type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 12,   name: "Glock 17",                type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 177,  name: "Cobra Derringer",         type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 483,  name: "MP5k",                    type: "Secondary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 257,  name: "Throwing Knife",          type: "Temporary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 242,  name: "HEG",                     type: "Temporary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
      { id: 220,  name: "Grenade",                 type: "Temporary", seller: "Big Al's Gun Shop",  sellerLink: "https://www.torn.com/bigalgunshop.php" },
    ];

    // Fast lookup: item id -> junk list entry
    const JUNK_MAP = Object.fromEntries(JUNK_LIST.map(e => [e.id, e]));

    // Our type names -> Torn API "cat" parameter values
    // (Armor = "Defensive" in the Torn v2 API enum)
    const TYPE_TO_API_CAT = {
        Alcohol:   'Alcohol',
        Armor:     'Defensive',
        Car:       'Car',
        Clothing:  'Clothing',
        Jewelry:   'Jewelry',
        Melee:     'Melee',
        Other:     'Other',
        Primary:   'Primary',
        Secondary: 'Secondary',
        Temporary: 'Temporary',
    };

    // Distinct API categories actually needed for our list
    const API_CATS = [...new Set(
        JUNK_LIST.map(e => TYPE_TO_API_CAT[e.type]).filter(Boolean)
    )];

    // ==================== Constants ====================
    const STORAGE_KEY_API     = 'sell_junk_api_key';
    const STORAGE_KEY_SESSION = 'sell_junk_session';
    const STORAGE_KEY_CONFIG  = 'sell_junk_config';
    const API_BASE            = 'https://api.torn.com/v2/user/inventory';

    // Categories excluded from junk by default (protect rank-war gear)
    const DEFAULT_OFF_CATS = new Set(['Armor', 'Melee', 'Primary', 'Secondary']);

    // ==================== Storage Helpers ====================
    function getApiKey() { return localStorage.getItem(STORAGE_KEY_API) || ''; }
    function setApiKey(k) { localStorage.setItem(STORAGE_KEY_API, k); }

    function getSession() {
        try { return JSON.parse(localStorage.getItem(STORAGE_KEY_SESSION)) || null; }
        catch { return null; }
    }
    function setSession(s) { localStorage.setItem(STORAGE_KEY_SESSION, JSON.stringify(s)); }
    function clearSession() { localStorage.removeItem(STORAGE_KEY_SESSION); }

    // Config: { excludedIds: number[] }  —  ids of items NOT considered junk
    function buildDefaultConfig() {
        return { excludedIds: JUNK_LIST.filter(e => DEFAULT_OFF_CATS.has(e.type)).map(e => e.id) };
    }
    function getConfig() {
        try {
            const raw = localStorage.getItem(STORAGE_KEY_CONFIG);
            if (raw) return JSON.parse(raw);
        } catch {}
        return buildDefaultConfig();
    }
    function saveConfig(cfg) { localStorage.setItem(STORAGE_KEY_CONFIG, JSON.stringify(cfg)); }

    // ==================== Inventory API ====================
    async function fetchCat(key, cat) {
        const now = Math.floor(Date.now() / 1000); // Unix timestamp — bypasses Torn's 1-hour cache
        const url = `${API_BASE}?cat=${encodeURIComponent(cat)}&offset=0&limit=250` +
                    `&timestamp=${now}` +
                    `&key=${encodeURIComponent(key)}&comment=SellJunk`;
        const resp = await fetch(url);
        if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
        const data = await resp.json();
        if (data.error) throw new Error(`API error ${data.error.code}: ${data.error.error}`);
        return data.inventory?.items ?? [];
    }

    async function fetchAllMatchingInventory(key) {
        const results = await Promise.all(
            API_CATS.map(cat =>
                fetchCat(key, cat).catch(err => {
                    console.warn(`[Sell Junk] cat=${cat}:`, err);
                    return [];
                })
            )
        );

        // Flatten, deduplicate by id+uid, keep only non-excluded items in our junk list.
        const excludedSet = new Set(getConfig().excludedIds);
        const seen = new Set();
        const matches = [];

        for (const items of results) {
            for (const inv of items) {
                const key = `${inv.id}_${inv.uid ?? ''}`;
                if (seen.has(key)) continue;
                seen.add(key);
                if (JUNK_MAP[inv.id] && !excludedSet.has(inv.id)) {
                    matches.push({ inv, entry: JUNK_MAP[inv.id] });
                }
            }
        }

        return matches;
    }

    // ==================== Loading Overlay ====================
    function showLoading() {
        removePanel();
        const ov = document.createElement('div');
        ov.id = 'sell-junk-panel-overlay';
        ov.style.cssText = overlayStyle();
        ov.innerHTML = `
            <div style="${boxStyle()};padding:30px 44px;text-align:center;">
                <div style="font-size:14px;margin-bottom:8px;">🔍 Checking inventory…</div>
                <div style="font-size:12px;color:#666;">
                    Querying ${API_CATS.length} categories in parallel
                </div>
            </div>`;
        document.body.appendChild(ov);
        return ov;
    }

    // ==================== Results Panel ====================
    function showResults(matches) {
        removePanel();

        // Group by shop, preserving insertion order
        const byShop = new Map();
        for (const { inv, entry } of matches) {
            const shop = entry.seller || '(Unknown)';
            if (!byShop.has(shop)) {
                byShop.set(shop, { link: entry.sellerLink, items: [] });
            }
            byShop.get(shop).items.push({ id: entry.id, name: entry.name, qty: inv.amount });
        }

        const ov = document.createElement('div');
        ov.id = 'sell-junk-panel-overlay';
        ov.style.cssText = overlayStyle();

        const panel = document.createElement('div');
        panel.style.cssText = [
            boxStyle(),
            'width:540px',
            'max-width:94vw',
            'max-height:82vh',
            'overflow-y:auto',
            'padding:20px 22px',
        ].join(';');

        /* ---- header ---- */
        let html = `
            <div style="display:flex;align-items:center;justify-content:space-between;
                        border-bottom:1px solid #383838;padding-bottom:10px;margin-bottom:14px;">
                <span style="font-size:15px;font-weight:bold;">🗑️ Sell Junk</span>
                <div style="display:flex;gap:6px;align-items:center;">
                    <button id="sj-config-btn" title="Configure which items are treated as junk"
                        style="${btnStyle('#1a3a5a','#7ab')};font-size:11px;padding:3px 9px;">
                        ⚙ Junk List
                    </button>
                    <button id="sj-key-btn" title="Update API key"
                        style="${btnStyle('#333','#888')};font-size:11px;padding:3px 9px;">
                        ⚙ Key
                    </button>
                    <button id="sj-close-btn"
                        style="${btnStyle('#444','#ccc')};padding:3px 10px;">✕</button>
                </div>
            </div>`;

        /* ---- body ---- */
        if (byShop.size === 0) {
            html += `
                <div style="text-align:center;padding:20px 0;">
                    <p style="color:#666;font-size:13px;margin:0 0 14px;">
                        No junk found in your inventory.
                    </p>
                    <p style="color:#555;font-size:12px;margin:0 0 14px;">
                        Weapons &amp; armor are excluded by default to protect rank war gear.<br>
                        Open the junk list config to add or remove items.
                    </p>
                    <button id="sj-config-btn-empty"
                        style="${btnStyle('#1a3a5a','#7ab')};padding:7px 16px;">
                        ⚙ Configure Junk List
                    </button>
                </div>`;
        } else {
            for (const [shop, { items }] of byShop) {
                html += `
                <div style="margin-bottom:12px;background:#111;border:1px solid #2e2e2e;
                            border-radius:5px;padding:10px 12px;">
                    <div style="margin-bottom:7px;">
                        <span style="font-weight:bold;font-size:13px;color:#c8c8c8;">
                            🏪 ${shop}
                        </span>
                    </div>
                    <ul style="margin:0;padding:0 0 0 14px;font-size:12px;color:#999;
                                line-height:1.7;">
                        ${items.map(i =>
                            `<li>${i.name} <span style="color:#5a9;font-size:11px;">×${i.qty}</span></li>`
                        ).join('')}
                    </ul>
                </div>`;
            }
        }

        /* ---- footer ---- */
        html += `
            <div style="border-top:1px solid #383838;padding-top:12px;margin-top:4px;
                        display:flex;align-items:center;justify-content:space-between;">
                <span style="font-size:11px;color:#555;">
                    ${matches.length} item type(s) · ${byShop.size} shop(s)
                </span>
                ${byShop.size > 0 ? `
                <button id="sj-go-shops-btn" style="${btnStyle('#1a6a30','#fff')}">
                    🛒 Go to Shops →
                </button>` : ''}
            </div>`;

        panel.innerHTML = html;
        ov.appendChild(panel);
        document.body.appendChild(ov);

        ov.addEventListener('click', e => { if (e.target === ov) removePanel(); });
        panel.querySelector('#sj-close-btn').addEventListener('click', removePanel);
        panel.querySelector('#sj-key-btn').addEventListener('click', () => {
            removePanel();
            showApiKeyModal();
        });
        panel.querySelector('#sj-config-btn')?.addEventListener('click', () => {
            removePanel();
            showConfigPanel();
        });
        panel.querySelector('#sj-config-btn-empty')?.addEventListener('click', () => {
            removePanel();
            showConfigPanel();
        });
        panel.querySelector('#sj-go-shops-btn')?.addEventListener('click', () => {
            startSession(byShop);
        });
    }

    // ==================== Select All Junk ====================
    /**
     * Iterates every <li data-item="…"> in .sell-items-list and, for each one
     * whose item-ID is in the current session's junk list:
     *  • choice-container items (qty=1): clicks the label to tick the checkbox
     *  • input-money-group items (qty>1): fills the visible text input with
     *    data-money (max qty) and fires events so Torn's JS reacts
     *
     * Full console logging is included — open DevTools > Console and look for
     * [Sell Junk] entries to diagnose any issues.
     */
    function selectAllJunk(btn, _attempt) {
        const attempt = _attempt || 1;
        const LOG = (...a) => console.log('[Sell Junk]', ...a);

        // ── 1. Read session ───────────────────────────────────────────────
        const session = getSession();
        if (!session) {
            LOG('selectAllJunk: no active session in localStorage');
            return;
        }
        const { shops, currentIndex } = session;
        const current = shops[currentIndex];
        LOG(`selectAllJunk: shop="${current.name}" items=`, current.items);

        // ── 2. Build junk-ID set (handle stale sessions missing .id) ─────
        //    Fallback: look up by name via JUNK_MAP if id is absent
        const junkIds = new Set();
        for (const item of current.items) {
            if (item.id !== undefined && item.id !== null) {
                junkIds.add(String(item.id));
            } else {
                // Stale session — recover id from JUNK_MAP by name
                const found = JUNK_LIST.find(e => e.name === item.name);
                if (found) junkIds.add(String(found.id));
            }
        }
        LOG('selectAllJunk: junkIds =', [...junkIds]);

        // ── 3. Wait for sell list ─────────────────────────────────────────
        const sellList = document.querySelector('.sell-items-list');
        if (!sellList) {
            LOG(`selectAllJunk: .sell-items-list not found (attempt ${attempt})`);
            if (attempt <= 10) {
                if (btn && attempt === 1) { btn.textContent = 'Waiting…'; btn.disabled = true; }
                setTimeout(() => selectAllJunk(btn, attempt + 1), 500);
            } else {
                LOG('selectAllJunk: gave up waiting for .sell-items-list');
                if (btn) { btn.textContent = '☑ Select All Junk'; btn.disabled = false; }
            }
            return;
        }

        const allLis = sellList.querySelectorAll('li[data-item]');
        LOG(`selectAllJunk: found ${allLis.length} li[data-item] rows in sell list`);

        let count = 0;

        allLis.forEach(li => {
            const dataItem = li.getAttribute('data-item');
            if (!junkIds.has(dataItem)) {
                LOG(`  skip data-item="${dataItem}" (not in junk list)`);
                return;
            }
            LOG(`  process data-item="${dataItem}"`);

            // ── Toggle (qty = 1): choice-container checkbox ───────────────
            const checkbox = li.querySelector('.choice-container input[type="checkbox"]');
            if (checkbox) {
                LOG(`    → checkbox found, checked=${checkbox.checked}`);
                if (!checkbox.checked) {
                    const label = li.querySelector('label.marker-css');
                    if (label) {
                        LOG('    → clicking label');
                        label.click();
                    } else {
                        LOG('    → clicking checkbox directly');
                        checkbox.click();
                    }
                }
                count++;
                return;
            }

            // ── Quantity input (qty > 1): input-money-group ───────────────
            const textInput = li.querySelector('.input-money-group input[type="text"]');
            if (textInput) {
                const max = textInput.getAttribute('data-money') || '';
                LOG(`    → text input found, data-money="${max}", current value="${textInput.value}"`);

                // Use native setter so React/framework change detection triggers
                const nativeSetter = Object.getOwnPropertyDescriptor(
                    window.HTMLInputElement.prototype, 'value'
                )?.set;
                if (nativeSetter) {
                    nativeSetter.call(textInput, max);
                } else {
                    textInput.value = max;
                }

                textInput.dispatchEvent(new Event('input',  { bubbles: true }));
                textInput.dispatchEvent(new Event('change', { bubbles: true }));
                textInput.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true }));
                count++;
                return;
            }

            LOG(`    → no known control found in this li`);
        });

        LOG(`selectAllJunk: done — ${count} item(s) acted on`);

        // Visual feedback
        if (btn) {
            const orig = '☑ Select All Junk';
            btn.textContent = count > 0 ? `✔ ${count} selected` : '⚠ Nothing matched';
            btn.disabled = true;
            setTimeout(() => { btn.textContent = orig; btn.disabled = false; }, 2500);
        }
    }

    // ==================== Session: start & shop overlay ====================
    function startSession(byShop) {
        const shops = [];
        for (const [name, { link, items }] of byShop) {
            shops.push({ name, link, items });
        }
        setSession({ shops, currentIndex: 0 });
        removePanel();
        window.location.href = shops[0].link;
    }

    function showShopOverlay() {
        const session = getSession();
        if (!session || !session.shops) return;

        const { shops, currentIndex } = session;
        if (currentIndex >= shops.length) { clearSession(); return; }

        const current = shops[currentIndex];
        const isLast  = currentIndex === shops.length - 1;
        const total   = shops.length;

        // Remove any stale overlay from a previous inject attempt
        document.getElementById('sell-junk-shop-overlay')?.remove();

        const el = document.createElement('div');
        el.id = 'sell-junk-shop-overlay';
        el.style.cssText = [
            'position:fixed',
            'top:24px',
            'right:24px',
            'z-index:99999',
            'width:300px',
            boxStyle(),
            'padding:14px 16px',
        ].join(';');

        el.innerHTML = `
            <div style="display:flex;align-items:center;justify-content:space-between;
                        border-bottom:1px solid #383838;padding-bottom:8px;margin-bottom:10px;">
                <span style="font-size:13px;font-weight:bold;">🗑️ Sell Junk Mode</span>
                <span style="font-size:11px;color:#555;background:#222;padding:2px 7px;
                             border-radius:10px;">${currentIndex + 1} / ${total}</span>
            </div>
            <div style="font-size:13px;font-weight:bold;color:#c8c8c8;margin-bottom:8px;">
                🏪 ${current.name}
            </div>
            <ul style="margin:0 0 10px;padding:0 0 0 14px;font-size:12px;color:#999;
                       line-height:1.8;">
                ${current.items.map(i =>
                    `<li>${i.name} <span style="color:#5a9;font-size:11px;">×${i.qty}</span></li>`
                ).join('')}
            </ul>
            <button id="sj-select-all"
                style="${btnStyle('#5a4a00','#f0c040')};width:100%;margin-bottom:10px;
                       text-align:center;border:1px solid #7a6a00;">
                ☑ Select All Junk
            </button>
            <div style="display:flex;gap:8px;justify-content:flex-end;">
                <button id="sj-mode-cancel"
                    style="${btnStyle('#444','#bbb')}">✕ Cancel</button>
                <button id="sj-mode-next"
                    style="${btnStyle(isLast ? '#2a7a3a' : '#1a4fa0', '#fff')}">
                    ${isLast ? '✓ Finish' : 'Next Shop →'}
                </button>
            </div>`;

        document.body.appendChild(el);

        el.querySelector('#sj-select-all').addEventListener('click', function () {
            selectAllJunk(this);
        });

        el.querySelector('#sj-mode-cancel').addEventListener('click', () => {
            clearSession();
            el.remove();
        });

        el.querySelector('#sj-mode-next').addEventListener('click', () => {
            if (isLast) {
                clearSession();
                el.remove();
            } else {
                setSession({ shops, currentIndex: currentIndex + 1 });
                window.location.href = shops[currentIndex + 1].link;
            }
        });
    }

    // ==================== Config Panel ====================
    function showConfigPanel() {
        removePanel();

        const cfg        = getConfig();
        const excludedSet = new Set(cfg.excludedIds);

        // Group JUNK_LIST by category; sort: non-default-off first then alpha
        const byCategory = new Map();
        for (const item of JUNK_LIST) {
            if (!byCategory.has(item.type)) byCategory.set(item.type, []);
            byCategory.get(item.type).push(item);
        }
        const sortedCats = [...byCategory.keys()].sort((a, b) => {
            const aOff = DEFAULT_OFF_CATS.has(a) ? 1 : 0, bOff = DEFAULT_OFF_CATS.has(b) ? 1 : 0;
            return aOff !== bOff ? aOff - bOff : a.localeCompare(b);
        });

        const ov = document.createElement('div');
        ov.id = 'sell-junk-panel-overlay';
        ov.style.cssText = overlayStyle();

        const panel = document.createElement('div');
        panel.style.cssText = [
            boxStyle(), 'width:560px', 'max-width:94vw',
            'max-height:86vh', 'overflow-y:auto', 'padding:20px 22px',
        ].join(';');

        const cbBase = 'width:14px;height:14px;margin-right:7px;cursor:pointer;' +
                       'accent-color:#4A90E2;flex-shrink:0;';

        let html = `
            <!-- header -->
            <div style="display:flex;align-items:center;justify-content:space-between;
                        border-bottom:1px solid #383838;padding-bottom:10px;margin-bottom:12px;">
                <span style="font-size:15px;font-weight:bold;">⚙ Configure Junk List</span>
                <div style="display:flex;gap:6px;">
                    <button id="sj-cfg-reset" style="${btnStyle('#3a2000','#ca6')};font-size:11px;padding:3px 9px;"
                            title="Restore factory defaults (weapons &amp; armor excluded)">
                        ↺ Defaults
                    </button>
                    <button id="sj-cfg-close" style="${btnStyle('#444','#ccc')};padding:3px 10px;">✕</button>
                </div>
            </div>
            <p style="font-size:12px;color:#666;margin:0 0 14px;line-height:1.6;">
                ✔ Checked items are treated as junk and will appear in scan results.<br>
                🛡 <em>Weapons &amp; armor are unchecked by default to protect rank war equipment.</em>
            </p>`;

        for (const cat of sortedCats) {
            const items     = byCategory.get(cat);
            const isProtected = DEFAULT_OFF_CATS.has(cat);
            // Category is "on" if at least one item in it is NOT excluded
            const catCheckedCount = items.filter(i => !excludedSet.has(i.id)).length;
            const catChecked      = catCheckedCount === items.length ? 'checked' : '';
            const catIndet        = catCheckedCount > 0 && catCheckedCount < items.length;

            html += `
            <div style="margin-bottom:8px;border:1px solid #2e2e2e;border-radius:5px;overflow:hidden;">
                <div style="display:flex;align-items:center;padding:6px 10px;background:#161616;">
                    <input type="checkbox" class="sj-cfg-cat-cb" data-cat="${cat}"
                           id="sj-cfg-cat-${cat}" ${catChecked} style="${cbBase}">
                    <label for="sj-cfg-cat-${cat}"
                           style="font-weight:bold;font-size:12px;color:#c8c8c8;cursor:pointer;flex:1;">
                        ${cat}${isProtected ? ' <span style="color:#888;font-size:10px;">🛡 protected by default</span>' : ''}
                    </label>
                    <span style="font-size:11px;color:#555;">${items.length} item${items.length > 1 ? 's' : ''}</span>
                </div>
                <ul style="margin:0;padding:0;list-style:none;">
                    ${items.map(item => {
                        const itemChecked = excludedSet.has(item.id) ? '' : 'checked';
                        return `
                    <li style="display:flex;align-items:center;padding:4px 10px 4px 26px;
                               border-top:1px solid #1e1e1e;">
                        <input type="checkbox" class="sj-cfg-item-cb" data-cat="${cat}"
                               data-id="${item.id}" id="sj-cfg-itm-${item.id}"
                               ${itemChecked} style="${cbBase}">
                        <label for="sj-cfg-itm-${item.id}"
                               style="flex:1;font-size:12px;color:#999;cursor:pointer;">
                            ${item.name}
                        </label>
                        <span style="font-size:11px;color:#555;">${item.seller}</span>
                    </li>`;
                    }).join('')}
                </ul>
            </div>`;
        }

        html += `
            <div style="border-top:1px solid #383838;padding-top:12px;margin-top:4px;
                        display:flex;gap:8px;justify-content:flex-end;">
                <button id="sj-cfg-cancel" style="${btnStyle('#444','#ccc')}">Cancel</button>
                <button id="sj-cfg-save"   style="${btnStyle('#1a4fa0','#fff')}">💾 Save &amp; Rescan</button>
            </div>`;

        panel.innerHTML = html;
        ov.appendChild(panel);
        document.body.appendChild(ov);

        // Fix indeterminate state (can't be set via HTML attribute)
        panel.querySelectorAll('.sj-cfg-cat-cb').forEach(cb => {
            const cat   = cb.dataset.cat;
            const items = [...panel.querySelectorAll(`.sj-cfg-item-cb[data-cat="${cat}"]`)];
            const n     = items.filter(i => i.checked).length;
            cb.indeterminate = n > 0 && n < items.length;
        });

        /* ── helpers ── */
        function syncCatCb(cat) {
            const items = [...panel.querySelectorAll(`.sj-cfg-item-cb[data-cat="${cat}"]`)];
            const n     = items.filter(i => i.checked).length;
            const catCb = panel.querySelector(`.sj-cfg-cat-cb[data-cat="${cat}"]`);
            if (!catCb) return;
            catCb.checked       = n === items.length;
            catCb.indeterminate = n > 0 && n < items.length;
        }

        function applyDefaults() {
            panel.querySelectorAll('.sj-cfg-item-cb').forEach(cb => {
                cb.checked = !DEFAULT_OFF_CATS.has(cb.dataset.cat);
            });
            panel.querySelectorAll('.sj-cfg-cat-cb').forEach(cb => {
                cb.checked       = !DEFAULT_OFF_CATS.has(cb.dataset.cat);
                cb.indeterminate = false;
            });
        }

        /* ── events ── */
        panel.querySelectorAll('.sj-cfg-cat-cb').forEach(catCb =>
            catCb.addEventListener('change', () => {
                panel.querySelectorAll(`.sj-cfg-item-cb[data-cat="${catCb.dataset.cat}"]`)
                     .forEach(cb => { cb.checked = catCb.checked; });
                catCb.indeterminate = false;
            })
        );

        panel.querySelectorAll('.sj-cfg-item-cb').forEach(itemCb =>
            itemCb.addEventListener('change', () => syncCatCb(itemCb.dataset.cat))
        );

        panel.querySelector('#sj-cfg-reset').addEventListener('click', applyDefaults);

        panel.querySelector('#sj-cfg-cancel').addEventListener('click', removePanel);

        panel.querySelector('#sj-cfg-save').addEventListener('click', () => {
            // Build excludedIds from unchecked item checkboxes
            const excludedIds = [...panel.querySelectorAll('.sj-cfg-item-cb:not(:checked)')]
                .map(cb => Number(cb.dataset.id));
            saveConfig({ excludedIds });
            removePanel();
            fetchAndShow();   // rescan with new config
        });

        ov.addEventListener('click', e => { if (e.target === ov) removePanel(); });
        panel.querySelector('#sj-cfg-close').addEventListener('click', removePanel);
    }

    // ==================== Fetch & Display ====================
    async function fetchAndShow() {
        const key = getApiKey();
        const loading = showLoading();
        try {
            const matches = await fetchAllMatchingInventory(key);
            loading.remove();
            showResults(matches);
        } catch (err) {
            loading.remove();
            if (/API error/i.test(err.message)) {
                showApiKeyModal(`⚠ ${err.message} — please re-enter your key.`);
            } else {
                console.error('[Sell Junk]', err);
                alert(`[Sell Junk] Unexpected error:\n${err.message}`);
            }
        }
    }

    // ==================== API Key Modal ====================
    function showApiKeyModal(errorMsg = '') {
        removePanel();
        const currentKey = getApiKey();
        const masked = currentKey
            ? `${currentKey.slice(0, 6)}••••••••••${currentKey.slice(-4)}`
            : '';

        const ov = document.createElement('div');
        ov.id = 'sell-junk-panel-overlay';
        ov.style.cssText = overlayStyle();

        const modal = document.createElement('div');
        modal.style.cssText = [boxStyle(), 'width:440px', 'max-width:92vw', 'padding:22px 24px'].join(';');

        modal.innerHTML = `
            <div style="font-size:15px;font-weight:bold;margin-bottom:10px;
                        border-bottom:1px solid #383838;padding-bottom:10px;">
                🗑️ Sell Junk — API Key Setup
            </div>
            <p style="font-size:12px;color:#888;margin:0 0 12px;">
                A <strong>Limited Access</strong> key with <strong>Inventory</strong>
                permission is required.<br>
                Create one at
                <a href="/preferences.php#tab=api" target="_blank"
                   style="color:#4A90E2;">Preferences → API Keys</a>.
            </p>
            ${currentKey ? `
            <p style="font-size:12px;color:#5a8;margin:0 0 10px;">
                ✔ Stored key:
                <code style="background:#222;padding:2px 5px;border-radius:3px;
                             font-size:11px;">${masked}</code>
            </p>` : ''}
            ${errorMsg ? `
            <p style="font-size:12px;color:#e05555;margin:0 0 10px;">${errorMsg}</p>` : ''}
            <label for="sj-api-input"
                   style="font-size:12px;display:block;margin-bottom:5px;">API Key:</label>
            <input id="sj-api-input" type="text" autocomplete="off"
                placeholder="Paste your Limited Access API key here"
                value="${currentKey}"
                style="width:100%;box-sizing:border-box;padding:8px 10px;
                       background:#111;color:#eee;border:1px solid #555;
                       border-radius:4px;font-size:13px;margin-bottom:10px;outline:none;" />
            <div id="sj-modal-status"
                 style="font-size:12px;min-height:18px;margin-bottom:12px;"></div>
            <div style="display:flex;gap:8px;justify-content:flex-end;">
                <button id="sj-modal-cancel" style="${btnStyle('#555','#ccc')}">Cancel</button>
                <button id="sj-modal-save"   style="${btnStyle('#1a4fa0','#fff')}">
                    Save &amp; Scan Inventory
                </button>
            </div>`;

        ov.appendChild(modal);
        document.body.appendChild(ov);

        setTimeout(() => {
            const inp = document.getElementById('sj-api-input');
            if (inp) { inp.focus(); inp.select(); }
        }, 50);

        ov.addEventListener('click', e => { if (e.target === ov) removePanel(); });
        modal.querySelector('#sj-modal-cancel').addEventListener('click', removePanel);

        modal.querySelector('#sj-modal-save').addEventListener('click', async () => {
            const key  = document.getElementById('sj-api-input').value.trim();
            const stat = document.getElementById('sj-modal-status');
            const btn  = modal.querySelector('#sj-modal-save');

            if (!key) {
                stat.style.color = '#e05555';
                stat.textContent = '⚠ Please enter an API key.';
                return;
            }

            stat.style.color = '#888';
            stat.textContent = 'Validating…';
            btn.disabled = true;

            try {
                // Quick validation: fetch a single small category via v2
                const resp = await fetch(
                    `${API_BASE}?cat=Alcohol&offset=0&limit=1` +
                    `&key=${encodeURIComponent(key)}&comment=SellJunkValidate`
                );
                if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
                const data = await resp.json();
                if (data.error) throw new Error(`${data.error.code}: ${data.error.error}`);

                setApiKey(key);
                updateButtonIndicator();
                stat.style.color = '#5a8';
                stat.textContent = '✔ Key saved! Scanning inventory…';

                setTimeout(() => {
                    removePanel();
                    fetchAndShow();
                }, 900);

            } catch (err) {
                stat.style.color = '#e05555';
                stat.textContent = `✗ Validation failed: ${err.message}`;
                btn.disabled = false;
            }
        });
    }

    // ==================== Shared Style Helpers ====================
    function overlayStyle() {
        return [
            'position:fixed', 'top:0', 'left:0', 'width:100%', 'height:100%',
            'background:rgba(0,0,0,0.65)', 'z-index:99999',
            'display:flex', 'align-items:center', 'justify-content:center',
        ].join(';');
    }
    function boxStyle() {
        return [
            'background:var(--default-bg-panel-color,#1a1a1a)',
            'color:var(--default-color,#ddd)',
            'border:1px solid var(--default-bg-panel-border-color,#3a3a3a)',
            'border-radius:6px',
            'box-shadow:0 6px 28px rgba(0,0,0,0.6)',
            'font-family:inherit',
        ].join(';');
    }
    function btnStyle(bg, color) {
        return `cursor:pointer;background:${bg};color:${color};border:none;` +
               `border-radius:4px;padding:5px 12px;font-size:12px;font-family:inherit;`;
    }

    function removePanel() {
        document.getElementById('sell-junk-panel-overlay')?.remove();
    }

    // ==================== Button Indicator ====================
    function updateButtonIndicator() {
        const dot = document.getElementById('sell-junk-key-dot');
        if (dot) dot.style.display = getApiKey() ? 'inline' : 'none';
    }

    // ==================== Button Click ====================
    function handleClick() {
        if (!getApiKey()) {
            showApiKeyModal();
        } else {
            // Always clear any previous session and re-fetch fresh inventory
            clearSession();
            document.getElementById('sell-junk-shop-overlay')?.remove();
            fetchAndShow();
        }
    }

    // ==================== Button Injection ====================
    function injectButton() {
        const linksBar = document.getElementById('top-page-links-list');
        if (!linksBar || document.getElementById('sell-junk-btn')) return;

        const btn = document.createElement('a');
        btn.id = 'sell-junk-btn';
        btn.setAttribute('role', 'button');
        btn.setAttribute('aria-label', 'Sell Junk');
        btn.className = 'events t-clear h c-pointer m-icon line-h24 right last';
        btn.style.cssText = 'cursor:pointer;';

        btn.innerHTML = `
            <span class="icon-wrap svg-icon-wrap">
                <span class="link-icon-svg events">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"
                         width="16" height="16" style="vertical-align:middle;">
                        <path fill="#777"
                            d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0
                               1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1
                               0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5
                               0 0 0 1 0z"/>
                        <path fill="#777"
                            d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2
                               0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1
                               1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1
                               1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0
                               1-1V4.059L11.882 4zM2.5 3h11V2h-11z"/>
                    </svg>
                </span>
            </span>
            <span id="sell-junk-btn-text">Sell Junk</span><span
                id="sell-junk-key-dot"
                title="API key stored — click to scan inventory"
                style="display:none;color:#4CAF50;margin-left:3px;font-size:10px;">●</span>`;

        btn.addEventListener('click', handleClick);

        // Mirror 冰娃.js: insert after last <a> in the bar
        const links = linksBar.querySelectorAll('a');
        const last  = links.length ? links[links.length - 1] : null;
        if (last) last.insertAdjacentElement('afterend', btn);
        else       linksBar.appendChild(btn);

        updateButtonIndicator();
    }

    // ==================== Initialisation ====================
    function waitAndInject(maxAttempts = 30, intervalMs = 400) {
        let attempts = 0;
        const timer = setInterval(() => {
            attempts++;
            if (document.getElementById('top-page-links-list')) {
                clearInterval(timer);
                injectButton();
            } else if (attempts >= maxAttempts) {
                clearInterval(timer);
                console.warn('[Sell Junk] Could not find #top-page-links-list');
            }
        }, intervalMs);
    }

    // Show shop overlay on all matched pages when a session is running.
    // Small delay so the page's own CSS variables are applied before rendering.
    if (getSession()) {
        setTimeout(showShopOverlay, 300);
    }

    // Inject the toolbar button only on item.php
    if (window.location.pathname === '/item.php') {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', waitAndInject);
        } else {
            waitAndInject();
        }
    }

})();