MouseHunt Map Colour Board

Displays uncaught mice in right side of main UI

// ==UserScript==
// @name         MouseHunt Map Colour Board
// @namespace    https://greasyfork.org/en/users/735492-mouseindustry
// @version      1.0.0
// @description  Displays uncaught mice in right side of main UI
// @author       mouseindustry
// @match        https://www.mousehuntgame.com/*
// @match        http://www.mousehuntgame.com/*
// @grant        none
// ==/UserScript==

(() => {
    "use strict";

    /* ================== Config ================== */
    const CFG = {
        RIGHT_COL_SEL: ".pageFrameView-column.right",
        ANCHOR_ID    : "mh-map-board-anchor",
        PANEL_ID     : "mh-map-board",
        STYLE_ID     : "mh-map-board-style",
        ENDPOINT     : "https://www.mousehuntgame.com/managers/ajax/users/treasuremap_v2.php",
        WIDTH_MIN    : 220,
        WIDTH_MAX    : 360,
        OFFSET_LEFT  : 8,
        OFFSET_TOP   : 0,
    };

    /* ============== Registry API ============== */
    const REGISTRY = [];
    function registerMap(name, aliases, sections) {
        const groups = [];
        const locOrder = [];
        const tierOrder = [];
        const knownMice = new Set();

        for (const sec of sections) {
            const parts = String(sec.label || "").split("/").map(s => s.trim());
            const loc  = parts[0] || "Other";
            const tier = parts[1] || sec.label || "Other";
            if (!locOrder.includes(loc)) locOrder.push(loc);
            if (!tierOrder.includes(tier)) tierOrder.push(tier);

            const arr = (sec.mice || []).map(m => {
                if (typeof m === "string") { knownMice.add(m); return [m, ""]; }
                const n = m?.name ?? ""; knownMice.add(n);
                return [n, m?.ar ?? ""];
            });

            groups.push([loc, arr, 0, tier, 0, sec.color || "#e5e7eb"]);
        }

        REGISTRY.push({
            meta: {
                key: name.toLowerCase().replace(/\s+/g,"_"),
                label: name,
                matcher: ({ mapName, goals }) => {
                    let score = 0;
                    const n = (mapName || "").toLowerCase();
                    if (n.includes(name.toLowerCase())) score += 8;
                    for (const a of (aliases || [])) if (n.includes(String(a).toLowerCase())) score += 4;
                    const overlap = (goals || []).reduce((c,g)=>c + (knownMice.has(g.name) ? 1 : 0), 0);
                    score += overlap * 0.75;
                    return score;
                },
            },
            locOrder,
            tierOrder,
            groups,
            humanTier: (t)=>t,
        });
    }

    /* ================== MAP REGISTRATIONS ================== */
    // registerMap("Map Name", ["alias1","alias2"], [
    //   { label:"Location / cheese type or tier", color:"#hex", mice:[
    //       "Mouse A",
    //       {name:"Mouse B", ar:"34.5%"}
    //     ]},
    //   ...
    // ])

    // --- Bountiful Beanstalk ---
    registerMap("Bountiful Beanstalk", ["Beanstalk"], [
        { label: "Beanstalk / SB", color:"#96b78a", mice:[{name:"Budrich Thornborn", ar:"48.27%"}, {name:"Leafton Beanwell", ar:"46.76%"}, {name:"Herbaceous Bravestalk", ar:"4.97%"}]},
        { label: "Beanstalk / Boss", color:"#45890e", mice:[{name:"Vinneus Stalkhome", ar:"100%"}]},

        { label: "Dungeon / SB", color:"#dde1f4", mice:[{name:"Peaceful Prisoner", ar:"25.74%"}, {name:"Diminutive Detainee", ar:"34.43%"}, {name:"Smug Smuggler", ar:"39.83%"}]},
        { label: "Dungeon / Beanster", color:"#b7bddc", mice:[{name:"Cell Sweeper", ar:"30.83%"}, {name:"Jovial Jailor", ar:"44.56%"}, {name:"Lethargic Guard", ar:"24.62%"}]},
        { label: "Dungeon / Lavish", color:"#919ac7", mice:[{name:"Gate Keeper", ar:"50.41%"}, {name:"Key Master", ar:"49.59%"}]},
        { label: "Dungeon / Royal", color:"#4257a6", mice:[{name:"Wrathful Warden", ar:"100%"}]},
        { label: "Dungeon / Boss", color:"#24347c", mice:[{name:"Dungeon Master", ar:"100%"}]},

        { label: "Ballroom / SB", color:"#f7dadb", mice:[{name:"Whimsical Waltzer", ar:"26.64%"}, {name:"Sassy Salsa Dancer", ar:"34.23%"}, {name:"Baroque Dancer", ar:"39.13%"}]},
        { label: "Ballroom / Beanster", color:"#e0b1b2", mice:[{name:"Obstinate Oboist", ar:"31.17%"}, {name:"Peevish Piccoloist", ar:"39.40%"}, {name:"Sultry Saxophonist", ar:"29.44%"}]},
        { label: "Ballroom / Lavish", color:"#cc8788", mice:[{name:"Violent Violinist", ar:"50.48%"}, {name:"Chafed Cellist", ar:"49.52%"}]},
        { label: "Ballroom / Royal", color:"#ae141b", mice:[{name:"Treacherous Tubaist", ar:"100%"}]},
        { label: "Ballroom / Boss", color:"#7e0711", mice:[{name:"Malevolent Maestro", ar:"100%"}]},

        { label: "Great Hall / SB", color:"#fce6d5", mice:[{name:"Clumsy Cupbearer", ar:"25.16%"}, {name:"Plotting Page", ar:"34.95%"}, {name:"Scheming Squire", ar:"39.89%"}]},
        { label: "Great Hall / Beanster", color:"#f2d0b3", mice:[{name:"Vindictive Viscount", ar:"26.27%"}, {name:"Baroness Von Bean", ar:"39.58%"}, {name:"Cagey Countess", ar:"34.15%"}]},
        { label: "Great Hall / Lavish", color:"#e8ba8e", mice:[{name:"Dastardly Duchess", ar:"49.12%"}, {name:"Malicious Marquis", ar:"50.88%"}]},
        { label: "Great Hall / Royal", color:"#d68d0a", mice:[{name:"Pernicious Prince", ar:"100%"}]},
        { label: "Great Hall / Boss", color:"#a95b04", mice:[{name:"Mythical Giant King", ar:"100%"}]},
    ]);

    // --- School of Sorcery ---
    registerMap("School of Sorcery", ["Sorcery","School"], [
        { label: "Outside / Boss", color:"#ddd", mice:[{name:"Hall Monitor", ar:"100%"}] },

        { label: "Final Exam Arcane / Std", color:"#ffabab", mice:[{name:"Sleep Starved Scholar", ar:"10.06%"}] },
        { label: "Final Exam Arcane / AAC", color:"#ff8282", mice:[{name:"Class Clown", ar:"9.83%"}] },
        { label: "Final Exam Arcane / MMC", color:"#ff5858", mice:[{name:"Tyrannical Thaumaturge", ar:"10.09%"}] },
        { label: "Final Exam Arcane / Boss", color:"#754BAC", mice:[{name:"Mythical Master Sorcerer", ar:"100%"}] },

        { label: "Final Exam Shadow / Std", color:"#b2bbf2", mice:[{name:"Cheat Sheet Conjurer", ar:"11.28%"}] },
        { label: "Final Exam Shadow / AAC", color:"#8b9aeb", mice:[{name:"Celestial Summoner", ar:"9.49%"}] },
        { label: "Final Exam Shadow / MMC", color:"#6578e5", mice:[{name:"Data Devourer", ar:"10.36%"}] },
        { label: "Final Exam Shadow / Boss", color:"#754BAC", mice:[{name:"Mythical Master Sorcerer", ar:"100%"}] },

        { label: "Arcane / Std", color:"#b5d09f", mice:[{name:"Perpetual Detention", ar:"32.07%"}, {name:"Broomstick Bungler", ar:"38.89%"}, {name:"Misfortune Teller", ar:"29.04%"}]},
        { label: "Arcane / AAC", color:"#8fb86e", mice:[{name:"Arcana Overachiever", ar:"40.94%"}, {name:"Invisible Fashionista", ar:"44.38%"}, {name:"Enchanted Chess Club Champion", ar:"14.68%"}]},
        { label: "Arcane / MMC", color:"#6aa13e", mice:[{name:"Illustrious Illusionist", ar:"39%"}, {name:"Featherlight", ar:"45.66%"}, {name:"Constructively Critical Artist", ar:"15.35%"}]},
        { label: "Arcane / Boss", color:"#45890e", mice:[{name:"Arcane Master Sorcerer", ar:"100%"}] },

        { label: "Shadow / Std", color:"#f2b2eb", mice:[{name:"Mixing Mishap", ar:"29.27%"}, {name:"Uncoordinated Cauldron Carrier", ar:"38.78%"}, {name:"Bookworm", ar:"29.27%"}]},
        { label: "Shadow / AAC", color:"#eb8be2", mice:[{name:"Classroom Keener", ar:"40.87%"}, {name:"Audacious Alchemist", ar:"44.2%"}, {name:"Prestigious Prestidigitator", ar:"14.93%"}]},
        { label: "Shadow / MMC", color:"#e565d8", mice:[{name:"Classroom Disrupter", ar:"39.77%"}, {name:"Teleporting Truant", ar:"44.98%"}, {name:"Magical Multitasker", ar:"15.25%"}]},
        { label: "Shadow / Boss", color:"#de3ece", mice:[{name:"Shadow Master Sorcerer", ar:"100%"}] },
    ]);

    // --- Draconic Depths ---
    registerMap("Draconic Depths", ["Draconic Depths", "Depths"], [
        { label: "Red / Outside", color:"#ff6666", mice:[{name:"Squire Sizzleton", ar:"16.48%"}, {name:"Torchbearer Tinderhelm", ar:"44.87%"}, {name:"Colonel Crisp", ar:"38.64%"}] },
        { label: "Red / 0", color:"#ff9999", mice:[{name:"Flamina Cinderbreath", ar:"60.19%"}, {name:"Crematio Scorchworth", ar:"25.91%"}] },
        { label: "Red / 100", color:"#ff7a7a", mice:[{name:"Incendarius the Unquenchable", ar:"29.72%"}, {name:"Combustius Furnaceheart", ar:"28.19%"}] },
        { label: "Red / 750", color:"#cc3333", mice:[{name:"Sulfurious the Raging Inferno", ar:"40.16%"}] },

        { label: "Green / Outside", color:"#66cc66", mice:[{name:"Goopus Dredgemore", ar:"16.67%"}, {name:"Noxio Sludgewell", ar:"44.84%"}, {name:"Dreck Grimehaven", ar:"38.49%"}] },
        { label: "Green / 0", color:"#99dd99", mice:[{name:"Malignus Vilestrom", ar:"25.49%"}, {name:"Venomona Festerbloom", ar:"59.77%"}] },
        { label: "Green / 100", color:"#66bb66", mice:[{name:"Belchazar Banewright", ar:"33.75%"}, {name:"Pestilentia the Putrid", ar:"29.94%"}] },
        { label: "Green / 750", color:"#339933", mice:[{name:"Corrupticus the Blight Baron", ar:"39.27%"}] },

        { label: "Blue / Outside", color:"#66ccff", mice:[{name:"Frostnip Icebound", ar:"14.18%"}, {name:"Blizzara Winterosa", ar:"45.66%"}, {name:"Iciclesius the Defender", ar:"41.8%"}] },
        { label: "Blue / 0", color:"#99ddff", mice:[{name:"Rimeus Polarblast", ar:"24.7%"}, {name:"Frigidocius Coldshot", ar:"60.79%"}] },
        { label: "Blue / 100", color:"#66bbff", mice:[{name:"Avalancheus the Glacial", ar:"29.59%"}, {name:"Chillandria Permafrost", ar:"33.11%"}] },
        { label: "Blue / 750", color:"#3399ff", mice:[{name:"Arcticus the Biting Frost", ar:"39.12%"}] },

        { label: "Elemental / Outside", color:"#cc66ff", mice:[{name:"Tranquilia Protecticus", ar:"100%"}] },
        { label: "Elemental / 0", color:"#b27dff", mice:[{name:"Absolutia Harmonius", ar:"64.2%"}, {name:"Magnatius Majestica", ar:"56.52%"}] },
        { label: "Elemental / 100", color:"#a066ff", mice:[{name:"Supremia Magnificus", ar:"29.51%"}, {name:"Three'amat the Mother of Dragons", ar:"30.77%"}] },
        { label: "Elemental / 750", color:"#8a33ff", mice:[{name:"Mythical Dragon Emperor", ar:"39.72%"}] }


    ]);

    // --- Origin of Dragons ---
    registerMap("Origin of Dragons", ["Origin of Dragons","OoD"], [
        { label:"Red / Outside", color:"#f28e2b", mice:[{name:"Squire Sizzleton",ar:"16.48%"},{name:"Torchbearer Tinderhelm",ar:"44.87%"},{name:"Colonel Crisp",ar:"38.64%"}]},
        { label:"Red / 0", color:"#f6a35c", mice:[{name:"Crematio Scorchworth",ar:"25.91%"}]},
        { label:"Red / 100", color:"#f6bd7f", mice:[{name:"Flamina Cinderbreath",ar:"60.19%"}]},
        { label:"Red / 750", color:"#e15759", mice:[{name:"Sulfurious the Raging Inferno",ar:"40.16%"},{name:"Incendarius the Unquenchable",ar:"29.72%"},{name:"Combustius Furnaceheart",ar:"28.19%"}]},

        { label:"Green / Outside", color:"#59a14f", mice:[{name:"Goopus Dredgemore",ar:"16.67%"},{name:"Noxio Sludgewell",ar:"44.84%"},{name:"Dreck Grimehaven",ar:"38.49%"}]},
        { label:"Green / 0", color:"#7dbf73", mice:[{name:"Malignus Vilestrom",ar:"25.49%"}]},
        { label:"Green / 100", color:"#9ad491", mice:[{name:"Venomona Festerbloom",ar:"59.77%"}]},
        { label:"Green / 750", color:"#39b54a", mice:[{name:"Corrupticus the Blight Baron",ar:"39.27%"},{name:"Belchazar Banewright",ar:"33.75%"},{name:"Pestilentia the Putrid",ar:"29.94%"}]},

        { label:"Blue / Outside", color:"#66ccff", mice:[{name:"Frostnip Icebound",ar:"14.18%"},{name:"Blizzara Winterosa",ar:"45.66%"},{name:"Iciclesius the Defender",ar:"41.8%"}]},
        { label:"Blue / 0", color:"#99ddff", mice:[{name:"Rimeus Polarblast",ar:"24.7%"}]},
        { label:"Blue / 100", color:"#66bbff", mice:[{name:"Frigidocius Coldshot",ar:"60.79%"}]},
        { label:"Blue / 750", color:"#3399ff", mice:[{name:"Avalancheus the Glacial",ar:"29.59%"},{name:"Chillandria Permafrost",ar:"33.11%"},{name:"Arcticus the Biting Frost",ar:"39.12%"}]},

        { label:"Elemental / Outside", color:"#cc66ff", mice:[{name:"Tranquilia Protecticus",ar:"100%"}]},
        { label:"Elemental / 0", color:"#b27dff", mice:[{name:"Absolutia Harmonius",ar:"64.2%"}]},
        { label:"Elemental / 100", color:"#a066ff", mice:[{name:"Magnatius Majestica",ar:"56.52%"}]},
        { label:"Elemental / 750", color:"#8a33ff", mice:[{name:"Mythical Dragon Emperor",ar:"39.72%"},{name:"Supremia Magnificus",ar:"29.51%"},{name:"Three'amat the Mother of Dragons",ar:"30.77%"}]},

        { label:"MoPi / Low", color:"#e1B2F7", mice:[{name:"Violet Stormchild",ar:"43.17%"},{name:"Thunder Strike",ar:"61.37%"}]},
        { label:"MoPi / Med", color:"#c296e5", mice:[{name:"⚡Thunderlord⚡",ar:"57.11%"}]},
        { label:"MoPi / High", color:"#b37cdf", mice:[{name:"Dragoon",ar:"5%"},{name:"Thundering Watcher",ar:"61.98%"}]},
        { label:"MoPi / Max", color:"#9a85c4", mice:[{name:"Ful'Mina the Mountain Queen",ar:"98.87%"}]},

        { label:"Eruption / Mild", color:"#FFFF56", mice:[{name:"Sizzle Pup",ar:"47.35%"},{name:"Mild Spicekin",ar:"52.65%"}]},
        { label:"Eruption / Trio", color:"#FFDF56", mice:[{name:"Bearded Elder",ar:"49.91%"},{name:"Smoldersnap",ar:"35.57%"},{name:"Ignatia",ar:"36.46%"}]},
        { label:"Eruption / Brut", color:"#FFBA56", mice:[{name:"Cinderstorm",ar:"68.92%"},{name:"Bruticus the Blazing",ar:"25.72%"}]},
        { label:"Eruption / KSS", color:"#FF8856", mice:[{name:"Kalor'ignis of the Geyser",ar:"100%"},{name:"Stormsurge the Vile Tempest",ar:"73.84%"}]},

        { label:"Cork / Bland", color:"#a8ff00", mice:[{name:"Fuzzy Drake",ar:"93.92%"}]},
        { label:"Cork / Mild", color:"#E9EA53", mice:[{name:"Cork Defender",ar:"74.88%"}]},
        { label:"Cork / Medium", color:"#EFC922", mice:[{name:"Burly Bruiser",ar:"72.93%"}]},
        { label:"Cork / Hot", color:"#EE970C", mice:[{name:"Horned Cork Hoarder",ar:"75.73%"}]},
        { label:"Cork / Flammin", color:"#FF842E", mice:[{name:"Rambunctious Rain Rumbler",ar:"76.04%"},{name:"Corky the Collector",ar:"6.90%"}]},
        { label:"Cork / Wildfire", color:"#E41122", mice:[{name:"Corkataur",ar:"100%"}]},

        { label:"Pressure / Mild", color:"#E9EA53", mice:[{name:"Steam Sailor",ar:"100%"}]},
        { label:"Pressure / Medium", color:"#EFC922", mice:[{name:"Warming Wyvern",ar:"84.56%"}]},
        { label:"Pressure / Hot", color:"#EE970C", mice:[{name:"Vaporior",ar:"83.77%"}]},
        { label:"Pressure / Flammin", color:"#FF842E", mice:[{name:"Pyrehyde",ar:"88.16%"}]},
        { label:"Pressure / Wildfire", color:"#E41122", mice:[{name:"Emberstone Scaled",ar:"100%"}]},

        { label:"PnF", color:"#aa5755", mice:[{name:"Tiny Dragonfly",ar:"51.76%"},{name:"Lancer Guard",ar:"30.13%"},{name:"Dragonbreather",ar:"16.39%"},{name:"Regal Spearman",ar:"34.37%"},{name:"Paragon of Dragons",ar:"100%"}]},
        { label:"Sky Palace", color:"#b17a4e", mice:[{name:"Empyrean Javelineer",ar:"20.30%"}]},
    ]);


    // --- Queso Canyon Grand Tour ---
    registerMap("Queso Canyon Grand Tour", ["QCGT", "Queso Canyon", "Queso"], [
        // Queso River (QR)
        { label: "River / Gouda", color:"#7ec8ff", mice:[{name:"Pump Raider",ar:"31%"},{name:"Tiny Saboteur",ar:"27%"},{name:"Queso Extractor",ar:"26%"},{name:"Croquet Crusher",ar:"4%"}] },
        { label: "River / SB", color:"#66b2ff", mice:[{name:"Tiny Saboteur",ar:"30%"},{name:"Pump Raider",ar:"29%"},{name:"Sleepy Merchant",ar:"24%"},{name:"Queso Extractor",ar:"9%"},{name:"Croquet Crusher",ar:"5%"}] },
        { label: "River / Wildfire", color:"#3aa0ff", mice:[{name:"Queen Quesada",ar:"99%"}] },

        // Prickly Plains (PP)
        { label: "Plains / Bland", color:"#ffff80", mice:[{name:"Spice Seer",ar:"56%"},{name:"Old Spice Collector",ar:"36%"}] },
        { label: "Plains / Mild", color:"#e9ea53", mice:[{name:"Spice Farmer",ar:"49%"},{name:"Granny Spice",ar:"47%"}] },
        { label: "Plains / Medium", color:"#efc922", mice:[{name:"Spice Finder",ar:"55%"},{name:"Spice Sovereign",ar:"43%"}] },
        { label: "Plains / Hot", color:"#ee970c", mice:[{name:"Spice Reaper",ar:"58%"},{name:"Spice Raider",ar:"40%"}] },
        { label: "Plains / Flamin", color:"#ff842e", mice:[{name:"Inferna, the Engulfed",ar:"100%"}] },

        // Cantera Quarry (CQ)
        { label: "Quarry / Bland", color:"#b6ff7f", mice:[{name:"Chip Chiseler",ar:"56%"},{name:"Tiny Toppler",ar:"37%"}] },
        { label: "Quarry / Mild", color:"#9cff5c", mice:[{name:"Ore Chipper",ar:"48%"},{name:"Rubble Rummager",ar:"48%"}] },
        { label: "Quarry / Medium", color:"#5ae031", mice:[{name:"Nachore Golem",ar:"60%"},{name:"Rubble Rouser",ar:"38%"}] },
        { label: "Quarry / Hot", color:"#37b224", mice:[{name:"Grampa Golem",ar:"50%"},{name:"Fiery Crusher",ar:"49%"}] },
        { label: "Quarry / Flamin", color:"#2fb514", mice:[{name:"Nachous, the Molten",ar:"100%"}] },

        // Cork Caverns
        { label: "Cork / Bland", color:"#a8ff00", mice:[{name:"Fuzzy Drake", ar:"93.92%"}] },
        { label: "Cork / Mild", color:"#E9EA53", mice:[{name:"Cork Defender", ar:"74.88%"}] },
        { label: "Cork / Medium", color:"#EFC922", mice:[{name:"Burly Bruiser", ar:"72.93%"}] },
        { label: "Cork / Hot", color:"#EE970C", mice:[{name:"Horned Cork Hoarder", ar:"75.73%"}] },
        { label: "Cork / Flammin", color:"#FF842E", mice:[{name:"Rambunctious Rain Rumbler", ar:"76.04%"}, {name:"Corky, the Collector", ar:"6.90%"}] },
        { label: "Cork / Wildfire", color:"#E41122", mice:[{name:"Corkataur", ar:"100%"}] },

        // Pressure Peaks
        { label: "Pressure / Mild", color:"#E9EA53", mice:[{name:"Steam Sailor", ar:"100%"}] },
        { label: "Pressure / Medium", color:"#EFC922", mice:[{name:"Warming Wyvern", ar:"84.56%"}] },
        { label: "Pressure / Hot", color:"#EE970C", mice:[{name:"Vaporior", ar:"83.77%"}] },
        { label: "Pressure / Flammin", color:"#FF842E", mice:[{name:"Pyrehyde", ar:"88.16%"}] },
        { label: "Pressure / Wildfire", color:"#E41122", mice:[{name:"Emberstone Scaled", ar:"100%"}] },

        // Small (Sizzle Mild)
        { label: "Small(Sizzle Mild) / Mild", color:"#ffff56", mice:[{name:"Sizzle Pup",ar:"54%"},{name:"Mild Spicekin",ar:"48%"}] },
        { label: "Small(Sizzle Mild) / Medium", color:"#ffdf56", mice:[{name:"Sizzle Pup",ar:"65%"},{name:"Ignatia",ar:"25%"},{name:"Smoldersnap",ar:"10%"},{name:"Mild Spicekin",ar:"5%"}] },

        // Medium (BE Trio)
        { label: "Medium(BE Trio) / Medium", color:"#ffdf56", mice:[{name:"Bearded Elder",ar:"49%"},{name:"Smoldersnap",ar:"35%"},{name:"Mild Spicekin",ar:"14%"}] },
        { label: "Medium(BE Trio) / Hot", color:"#ffdf56", mice:[{name:"Bearded Elder",ar:"49%"},{name:"Ignatia",ar:"36%"},{name:"Smoldersnap",ar:"9%"},{name:"Mild Spicekin",ar:"5%"}] },

        // Large (Cinderbrut)
        { label: "Large(Cinderbrut) / Hot", color:"#ffba56", mice:[{name:"Cinderstorm",ar:"49%"},{name:"Ignatia",ar:"35%"},{name:"Smoldersnap",ar:"10%"},{name:"Mild Spicekin",ar:"5%"}] },
        { label: "Large(Cinderbrut) / Flamin", color:"#ffba56", mice:[{name:"Cinderstorm",ar:"70%"},{name:"Bruticus, the Blazing",ar:"25%"}] },

        // Epic (KSS)
        { label: "Epic(KSS) / Flammin (KSS)", color:"#ff7f4f", mice:[{name:"Stormsurge, the Vile Tempest",ar:"75%"},{name:"Bruticus, the Blazing",ar:"22%"}] },
        { label: "Epic / Wildfire (KSS)", color:"#ff5a36", mice:[{name:"Kalor'ignis of the Geyser",ar:"99%"}] },

    ]);

    // --- Valour Rift ---
    registerMap("Valour Rift", ["Valour Rift", "VR"], [
        { label: "Outside", color:"#B6D7A8", mice:["Timid Explorer", "Elixir Maker"] },
        { label: "Floor 1", color:"#93C47D", mice:["Puppetto", "Puppet Champion"] },
        { label: "Floor 2", color:"#6AA84F", mice:["Cutpurse", "Champion Thief"] },
        { label: "Floor 3", color:"#FCE5CD", mice:["Martial", "Praetorian Champion"] },
        { label: "Floor 4", color:"#F6B26B", mice:["One-Mouse Band", "Champion Danseuse"] },
        { label: "Floor 5", color:"#F9CB9C", mice:["Mouse of Elements", "Magic Champion"] },
        { label: "Floor 6", color:"#F4CCCC", mice:["Cursed Crusader", "Fallen Champion Footman"] },
        { label: "Floor 7", color:"#E06666", mice:["Withered Remains", "Arch Champion Necromancer"] },
        { label: "Non-UU", color:"#EA9999", mice:["Shade of the Eclipse", "Puppet Champion"] },
        { label: "UU", color:"#C9DAF8", mice:["Bulwark of Ascent", "The Total Eclipse"] },
        { label: "Other", color:"#3C78D8", mice:["Terrified Adventurer", "Unwavering Adventurer", "Berzerker", "Lumi-lancer", "Possessed Armaments", "Prestigious Adventurer", "Soldier of the Shade"] }
    ]);

    // --- Lantern Lighter ---
    registerMap("Lantern Lighter", ["Lantern Lighter", "LNY"], [
        { label: "Any", color:"#B6D7A8", mice:[{name:"Costumed Dog", ar:"22.74%"}, {name:"Costumed Dragon", ar:"22.74%"}, {name:"Costumed Horse", ar:"22.74%"}, {name:"Costumed Monkey", ar:"22.74%"}, {name:"Costumed Ox", ar:"22.74%"}, {name:"Costumed Pig", ar:"22.74%"}, {name:"Costumed Rabbit", ar:"22.74%"}, {name:"Costumed Rat", ar:"22.74%"}, {name:"Costumed Rooster", ar:"22.74%"}, {name:"Costumed Sheep", ar:"22.74%"}, {name:"Costumed Snake", ar:"22.74%"}, {name:"Costumed Tiger", ar:"22.74%"}] },
        { label: "Nian Gao'da", color:"#EA9999", mice:[{name:"Lunar Red Candle Maker", ar:"2.6% | 4.48%"}] },
        { label: "Dumpling", color:"#6FA8DC", mice:[{name:"Calligraphy", ar:"4.35%"}, {name:"Red Envelope", ar:"5.86%"}, {name:"Dumpling Chef", ar:"5.86%"}] },
    ]);

    // --- Halloween ---
    registerMap("Halloween", ["Halloween", "Trick", "Treat"], [
        { label: "Standard", color:"#fff935", mice:[{name:"Candy Cat", ar:"9.02%"}, {name:"Candy Goblin", ar:"9.23%"}, {name:"Cobweb", ar:"9.74%"}, {name:"Grey Recluse", ar:"9.88%"}, {name:"Shortcut", ar:"20.30%"}, {name:"Sugar Rush", ar:"7.60%"}, {name:"Teenage Vampire", ar:"8.79%"}, {name:"Tricky Witch", ar:"15.40%"}, {name:"Zombot Unipire", ar:"10.04%"}] },
        { label: "Jack O", color:"#f9a645", mice:[{name:"Gourdborg", ar:"7.90%"}, {name:"Maize Harvester", ar:"19.94%"}, {name:"Pumpkin Hoarder", ar:"25.50%"}, {name:"Spirit Light", ar:"11.97%"}, {name:"Treat", ar:"14.84%"}, {name:"Trick", ar:"14.85%"}, {name:"Wild Chainsaw", ar:"5.00%"}] },
        { label: "Bone", color:"#bfbfbf", mice:[{name:"Creepy Marionette", ar:"9.91%"}, {name:"Dire Lycan", ar:"14.96%"}, {name:"Grave Robber", ar:"4.99%"}, {name:"Hollowhead", ar:"19.68%"}, {name:"Mousataur Priestess", ar:"9.94%"}, {name:"Sandmouse", ar:"20.62%"}, {name:"Titanic Brain-Taker", ar:"14.78%"}, {name:"Tomb Exhumer", ar:"5.12%"}] },
        { label: "Polter", color:"#5d9fce", mice:[{name:"Admiral Arrrgh", ar:"4.92%"}, {name:"Captain Cannonball", ar:"22.63%"}, {name:"Ghost Pirate Queen", ar:"4.97%"}, {name:"Gourd Ghoul", ar:"10.06%"}, {name:"Scorned Pirate", ar:"21.66%"}, {name:"Spectral Butler", ar:"13.98%"}, {name:"Spectral Swashbuckler", ar:"21.78%"}] },
        { label: "Scream", color:"#5ae031", mice:[{name:"Baba Gaga", ar:"15.15%"}, {name:"Bonbon Gummy Globlin", ar:"20.65%"}, {name:"Hollowed", ar:"19.65%"}, {name:"Hollowed Minion", ar:"24.90%"}, {name:"Swamp Thang", ar:"19.65%"}] },
    ]);

    // --- Great Winter Hunt ---
    registerMap("Great Winter Hunt 2022", ["Winter Hunt", "GWH"], [
        { label: "Any / Standard", color:"#B6D7A8", mice:[{name:"Hoarder", ar:"22.74%"}] },
        { label: "Any / PP", color:"#93C47D", mice:[{name:"Snowflake", ar:"2.6% | 4.48%"}, {name:"Stuck Snowball", ar:"3.86% | 4.48%"}] },
        { label: "Any / GPP", color:"#6AA84F", mice:[{name:"Glazy", ar:"4.35%"}, {name:"Joy", ar:"5.86%"}] },

        { label: "Hill / Standard", color:"#FCE5CD", mice:[{name:"Candy Cane", ar:"24.18%"}, {name:"Nice Knitting", ar:"12.94% | 8.06%"}, {name:"Shorts-All-Year", ar:"10.94%"}, {name:"Snow Scavenger", ar:"6.72%"}, {name:"Toboggan Technician", ar:"11.13%"}, {name:"Young Prodigy Racer", ar:"19.72% | 15.55%"}] },
        { label: "Hill / PP", color:"#F6B26B", mice:[{name:"Triple Lutz", ar:"5.04%"}] },
        { label: "Hill / PP+GPP", color:"#F9CB9C", mice:[{name:"Black Diamond Racer", ar:"19.22% | 8.73%"}, {name:"Double Black Diamond Racer", ar:"4.02% | 3.63%"}, {name:"Free Skiing", ar:"4.09% | 3.76%"}, {name:"Great Giftnapper", ar:"3.06% | 2.61%"}, {name:"Nitro Racer", ar:"4.24% | 3.89%"}, {name:"Ol' King Coal", ar:"2.11% | 2.23%"}, {name:"Rainbow Racer", ar:"2.36% | 2.17%"}, {name:"Snow Boulder", ar:"7.83% | 11.91%"}, {name:"Snow Golem Jockey", ar:"9.8% | 8.22%"}, {name:"Snowball Hoarder", ar:"7.99% | 13.69%"}, {name:"Sporty Ski Instructor", ar:"12.75% | 10%"}, {name:"Wreath Thief", ar:"10.3% | 8.41%"}, {name:"Frightened Flying Fireworks", ar:"???% | ???%"}] },

        { label: "Workshop / Standard", color:"#F4CCCC", mice:[{name:"Gingerbread", ar:"18.77%"}, {name:"Greedy Al", ar:"11.08%"}, {name:"Mouse of Winter Future", ar:"14.5% | 9.23%"}, {name:"Mouse of Winter Past", ar:"11.08%"}, {name:"Mouse of Winter Present", ar:"22.75% | 16.31%"}] },
        { label: "Workshop / SB", color:"#eeeeee", mice:[{name:"Scrooge", ar:"9.23%"}] },
        { label: "Workshop / PP", color:"#E06666", mice:[{name:"Ribbon", ar:"5.97%"}] },
        { label: "Workshop / PP+GPP", color:"#EA9999", mice:[{name:"Christmas Tree", ar:"9.7% | 7.99%"}, {name:"Destructoy", ar:"7.61% | 10.69%"}, {name:"Elf", ar:"17.44% | 8.7%"}, {name:"Mad Elf", ar:"1.51% | 1.76%"}, {name:"Nutcracker", ar:"4.33% | 3.88%"}, {name:"Ornament", ar:"3.8% | 3.41%"}, {name:"Present", ar:"10.03% | 7.52%"}, {name:"Ridiculous Sweater", ar:"8.52% | 9.75%"}, {name:"Snow Golem Architect", ar:"2.82% | 4.11%"}, {name:"Stocking", ar:"3.21% | 3.53%"}, {name:"Toy", ar:"10.62% | 11.52%"}, {name:"Toy Tinkerer", ar:"7.41% | 5.76%"}, {name:"Party Head", ar:"???% | ???%"}] },

        { label: "Fortress / Standard", color:"#C9DAF8", mice:[{name:"Confused Courier", ar:"13.75%"}, {name:"Frigid Foreman", ar:"11.67% | 8.13%"}, {name:"Miser", ar:"15.63%"}, {name:"Missile Toe", ar:"15.63%"}, {name:"Snowblower", ar:"12.78% | 7.5%"}, {name:"Snowglobe", ar:"21.67% | 9.38%"}] },
        { label: "Fortress / PP", color:"#3C78D8", mice:[{name:"Builder", ar:"4.29%"}] },
        { label: "Fortress / PP+GPP", color:"#6FA8DC", mice:[{name:"Borean Commander", ar:"4.91% | 6.37%"}, {name:"Glacia Ice Fist", ar:"11.66% | 4.46%"}, {name:"Great Winter Hunt Impostor", ar:"6.75% | 11.78%"}, {name:"Iceberg Sculptor", ar:"3.07% | 2.55%"}, {name:"Naughty Nougat", ar:"4.29% | 6.05%"}, {name:"Reinbo", ar:"19.02% | 4.14%"}, {name:"S.N.O.W. Golem", ar:"6.75% | 4.78%"}, {name:"Slay Ride", ar:"9.2% | 11.46%"}, {name:"Snow Fort", ar:"11.04% | 8.6%"}, {name:"Snow Sorceress", ar:"3.68% | 6.37%"}, {name:"Squeaker Claws", ar:"4.91% | 3.18%"}, {name:"Tundra Huntress", ar:"4.91% | 2.55%"}, {name:"New Year's", ar:"???% | ???%"}] },
        { label: "Fortress / Boss", color:"#7095E4", mice:[{name:"Frost King", ar:"special"}] },
    ]);


    /* ================== Board engine ================== */
    const qs = (s, r=document)=>r.querySelector(s);
    const sleep = (ms)=>new Promise(r=>setTimeout(r, ms));
    const textColor = (hex) => {
        const mm = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex||"");
        if (!mm) return "#111";
        const r=parseInt(mm[1],16), g=parseInt(mm[2],16), b=parseInt(mm[3],16);
        const L=(0.2126*r+0.7152*g+0.0722*b)/255;
        return L>0.6 ? "#111827" : "#f9fafb";
    };

    function getIds() {
        const u = window.user || window.hg?.user || {};
        const mapId = u?.quests?.QuestRelicHunter?.maps?.[0]?.map_id ?? u?.quests?.TreasureMap?.map_id ?? null;
        const uh = u?.unique_hash || null;
        return { mapId, uh };
    }
    async function fetchMap(mapId, uh) {
        const body = new URLSearchParams({
            sn:"Hitgrab", hg_is_ajax:"1", action:"map_info", map_id:String(mapId), uh:String(uh)
        }).toString();
        const res = await fetch(CFG.ENDPOINT, {
            method:"POST",
            credentials:"include",
            headers:{ "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8", "X-Requested-With":"XMLHttpRequest" },
            body
        });
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.json();
    }

    function pickProfile(tm) {
        const mapName = tm?.name || tm?.type || "";
        const goals = (tm?.goals?.mouse ?? tm?.goals ?? tm?.goal_list ?? []).map(g => ({ name: g.name }));
        let best = null, bestScore = 0;
        for (const p of REGISTRY) {
            const sc = Math.max(0, Number(p.meta.matcher({ mapName, goals })) || 0);
            if (sc > bestScore) { bestScore = sc; best = p; }
        }
        return best || null;
    }

    function buildBoardDataKnown(tm, profile) {
        const NAME_INFO = new Map();
        for (const [loc, arr, , tier, , color] of profile.groups) {
            for (const [name, ar] of arr) NAME_INFO.set(name, { loc, tier, color, ar: ar || "" });
        }

        const goals = tm?.goals?.mouse ?? tm?.goals ?? tm?.goal_list ?? [];
        const done = new Set((tm?.hunters ?? []).flatMap(h => h?.completed_goal_ids?.mouse ?? []));
        const rows = goals.map(g => {
            const info = NAME_INFO.get(g.name) || { loc:"Other", tier:"Other", color:"#ddd", ar:"" };
            return { name: g.name, loc: info.loc, tier: info.tier, color: info.color, ar: info.ar, complete: done.has(g.unique_id) };
        });

        const locMap = new Map();
        for (const r of rows) {
            if (!locMap.has(r.loc)) locMap.set(r.loc, new Map());
            const sec = locMap.get(r.loc);
            if (!sec.has(r.tier)) sec.set(r.tier, []);
            sec.get(r.tier).push(r);
        }

        const result = [];
        for (const loc of profile.locOrder) if (locMap.has(loc)) {
            const secMap = locMap.get(loc);
            const sections = [];
            for (const t of profile.tierOrder) if (secMap.has(t)) sections.push([t, secMap.get(t)]);
            const headColor = sections.find(s => s[1].length)?.[1]?.[0]?.color || "#e5e7eb";
            result.push({ loc, headColor, sections, humanTier: profile.humanTier || ((x)=>x) });
        }
        return result;
    }

    function buildBoardDataGeneric(tm) {
        const goals = tm?.goals?.mouse ?? tm?.goals ?? tm?.goal_list ?? [];
        const done = new Set((tm?.hunters ?? []).flatMap(h => h?.completed_goal_ids?.mouse ?? []));
        const buckets = new Map();
        for (const g of goals) {
            const key = (g.name?.[0] || "#").toUpperCase();
            if (!buckets.has(key)) buckets.set(key, []);
            buckets.get(key).push({ name: g.name, loc:"Targets", tier:key, color:"#e5e7eb", ar:"", complete: done.has(g.unique_id) });
        }
        const letters = Array.from(buckets.keys()).sort();
        const sections = letters.map(L => [L, buckets.get(L)]);
        return [{ loc: (tm?.name || "Map"), headColor:"#e5e7eb", sections, humanTier:(t)=>t }];
    }

    function ensureStyles(widthPx) {
        let st = qs(`#${CFG.STYLE_ID}`);
        if (!st) { st = document.createElement("style"); st.id = CFG.STYLE_ID; document.head.appendChild(st); }
        st.textContent = `
      :root { --mh-board-width: ${widthPx}px; --mh-board-left: ${CFG.OFFSET_LEFT}px; --mh-board-top: ${CFG.OFFSET_TOP}px; }

      #${CFG.ANCHOR_ID} { position: relative !important; height: 0 !important; pointer-events: none; }
      #${CFG.PANEL_ID}{
        position: absolute !important;
        left: var(--mh-board-left); top: var(--mh-board-top);
        width: var(--mh-board-width);
        box-sizing: border-box;
        pointer-events: auto; z-index: 50;
      }

      .mb-tools{ display:flex; gap:6px; margin:6px 0 10px; }
      .mb-btn{ font-size:12px; padding:4px 8px; border:1px solid #ddd; background:#fafafa; border-radius:8px; cursor:pointer; }
      .mb-btn:hover{ background:#f3f4f6; }

      .mb-card{ width:100%; border:1px solid #e5e7eb; border-radius:10px; background:#fff; margin:8px 0; box-shadow:0 1px 2px rgba(0,0,0,.05); }
      .mb-head{ padding:6px 10px; font-weight:700; font-size:13px; border-bottom:1px solid #eee; }
      .mb-body{ padding:6px; }

      .mb-section{ border:1px solid #eee; border-radius:8px; margin:6px 0; overflow:hidden; }
      .mb-title{ font-size:12px; font-weight:600; padding:4px 8px; background:#f8fafc; border-bottom:1px solid #eee; color:#334155; }

      .mb-item{ display:flex; justify-content:space-between; align-items:center; padding:4px 8px; border-top:1px solid #f3f4f6; font-size:13px; background:#fff; }
      .mb-item:first-child{ border-top:none; }
      .mb-item.complete{ display:none !important; } /* always hide completed */
      .mb-name{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width: calc(var(--mh-board-width) - 120px); }

      .mb-right{ display:flex; align-items:center; gap:6px; }
      .mb-badge{ font-size:11px; padding:2px 6px; border:1px solid #ddd; border-radius:999px; background:#fff; }

      .mb-section.hide{ display:none !important; }
      .mb-card.hide{ display:none !important; }
    `;
    }

    function ensureAnchor(mount) {
        let anchor = qs(`#${CFG.ANCHOR_ID}`, mount);
        if (!anchor) { anchor = document.createElement("div"); anchor.id = CFG.ANCHOR_ID; mount.prepend(anchor); }
        return anchor;
    }

    function autoHide(panel) {
        panel.querySelectorAll('.mb-card').forEach(card => {
            let anySectionVisible = false;
            card.querySelectorAll('.mb-section').forEach(sec => {
                const hasVisibleRow = !!sec.querySelector('.mb-item:not(.complete)');
                sec.classList.toggle('hide', !hasVisibleRow);
                if (hasVisibleRow) anySectionVisible = true;
            });
            card.classList.toggle('hide', !anySectionVisible);
        });
    }

    function calcBoardWidth(columns) {
        const names = [];
        columns.forEach(c => c.sections.forEach(([,items]) => items.forEach(it => names.push(it.name))));
        const maxLen = Math.max(1, ...names.map(n => n.length));
        const estimate = Math.round(8 * maxLen + 120);
        return Math.min(CFG.WIDTH_MAX, Math.max(CFG.WIDTH_MIN, estimate));
    }

    function renderBoard(columns, rightColEl, widthPx) {
        ensureStyles(widthPx);
        const anchor = ensureAnchor(rightColEl);
        let panel = qs(`#${CFG.PANEL_ID}`, anchor);
        if (!panel) { panel = document.createElement("div"); panel.id = CFG.PANEL_ID; anchor.appendChild(panel); }

        const tools = `<div class="mb-tools"><button class="mb-btn" data-act="refresh">Refresh</button></div>`;
        const htmlCols = columns.map(col => {
            const headStyle = `style="background:${col.headColor};color:${textColor(col.headColor)}"`;
            const sections = col.sections.map(([tier, items]) => {
                if (!items || !items.length) return "";
                const rows = items.map(it => `
                  <div class="mb-item ${it.complete ? "complete":""}" data-complete="${it.complete ? "1":"0"}">
                    <div class="mb-name">${it.name}</div>
                    <div class="mb-right">${it.ar ? `<span class="mb-badge">${it.ar}</span>` : ""}</div>
                  </div>
                `).join("");
                const niceTier = (col.humanTier || ((t)=>t))(tier);
                return `<div class="mb-section"><div class="mb-title">${niceTier}</div>${rows}</div>`;
            }).join("");
            return `<div class="mb-card"><div class="mb-head" ${headStyle}>${col.loc}</div><div class="mb-body">${sections}</div></div>`;
        }).join("");

        panel.innerHTML = tools + htmlCols;

        const btn = panel.querySelector('[data-act="refresh"]');
        btn.onclick = async () => {
            btn.disabled = true;
            try { await bootstrap(true); } finally { btn.disabled = false; }
        };

        autoHide(panel);
    }

    /* ================== Bootstrap & refresh hooks ================== */

    async function bootstrap(force = false) {
        const rightCol = qs(CFG.RIGHT_COL_SEL);
        if (!rightCol) return;
        const { mapId, uh } = getIds();
        if (!mapId || !uh) return;

        const data = await fetchMap(mapId, uh);
        const tm = data?.treasure_map;

        const profile = pickProfile(tm);
        const columns = profile ? buildBoardDataKnown(tm, profile) : buildBoardDataGeneric(tm);
        const widthPx = calcBoardWidth(columns);
        renderBoard(columns, rightCol, widthPx);

        if (force) console.log(`[Map Board] refreshed; width=${widthPx}px; profile=${profile?.meta?.label || "generic"}`);
    }

    (async () => { for (let i=0;i<40;i++){ try{ await bootstrap(); break; } catch{ await sleep(500);} } })();

    // auto-refresh on treasure map xhrs
    const _open = XMLHttpRequest.prototype.open;
    const _send = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.open = function(m,u,...r){ this.__mh_u=u; return _open.call(this,m,u,...r); };
    XMLHttpRequest.prototype.send = function(b){
        this.addEventListener("load", () => {
            if (typeof this.__mh_u === "string" && /treasuremap_v2\.php/i.test(this.__mh_u)) bootstrap(true);
        });
        return _send.call(this,b);
    };

    // manual refresh
    window.MHBoardAbsLeftAlwaysHide = { refresh: () => bootstrap(true) };
})();