NCC AAA - Visual Base

Hub visual temático do NCC AAA com petpage, Arena, Training, Battledome, Trophy Tracker e módulos autoplayer por toggle.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Advertisement:

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

Advertisement:

// ==UserScript==
// @name         NCC AAA - Visual Base
// @namespace    Assistente.Neopets.Brasil.NCCAAA
// @license      GPL-3.0-or-later
// @version      0.1.63
// @description  Hub visual temático do NCC AAA com petpage, Arena, Training, Battledome, Trophy Tracker e módulos autoplayer por toggle.
// @match        *://www.neopets.com/*
// @match        *://neopets.com/*
// @match        *://*.neopets.com/*
// @match        *://www.neopets.com/~Yillcivi*
// @match        *://neopets.com/~Yillcivi*
// @run-at       document-idle
// @noframes
// @grant        unsafeWindow
// @icon         https://images.neopets.com/neoboards/avatars/avinroo.gif
// ==/UserScript==

(function NCC_AAA_Visual_Base() {
    "use strict";

    const VERSION = "0.1.63";
    const APP_ID = "ncc-aaa-page";
    const STYLE_ID = "ncc-aaa-style";
    const MARKER_ID = "ncc-aaa-boot-marker";

    const MOUNT_PATH = "/~Yillcivi";
    const LEGACY_MOUNT_PATH = "/sketches.phtml";
    const MOUNT_PATHS = Object.freeze([MOUNT_PATH, LEGACY_MOUNT_PATH]);
    const ENTRY_POINT = "https://www.neopets.com/~Yillcivi";
    const LEGACY_ENTRY_POINT = "https://www.neopets.com/sketches.phtml";
    const CELLBLOCK_PATH = "/games/cellblock/cellblock_main.phtml";
    const CELLBLOCK_URL = "https://www.neopets.com/games/cellblock/cellblock_main.phtml";
    const KIKO_POP_PATH = "/worlds/kiko/kpop/";
    const KIKO_POP_URL = "https://www.neopets.com/worlds/kiko/kpop/";
    const NEOQUEST_ONE_PATH = "/games/neoquest/neoquest.phtml";
    const NEOQUEST_ONE_URL = "https://www.neopets.com/games/neoquest/neoquest.phtml";
    const NEOQUEST_TWO_PATH = "/games/nq2/nq2.phtml";
    const NEOQUEST_TWO_URL = "https://www.neopets.com/games/nq2/nq2.phtml";
    const TYRANU_EVAVU_PATH = "/games/tyranuevavu.phtml";
    const TYRANU_EVAVU_URL = "https://www.neopets.com/games/tyranuevavu.phtml";
    const TYRANU_EVAVU_ICON_URL = "https://images.neopets.com/trophies/47_1.gif";
    const TYRANU_EVAVU_STORAGE_CARDS_KEY = "ncc.aaa.tyranuEvavu.cards.v1";
    const TYRANU_EVAVU_STORAGE_ENABLED_KEY = "ncc.aaa.tyranuEvavu.enabled.v1"; // legacy persistent key; not used to keep autoplay on across sessions.
    const TYRANU_EVAVU_SESSION_ENABLED_KEY = "ncc.aaa.tyranuEvavu.enabled.session.v1";
    const TYRANU_EVAVU_REPORT_KEY = "ncc.aaa.tyranuEvavu.report.v1";
    const TYRANU_EVAVU_EVENT = "ncc:aaa:tyranu-evavu";
    const TYRANU_EVAVU_SCHEMA = "ncc-aaa-tyranu-evavu-autoplayer-v1";
    const KISS_THE_MORTOG_PLAY_PATH = "/medieval/kissthemortog.phtml";
    const KISS_THE_MORTOG_PROCESS_PATH = "/medieval/process_kissthemortog.phtml";
    const KISS_THE_MORTOG_URL = "https://www.neopets.com/medieval/kissthemortog.phtml";
    const KISS_THE_MORTOG_PROCESS_URL = "https://www.neopets.com/medieval/process_kissthemortog.phtml";
    const KISS_THE_MORTOG_ICON_URL = "https://images.neopets.com/items/pet_mortog.gif";
    const KISS_THE_MORTOG_TARGET_KEY = "ncc.aaa.kissTheMortog.target.v1";
    const KISS_THE_MORTOG_SESSION_PLAYING_KEY = "ncc.aaa.kissTheMortog.playing.session.v1";
    const KISS_THE_MORTOG_REPORT_KEY = "ncc.aaa.kissTheMortog.report.v1";
    const KISS_THE_MORTOG_EVENT = "ncc:aaa:kiss-the-mortog";
    const KISS_THE_MORTOG_SCHEMA = "ncc-aaa-kiss-the-mortog-autoplayer-v1";
    const KISS_THE_MORTOG_STATE = Object.freeze({
        MORTOG_SELECT: "MORTOG_SELECT",
        COLLECT_OR_CONTINUE: "COLLECT_OR_CONTINUE",
        TRY_AGAIN: "TRY_AGAIN",
        ERROR_PAGE: "ERROR_PAGE",
        UNKNOWN: "UNKNOWN"
    });
    const KISS_THE_MORTOG_MAX_SAFE_SCORE = 5900;
    const KISS_THE_MORTOG_TARGET_OPTIONS = Object.freeze([
        Object.freeze({ value: 100, label: "100 NP" }),
        Object.freeze({ value: 300, label: "300 NP" }),
        Object.freeze({ value: 1150, label: "1,150 NP" }),
        Object.freeze({ value: 5900, label: "5,900 NP - Avatar / Máximo" })
    ]);
    const KISS_THE_MORTOG_ACTION_DELAY_MIN = 1400;
    const KISS_THE_MORTOG_ACTION_DELAY_MAX = 2400;
    const KISS_THE_MORTOG_CLICK_DELAY_MIN = 150;
    const KISS_THE_MORTOG_CLICK_DELAY_MAX = 450;
    let kissTheMortogTimeout = null;
    let kissTheMortogAlreadyActing = false;

    const GODORI_PATH_PREFIX = "/games/godori/";
    const GODORI_URL = "https://www.neopets.com/games/godori/";
    const GODORI_ICON_URL = "https://images.neopets.com/games/godori/cards/deck.gif";
    const GODORI_REPORT_KEY = "ncc.aaa.godori.report.v1";
    const GODORI_EVENT = "ncc:aaa:godori";
    const GODORI_SCHEMA = "ncc-aaa-godori-autoplayer-v1";
    const GODORI_SOURCE_VERSION = "1.6.4-ncc-aaa";
    const GODORI_SESSION_ENABLED_KEY = "ncc.aaa.godori.autoplayer.enabled.session.v1";
    const GODORI_AUTO_PLAY_AGAIN_KEY = "ncc.aaa.godori.autoplayer.avatar.autoPlayAgain.v1";
    const GODORI_AUTO_STOP_250_KEY = "ncc.aaa.godori.autoplayer.avatar.autoStop250.v1";
    const GODORI_MODULE_SOURCE = "(function () {\n  'use strict';\n\n  const VERSION = '1.6.4-ncc-aaa';\n  const MODULE_ID = 'ncc-godori-autoplayer';\n  const MODULE_NAME = 'Godori Autoplayer';\n  const NCC_STATE_EVENT = 'NCC_GODORI_STATE_UPDATE';\n  const NCC_COMMAND_EVENT = 'NCC_GODORI_COMMAND';\n  const LOG_LIMIT = 12;\n\n  const STORAGE_KEY = 'ncc.aaa.godori.autoplayer.enabled.session.v1';\n  const AUTO_PLAY_AGAIN_KEY = 'ncc.aaa.godori.autoplayer.avatar.autoPlayAgain.v1';\n  const AUTO_STOP_250_KEY = 'ncc.aaa.godori.autoplayer.avatar.autoStop250.v1';\n  const TARGET_HANDS = 250;\n\n  const nccGodoriNoop = { log() {}, warn() {}, error() {} };\n  try { localStorage.removeItem('ncc.godori.autoplayer.v1.safe.enabled'); } catch {}\n\n  const CFG = {\n    enabled: false,\n    autoPlayAgain: false,\n    autoStopAtTarget: true,\n    avatarTargetHands: TARGET_HANDS,\n    forceMediumSpeed: true,\n\n    delayBeforePlay: 800,\n    delayHandToTable: 600,\n    pollInterval: 500,\n    selectionRetries: 5,\n    selectionRetryMs: 250,\n    replayDelayMs: 3500,\n    startPageDelayMs: 3200,\n    recoveryPollInterval: 1000,\n    startPageClickCooldownMs: 7000,\n\n    debug: false,\n  };\n\n  // Etapa 6 Continuity Hotfix 6.1:\n  // - Keeps Safe Core, JN Rules Engine, JN Strategy, Avatar Helper and NCC bridge.\n  // - Adds Smart Alert Handler for routine Godori score/result popups.\n  // - v1.6.4-ncc-aaa: suppresses routine ahead/match-not-over OK popups as log entries.\n  // - Adds Start Page Watcher to continue after loss/return to index page.\n  // - Adds recovery polling so the module stays alive even without the board.\n\n\n  function normalizeAlertText(msg) {\n    return String(msg ?? '').replace(/\\s+/g, ' ').trim();\n  }\n\n  function isRoutineGodoriAlert(msg) {\n    const text = normalizeAlertText(msg);\n    if (!text) return false;\n    const lower = text.toLowerCase();\n\n    // Critical alerts must remain visible. These usually mean account/session/game-state trouble.\n    const critical = [\n      'trying to cheat',\n      'not have enough np',\n      \"don't have enough np\",\n      'there was an error',\n      'please refresh',\n      'too much time',\n      'problem generating',\n      'problem with the card layout',\n      'session',\n      'mismatch',\n      'forfeit',\n      'quit?'\n    ];\n    if (critical.some(pattern => lower.includes(pattern))) return false;\n\n    // Routine score/result narration from Godori. These block the JS thread as native alerts,\n    // so we convert them into a log entry and let the native game queue continue.\n    const routine = [\n      'so far it\\'s a tie',\n      'so far its a tie',\n      'you are going to lose the match',\n      'you are behind',\n      'you are ahead',\n      'so you are ahead',\n      \"match isn't over yet\",\n      'match isnt over yet',\n      'the match is not over yet',\n      \"don't get too comfy\",\n      'i\\'m going to make a run for the lead',\n      'ha ha ha',\n      'on with the match',\n      'it was my plan all along',\n      'congratulations, you won the match',\n      'awww, you lost',\n      \"it's a tie\",\n      \"you're just trying to trick me by playing poorly\",\n      'just trying to trick me by playing poorly',\n      'playing poorly',\n      'you play first',\n      'plays first',\n      'goes first',\n      'takes the first turn',\n      'won the match',\n      'lost :0('\n    ];\n\n    return routine.some(pattern => lower.includes(pattern));\n  }\n\n  function queueBootLog(text) {\n    const msg = String(text || '').trim();\n    if (!msg) return;\n    pendingBootLogs.push(msg);\n    while (pendingBootLogs.length > 6) pendingBootLogs.shift();\n    if (CFG.debug) nccGodoriNoop.log('[Godori Continuity]', msg);\n  }\n\n  function flushPendingBootLogs() {\n    if (!pendingBootLogs.length) return;\n    const copy = pendingBootLogs.splice(0, pendingBootLogs.length);\n    for (const msg of copy) logUI(msg);\n  }\n\n  function installSmartAlertHandler() {\n    const targets = [];\n    try { targets.push(window); } catch {}\n    try {\n      const uw = typeof unsafeWindow !== 'undefined' ? unsafeWindow : null;\n      if (uw && !targets.includes(uw)) targets.push(uw);\n    } catch {}\n\n    for (const target of targets) {\n      try {\n        if (target.alert && target.alert.__nccGodoriSmartAlertV16) continue;\n\n        const original = typeof target.alert === 'function' ? target.alert.bind(target) : null;\n        if (!nativeAlert && original) nativeAlert = original;\n\n        const wrappedAlert = function nccGodoriSmartAlert(message) {\n          const text = normalizeAlertText(message);\n          if (isRoutineGodoriAlert(text)) {\n            lastSuppressedAlertText = text;\n            queueBootLog(`Popup Godori convertido em log: ${text.slice(0, 80)}`);\n            try { logUI(`Popup suprimido: ${text.slice(0, 96)}`); } catch {}\n            try { renderUI('Popup de placar processado.'); } catch {}\n            try { setTimeout(checkGame, 80); } catch {}\n            return undefined;\n          }\n\n          const alertFn = nativeAlert || original;\n          if (alertFn) return alertFn(message);\n          return undefined;\n        };\n        try { wrappedAlert.__nccGodoriSmartAlertV16 = true; } catch {}\n\n        Object.defineProperty(target, 'alert', {\n          configurable: true,\n          writable: true,\n          value: wrappedAlert,\n        });\n        smartAlertInstalled = true;\n      } catch {}\n    }\n  }\n\n  function readStoredBool(key, defaultValue = false) {\n    try {\n      const raw = localStorage.getItem(key);\n      if (raw === null) return defaultValue;\n      return raw === '1';\n    } catch {\n      return defaultValue;\n    }\n  }\n\n  function writeStoredBool(key, value) {\n    try {\n      localStorage.setItem(key, value ? '1' : '0');\n    } catch {}\n  }\n\n  function readStoredEnabled() {\n    try { return sessionStorage.getItem(STORAGE_KEY) === '1'; } catch { return false; }\n  }\n\n  function writeStoredEnabled(value) {\n    try {\n      if (value) sessionStorage.setItem(STORAGE_KEY, '1');\n      else sessionStorage.removeItem(STORAGE_KEY);\n    } catch {}\n  }\n\n  CFG.enabled = readStoredEnabled();\n  CFG.autoPlayAgain = readStoredBool(AUTO_PLAY_AGAIN_KEY, false);\n  CFG.autoStopAtTarget = readStoredBool(AUTO_STOP_250_KEY, true);\n\n  // == Card identity constants ==\n  const NEOPETS_CARDS = new Set(['augk','jank','mark','novk','deck']);\n\n  // sepy is special: for scoring it may count as Petpet OR World, not both.\n  const SPECIAL_SEPY = 'sepy';\n\n  const FLYING_PETPETS = new Set(['apry','augy','feby']);\n  const BASE_PETPETS = new Set(['feby','apry','mayy','juny','july','augy','octy','decy']);\n\n  const AC_WAVING = new Set(['jant','febt','mart']);\n  const AC_HANGING = new Set(['mayt','aprt','jult']);\n  const AC_RIBBON = new Set(['octt','sept','junt']);\n  const AC_TRIOS = [AC_WAVING, AC_HANGING, AC_RIBBON];\n  const ALL_AC = new Set(['jant','febt','mart','aprt','mayt','junt','jult','sept','octt','dect']);\n\n  // These World cards count as two World cards for score purposes.\n  const DOUBLE_WORLD = new Set(['nov3','dec2']);\n\n  // == Floating UI ==\n  let uiRoot = null;\n  let uiButton = null;\n  let uiStatus = null;\n  let uiLog = null;\n  let uiProgress = null;\n  let uiAutoPlayButton = null;\n  let uiAutoStopButton = null;\n\n  let lastLogText = '';\n  let lastMoveText = '';\n  let lastReasonText = '';\n  let lastDecisionText = '';\n  let lastCommandText = '';\n  let watcherStarted = false;\n  let continuityWatcherStarted = false;\n  let bridgeInstalled = false;\n  let smartAlertInstalled = false;\n  let nativeAlert = null;\n  let lastSuppressedAlertText = '';\n  let lastStartPageActionAt = 0;\n  let startPageClickPending = false;\n  const pendingBootLogs = [];\n  const logBuffer = [];\n\n  installSmartAlertHandler();\n\n  function installUI() {\n    if (document.getElementById('ncc-godori-safe-core')) return;\n\n    const style = document.createElement('style');\n    style.id = 'ncc-godori-safe-core-style';\n    style.textContent = `\n      #ncc-godori-safe-core {\n        position: fixed;\n        right: 14px;\n        top: 120px;\n        z-index: 2147482500;\n        width: 176px;\n        font-family: Verdana, Arial, Helvetica, sans-serif;\n        font-size: 11px;\n        color: #2b1b08;\n        background: #fff8dc;\n        border: 2px solid #c99220;\n        border-radius: 10px;\n        box-shadow: 0 5px 16px rgba(0,0,0,.28);\n        overflow: hidden;\n        text-align: left;\n      }\n      #ncc-godori-safe-core .ncc-godori-head {\n        background: linear-gradient(#ffd95a, #f1b900);\n        padding: 6px 7px;\n        font-weight: bold;\n        border-bottom: 1px solid #b98500;\n      }\n      #ncc-godori-safe-core .ncc-godori-body {\n        padding: 7px;\n        display: grid;\n        gap: 6px;\n      }\n      #ncc-godori-safe-core .ncc-godori-toggle {\n        border: 1px solid #8d6500;\n        border-radius: 8px;\n        padding: 6px;\n        font-weight: bold;\n        cursor: pointer;\n        color: white;\n        background: #9a2525;\n        text-align: center;\n        user-select: none;\n      }\n      #ncc-godori-safe-core[data-enabled=\"1\"] .ncc-godori-toggle {\n        background: #197b32;\n      }\n      #ncc-godori-safe-core .ncc-godori-status {\n        background: rgba(255,255,255,.72);\n        border: 1px solid rgba(141,101,0,.35);\n        border-radius: 7px;\n        padding: 5px;\n        min-height: 14px;\n      }\n      #ncc-godori-safe-core .ncc-godori-log {\n        background: rgba(255,255,255,.72);\n        border: 1px solid rgba(141,101,0,.35);\n        border-radius: 7px;\n        padding: 5px;\n        min-height: 28px;\n        max-height: 56px;\n        overflow: hidden;\n        line-height: 1.25;\n        color: #4b3a1a;\n      }\n      #ncc-godori-safe-core .ncc-godori-progress {\n        background: rgba(255,255,255,.78);\n        border: 1px solid rgba(141,101,0,.35);\n        border-radius: 7px;\n        padding: 5px;\n        min-height: 34px;\n        line-height: 1.25;\n        color: #3f2d0a;\n      }\n      #ncc-godori-safe-core .ncc-godori-bar {\n        width: 100%;\n        height: 7px;\n        border-radius: 999px;\n        background: rgba(120,86,0,.18);\n        overflow: hidden;\n        margin-top: 4px;\n      }\n      #ncc-godori-safe-core .ncc-godori-bar-fill {\n        height: 100%;\n        width: 0%;\n        background: linear-gradient(90deg, #4ea244, #f1b900);\n      }\n      #ncc-godori-safe-core .ncc-godori-actions {\n        display: grid;\n        grid-template-columns: 1fr;\n        gap: 4px;\n      }\n      #ncc-godori-safe-core .ncc-godori-option {\n        border: 1px solid rgba(141,101,0,.55);\n        border-radius: 7px;\n        padding: 5px;\n        cursor: pointer;\n        font-size: 10.5px;\n        font-weight: bold;\n        color: #4b3a1a;\n        background: #fff1b8;\n      }\n      #ncc-godori-safe-core .ncc-godori-option[data-on=\"1\"] {\n        color: white;\n        background: #2f8a43;\n      }\n      #ncc-godori-safe-core .ncc-godori-note,\n      #ncc-godori-safe-core .ncc-godori-integration {\n        font-size: 10px;\n        color: #6c551e;\n        line-height: 1.25;\n      }\n      #ncc-godori-safe-core .ncc-godori-integration {\n        border-top: 1px dashed rgba(141,101,0,.35);\n        padding-top: 4px;\n      }\n    `;\n    document.documentElement.appendChild(style);\n\n    uiRoot = document.createElement('div');\n    uiRoot.id = 'ncc-godori-safe-core';\n    uiRoot.setAttribute('data-enabled', CFG.enabled ? '1' : '0');\n    uiRoot.setAttribute('data-ncc-module', MODULE_ID);\n    uiRoot.setAttribute('data-ncc-version', VERSION);\n    uiRoot.innerHTML = `\n      <div class=\"ncc-godori-head\">Godori Autoplayer</div>\n      <div class=\"ncc-godori-body\">\n        <button type=\"button\" class=\"ncc-godori-toggle\"></button>\n        <div class=\"ncc-godori-status\"></div>\n        <div class=\"ncc-godori-progress\"></div>\n        <div class=\"ncc-godori-log\"></div>\n        <div class=\"ncc-godori-actions\">\n          <button type=\"button\" class=\"ncc-godori-option ncc-godori-autoplay\"></button>\n          <button type=\"button\" class=\"ncc-godori-option ncc-godori-autostop\"></button>\n        </div>\n        <div class=\"ncc-godori-note\">ON inicia a tela inicial. Auto Replay s\u00f3 reinicia ap\u00f3s partidas.</div>\n        <div class=\"ncc-godori-integration\">NCC AAA bridge + recovery ativo.</div>\n      </div>\n    `;\n\n    document.body.appendChild(uiRoot);\n\n    uiButton = uiRoot.querySelector('.ncc-godori-toggle');\n    uiStatus = uiRoot.querySelector('.ncc-godori-status');\n    uiLog = uiRoot.querySelector('.ncc-godori-log');\n    uiProgress = uiRoot.querySelector('.ncc-godori-progress');\n    uiAutoPlayButton = uiRoot.querySelector('.ncc-godori-autoplay');\n    uiAutoStopButton = uiRoot.querySelector('.ncc-godori-autostop');\n\n    uiButton.addEventListener('click', () => {\n      CFG.enabled = !CFG.enabled;\n      writeStoredEnabled(CFG.enabled);\n      if (CFG.enabled) forceSafeGameSpeed();\n      renderUI(CFG.enabled ? 'Ligado. Aguardando turno.' : 'Desligado.');\n      logUI(CFG.enabled ? 'Autoplayer ativado.' : 'Autoplayer desativado.');\n      publishState();\n    });\n\n    uiAutoPlayButton.addEventListener('click', () => {\n      CFG.autoPlayAgain = !CFG.autoPlayAgain;\n      writeStoredBool(AUTO_PLAY_AGAIN_KEY, CFG.autoPlayAgain);\n      renderUI();\n      logUI(CFG.autoPlayAgain ? 'Auto Play Again ativado.' : 'Auto Play Again desativado.');\n      publishState();\n    });\n\n    uiAutoStopButton.addEventListener('click', () => {\n      CFG.autoStopAtTarget = !CFG.autoStopAtTarget;\n      writeStoredBool(AUTO_STOP_250_KEY, CFG.autoStopAtTarget);\n      renderUI();\n      logUI(CFG.autoStopAtTarget ? 'Auto Stop em 250 ativado.' : 'Auto Stop em 250 desativado.');\n      publishState();\n    });\n\n    renderUI(CFG.enabled ? 'Ligado. Aguardando turno.' : 'Desligado.');\n    flushPendingBootLogs();\n    logUI('v1.6.4 Alert OK Hotfix carregado.');\n  }\n\n  function renderUI(statusText) {\n    if (!uiRoot) return;\n    uiRoot.setAttribute('data-enabled', CFG.enabled ? '1' : '0');\n    if (uiButton) uiButton.textContent = CFG.enabled ? 'ON' : 'OFF';\n    if (uiStatus) uiStatus.textContent = statusText || (CFG.enabled ? 'Ligado.' : 'Desligado.');\n    renderAvatarProgress();\n    renderOptionButtons();\n  }\n\n  function renderOptionButtons() {\n    if (uiAutoPlayButton) {\n      uiAutoPlayButton.setAttribute('data-on', CFG.autoPlayAgain ? '1' : '0');\n      uiAutoPlayButton.textContent = `Auto Replay: ${CFG.autoPlayAgain ? 'ON' : 'OFF'}`;\n    }\n    if (uiAutoStopButton) {\n      uiAutoStopButton.setAttribute('data-on', CFG.autoStopAtTarget ? '1' : '0');\n      uiAutoStopButton.textContent = `Auto Stop 250: ${CFG.autoStopAtTarget ? 'ON' : 'OFF'}`;\n    }\n  }\n\n  function logUI(text) {\n    const msg = text || '';\n    lastLogText = msg;\n    if (msg) {\n      logBuffer.push({ at: Date.now(), message: msg });\n      while (logBuffer.length > LOG_LIMIT) logBuffer.shift();\n    }\n    if (CFG.debug) nccGodoriNoop.log('[Godori NCC Integration]', msg);\n    if (uiLog) uiLog.textContent = msg;\n    publishState({ lastLog: msg });\n  }\n\n  function shortCardList(ids) {\n    if (!ids || !ids.length) return 'empty';\n    return ids.join(', ');\n  }\n\n  function parseIntLoose(text) {\n    const m = String(text || '').replace(/,/g, '').match(/-?\\d+/);\n    return m ? parseInt(m[0], 10) : 0;\n  }\n\n  function formatNumber(n) {\n    try {\n      return Number(n || 0).toLocaleString('en-US');\n    } catch {\n      return String(n || 0);\n    }\n  }\n\n  function normalizeStatLabel(text) {\n    return String(text || '').replace(/\\s+/g, ' ').trim().toLowerCase();\n  }\n\n  function readAvatarStats() {\n    const empty = {\n      found: false,\n      npWon: 0,\n      handsWon: 0,\n      matchesWon: 0,\n      topHandsInRow: 0,\n      topMatchesInRow: 0,\n      targetHands: CFG.avatarTargetHands,\n      remainingHands: CFG.avatarTargetHands,\n      progressPct: 0,\n    };\n\n    const table = document.querySelector('table.g_stats_table');\n    if (!table) return empty;\n\n    const rows = [...table.querySelectorAll('tr')];\n    if (rows.length < 2) return empty;\n\n    const headers = [...rows[0].querySelectorAll('td, th')].map(el => normalizeStatLabel(el.textContent));\n    const values = [...rows[1].querySelectorAll('td, th')].map(el => parseIntLoose(el.textContent));\n\n    function byLabel(label, fallbackIndex = -1) {\n      const idx = headers.findIndex(h => h === label || h.includes(label));\n      if (idx >= 0 && idx < values.length) return values[idx] || 0;\n      if (fallbackIndex >= 0 && fallbackIndex < values.length) return values[fallbackIndex] || 0;\n      return 0;\n    }\n\n    const handsWon = byLabel('hands won', 1);\n    const target = CFG.avatarTargetHands;\n    const remaining = Math.max(0, target - handsWon);\n    const pct = Math.max(0, Math.min(100, Math.floor((handsWon / target) * 100)));\n\n    return {\n      found: true,\n      npWon: byLabel('np won', 0),\n      handsWon,\n      matchesWon: byLabel('matches won', 2),\n      topHandsInRow: byLabel('top hands in a row', 3),\n      topMatchesInRow: byLabel('top matches in a row', 4),\n      targetHands: target,\n      remainingHands: remaining,\n      progressPct: pct,\n    };\n  }\n\n  function renderAvatarProgress() {\n    if (!uiProgress) return;\n    const stats = readAvatarStats();\n    if (!stats.found) {\n      uiProgress.innerHTML = `Avatar: aguardando stats...<div class=\"ncc-godori-bar\"><div class=\"ncc-godori-bar-fill\" style=\"width:0%\"></div></div>`;\n      return;\n    }\n\n    const hands = formatNumber(stats.handsWon);\n    const target = formatNumber(stats.targetHands);\n    const remaining = formatNumber(stats.remainingHands);\n    uiProgress.innerHTML = `Avatar: <b>${hands}/${target}</b> hands<br>Faltam: <b>${remaining}</b> \u00b7 ${stats.progressPct}%<div class=\"ncc-godori-bar\"><div class=\"ncc-godori-bar-fill\" style=\"width:${stats.progressPct}%\"></div></div>`;\n  }\n\n  function enforceAvatarStop(stats = readAvatarStats()) {\n    if (!CFG.enabled || !CFG.autoStopAtTarget) return false;\n    if (!stats.found || stats.handsWon < CFG.avatarTargetHands) return false;\n\n    CFG.enabled = false;\n    writeStoredEnabled(false);\n    renderUI('Meta 250 atingida. Autoplayer parado.');\n    logUI(`Auto Stop: ${stats.handsWon}/${CFG.avatarTargetHands} hands won.`);\n    publishState({ avatarStopTriggered: true });\n    return true;\n  }\n\n\n  // == NCC/AAA integration bridge ==\n  function setEnabledFromBridge(value, source = 'bridge') {\n    CFG.enabled = !!value;\n    writeStoredEnabled(CFG.enabled);\n    if (CFG.enabled) forceSafeGameSpeed();\n    lastCommandText = `${source}: setEnabled(${CFG.enabled ? 'ON' : 'OFF'})`;\n    renderUI(CFG.enabled ? 'Ligado pelo NCC.' : 'Desligado pelo NCC.');\n    logUI(CFG.enabled ? 'NCC: Autoplayer ativado.' : 'NCC: Autoplayer desativado.');\n    return publishState({ commandSource: source, command: 'setEnabled' });\n  }\n\n  function setAutoPlayAgainFromBridge(value, source = 'bridge') {\n    CFG.autoPlayAgain = !!value;\n    writeStoredBool(AUTO_PLAY_AGAIN_KEY, CFG.autoPlayAgain);\n    lastCommandText = `${source}: setAutoPlayAgain(${CFG.autoPlayAgain ? 'ON' : 'OFF'})`;\n    renderUI();\n    logUI(CFG.autoPlayAgain ? 'NCC: Auto Play Again ativado.' : 'NCC: Auto Play Again desativado.');\n    return publishState({ commandSource: source, command: 'setAutoPlayAgain' });\n  }\n\n  function setAutoStopAtTargetFromBridge(value, source = 'bridge') {\n    CFG.autoStopAtTarget = !!value;\n    writeStoredBool(AUTO_STOP_250_KEY, CFG.autoStopAtTarget);\n    lastCommandText = `${source}: setAutoStopAtTarget(${CFG.autoStopAtTarget ? 'ON' : 'OFF'})`;\n    renderUI();\n    logUI(CFG.autoStopAtTarget ? 'NCC: Auto Stop 250 ativado.' : 'NCC: Auto Stop 250 desativado.');\n    return publishState({ commandSource: source, command: 'setAutoStopAtTarget' });\n  }\n\n  function handleBridgeCommand(detail) {\n    if (!detail || typeof detail !== 'object') return publishState({ commandIgnored: 'empty' });\n    const action = String(detail.action || detail.type || '').toLowerCase();\n    const value = detail.value;\n    const source = detail.source || 'NCC_COMMAND_EVENT';\n\n    switch (action) {\n      case 'enable':\n      case 'on':\n        return setEnabledFromBridge(true, source);\n      case 'disable':\n      case 'off':\n        return setEnabledFromBridge(false, source);\n      case 'toggle':\n        return setEnabledFromBridge(!CFG.enabled, source);\n      case 'setenabled':\n        return setEnabledFromBridge(value, source);\n      case 'setautoplayagain':\n        return setAutoPlayAgainFromBridge(value, source);\n      case 'setautostopattarget':\n      case 'setautostop250':\n        return setAutoStopAtTargetFromBridge(value, source);\n      case 'check':\n      case 'refresh':\n        lastCommandText = `${source}: ${action}`;\n        checkGame();\n        return publishState({ commandSource: source, command: action });\n      case 'playonce':\n        lastCommandText = `${source}: playOnce`;\n        doPlay();\n        return publishState({ commandSource: source, command: 'playOnce' });\n      case 'forcemediumspeed':\n        lastCommandText = `${source}: forceMediumSpeed`;\n        forceSafeGameSpeed();\n        return publishState({ commandSource: source, command: 'forceMediumSpeed' });\n      case 'startfromstartpage':\n      case 'startpageplay':\n        lastCommandText = `${source}: startFromStartPage`;\n        tryStartFromStartPage(source);\n        return publishState({ commandSource: source, command: 'startFromStartPage' });\n      default:\n        lastCommandText = `${source}: unknown(${action || 'none'})`;\n        return publishState({ commandSource: source, commandIgnored: action || 'none' });\n    }\n  }\n\n  function installBridge() {\n    if (bridgeInstalled) return;\n    bridgeInstalled = true;\n\n    const api = {\n      version: VERSION,\n      moduleId: MODULE_ID,\n      moduleName: MODULE_NAME,\n      getState: () => publishState({ bridgeRead: true }),\n      refresh: () => handleBridgeCommand({ action: 'refresh', source: 'api' }),\n      enable: () => setEnabledFromBridge(true, 'api'),\n      disable: () => setEnabledFromBridge(false, 'api'),\n      toggle: () => setEnabledFromBridge(!CFG.enabled, 'api'),\n      setEnabled: value => setEnabledFromBridge(value, 'api'),\n      setAutoPlayAgain: value => setAutoPlayAgainFromBridge(value, 'api'),\n      setAutoStopAtTarget: value => setAutoStopAtTargetFromBridge(value, 'api'),\n      playOnce: () => handleBridgeCommand({ action: 'playOnce', source: 'api' }),\n      forceMediumSpeed: () => handleBridgeCommand({ action: 'forceMediumSpeed', source: 'api' }),\n      startFromStartPage: () => tryStartFromStartPage('api'),\n    };\n\n    try { window.NCC_GODORI_API = api; } catch {}\n    try { getUnsafeWindow().NCC_GODORI_API = api; } catch {}\n\n    window.addEventListener(NCC_COMMAND_EVENT, event => {\n      try { handleBridgeCommand(event.detail || {}); } catch (err) { nccGodoriNoop.error('[Godori NCC Integration] command error:', err); }\n    });\n\n    publishState({ bridgeReady: true });\n  }\n\n  // == DOM helpers ==\n  const getStatus = () => (document.getElementById('game_status')?.textContent ?? '').trim();\n  const isTurn = () => {\n    const s = getStatus().toLowerCase();\n    return s.includes('your turn') || s.includes('your move');\n  };\n\n  function getUnsafeWindow() {\n    try {\n      if (typeof unsafeWindow !== 'undefined' && unsafeWindow) return unsafeWindow;\n    } catch {}\n    return window;\n  }\n\n  function getNative(name) {\n    const uw = getUnsafeWindow();\n    try {\n      if (typeof uw[name] !== 'undefined') return uw[name];\n    } catch {}\n    try {\n      if (typeof window[name] !== 'undefined') return window[name];\n    } catch {}\n    return undefined;\n  }\n\n  function gameBusy() {\n    try {\n      return getNative('move_in_process') === true;\n    } catch {\n      return false;\n    }\n  }\n\n  function cardIdFromSrc(src) {\n    if (!src) return null;\n\n    let pathname = '';\n    try {\n      pathname = new URL(src, location.href).pathname;\n    } catch {\n      pathname = String(src).split('?')[0].split('#')[0];\n    }\n\n    const file = pathname.split('/').pop() || '';\n    const m = file.match(/^([a-z]{3}[a-z0-9]?|deck)\\.gif$/i);\n    if (!m) return null;\n\n    const id = m[1].toLowerCase();\n    if (id === 'back' || id === 'empty') return null;\n    if (String(src).includes('/small/')) return null;\n    return id;\n  }\n\n  function allCardIdsInContainer(div) {\n    if (!div) return [];\n    const ids = [];\n\n    for (const img of div.querySelectorAll('img')) {\n      if (img.classList.contains('g_lrg_card_hidden')) continue;\n      if (img.classList.contains('g_lrg_card_visible')) continue;\n      if (img.classList.contains('g_card_back')) continue;\n\n      const id = cardIdFromSrc(img.getAttribute('src') || img.src || '');\n      if (id) ids.push(id);\n    }\n\n    return ids;\n  }\n\n  function singleCardId(div) {\n    return allCardIdsInContainer(div)[0] ?? null;\n  }\n\n  const monthFromId = id => id ? id.slice(0, 3) : null;\n  const isHandCardSelected = div => !!div?.querySelector('img.g_card_highlight');\n\n  // == Game-state readers ==\n  function getPlayerCards() {\n    const cards = [];\n    for (let i = 1; i <= 10; i++) {\n      const div = document.getElementById(`player_${i}`);\n      const id = singleCardId(div);\n      if (id) cards.push({ div, id, month: monthFromId(id), pos: i });\n    }\n    return cards;\n  }\n\n  function getTableSlots() {\n    const slots = [];\n    for (let i = 1; i <= 12; i++) {\n      const div = document.getElementById(`table_${i}`);\n      const ids = allCardIdsInContainer(div);\n      if (ids.length > 0) slots.push({ div, ids, month: monthFromId(ids[0]), pos: i });\n    }\n    return slots;\n  }\n\n  function getEmptyTableSlot() {\n    for (let i = 1; i <= 12; i++) {\n      const div = document.getElementById(`table_${i}`);\n      if (!div) continue;\n      if (allCardIdsInContainer(div).length === 0) return div;\n    }\n    return null;\n  }\n\n  const PLAYER_CAP_IDS = ['player_bright','player_animal','player_ribbon','player_junk'];\n  const OPPONENT_CAP_IDS = ['comp_bright','comp_animal','comp_ribbon','comp_junk'];\n\n  function readCaptureArea(containerIds) {\n    const captured = new Set();\n    for (const cid of containerIds) {\n      const div = document.getElementById(cid);\n      if (!div) continue;\n\n      // Safe Core improvement: count both hidden and currently-hover-visible large cards.\n      for (const img of div.querySelectorAll('img.g_lrg_card_hidden, img.g_lrg_card_visible')) {\n        const id = cardIdFromSrc(img.getAttribute('src') || img.src || '');\n        if (id) captured.add(id);\n      }\n    }\n    return captured;\n  }\n\n  const getPlayerCaptured = () => readCaptureArea(PLAYER_CAP_IDS);\n  const getOpponentCaptured = () => readCaptureArea(OPPONENT_CAP_IDS);\n\n  function parseScoreEl(id) {\n    const el = document.getElementById(id);\n    if (!el) return 0;\n    const m = el.textContent.match(/:\\s*(-?\\d+)/);\n    return m ? parseInt(m[1], 10) : 0;\n  }\n\n  const getScores = () => ({\n    playerScore: parseScoreEl('user_score'),\n    opponentScore: parseScoreEl('comp_score'),\n  });\n\n  // == JN-aligned scoring model / Etapa 2 Rules Engine ==\n  function setCount(captured, set) {\n    let n = 0;\n    for (const id of set) if (captured.has(id)) n++;\n    return n;\n  }\n\n  function addCards(captured, cardIds) {\n    const next = new Set(captured || []);\n    for (const id of cardIds || []) if (id) next.add(id);\n    return next;\n  }\n\n  function scoreNeopetsCards(captured) {\n    const have = [...NEOPETS_CARDS].filter(id => captured.has(id));\n    const n = have.length;\n\n    if (n >= 5) return 15;\n    if (n === 4) return 4;\n    if (n === 3) return have.includes('deck') ? 2 : 3;\n    return 0;\n  }\n\n  function countWorldCards(captured, sepyAsWorld = false) {\n    let n = 0;\n\n    for (const id of captured) {\n      if (id === SPECIAL_SEPY) {\n        if (sepyAsWorld) n += 1;\n        continue;\n      }\n\n      if (NEOPETS_CARDS.has(id) || BASE_PETPETS.has(id) || ALL_AC.has(id)) continue;\n      n += DOUBLE_WORLD.has(id) ? 2 : 1;\n    }\n\n    return n;\n  }\n\n  function scoreWorldCards(captured, sepyAsWorld = false) {\n    const n = countWorldCards(captured, sepyAsWorld);\n    return n >= 10 ? n - 9 : 0;\n  }\n\n  function countPetpetCards(captured, sepyAsPetpet = false) {\n    let n = setCount(captured, BASE_PETPETS);\n    if (sepyAsPetpet && captured.has(SPECIAL_SEPY)) n += 1;\n    return n;\n  }\n\n  function scorePetpetCards(captured, sepyAsPetpet = false) {\n    const count = countPetpetCards(captured, sepyAsPetpet);\n    let score = count >= 5 ? count - 4 : 0;\n    if ([...FLYING_PETPETS].every(id => captured.has(id))) score += 5;\n    return score;\n  }\n\n  function scoreAltadorCupCards(captured) {\n    const count = setCount(captured, ALL_AC);\n    let score = count >= 5 ? count - 4 : 0;\n    for (const trio of AC_TRIOS) {\n      if ([...trio].every(id => captured.has(id))) score += 3;\n    }\n    return score;\n  }\n\n  function scoreTotal(captured) {\n    const fixedScore = scoreNeopetsCards(captured) + scoreAltadorCupCards(captured);\n\n    if (!captured.has(SPECIAL_SEPY)) {\n      return fixedScore\n        + scorePetpetCards(captured, false)\n        + scoreWorldCards(captured, false);\n    }\n\n    const sepyAsPetpet = scorePetpetCards(captured, true) + scoreWorldCards(captured, false);\n    const sepyAsWorld = scorePetpetCards(captured, false) + scoreWorldCards(captured, true);\n\n    return fixedScore + Math.max(sepyAsPetpet, sepyAsWorld);\n  }\n\n  function cardPotentialValue(cardId, captured) {\n    if (!cardId) return 0;\n\n    let v = 0;\n\n    if (NEOPETS_CARDS.has(cardId)) {\n      const have = setCount(captured, NEOPETS_CARDS);\n      const otherNonDeck = [...NEOPETS_CARDS].filter(id => id !== 'deck' && captured.has(id)).length;\n\n      // Neopets cards are the highest-impact group. Haunted Woods/deck is weaker\n      // until a real trio/quad/five-card path exists.\n      if (cardId === 'deck' && otherNonDeck < 2) v += 22;\n      else v += 52;\n\n      if (have === 1) v += 12;\n      if (have === 2) v += cardId === 'deck' ? 18 : 34;\n      if (have === 3) v += 18;\n      if (have === 4) v += 70;\n    }\n\n    if (FLYING_PETPETS.has(cardId)) {\n      const have = setCount(captured, FLYING_PETPETS);\n      v += have === 2 ? 62 : have === 1 ? 28 : 12;\n    }\n\n    if (BASE_PETPETS.has(cardId) || cardId === SPECIAL_SEPY) {\n      const petCount = countPetpetCards(captured, false);\n      v += petCount >= 4 ? 20 : Math.max(5, (petCount + 1) * 3);\n    }\n\n    if (ALL_AC.has(cardId)) {\n      for (const trio of AC_TRIOS) {\n        if (trio.has(cardId)) {\n          const have = setCount(captured, trio);\n          v += have === 2 ? 38 : have === 1 ? 16 : 6;\n        }\n      }\n      const acCount = setCount(captured, ALL_AC);\n      v += acCount >= 4 ? 18 : Math.max(5, (acCount + 1) * 3);\n    }\n\n    const canBeWorld = cardId === SPECIAL_SEPY\n      || (!NEOPETS_CARDS.has(cardId) && !BASE_PETPETS.has(cardId) && !ALL_AC.has(cardId));\n\n    if (canBeWorld) {\n      const wc = countWorldCards(captured, false);\n      const add = DOUBLE_WORLD.has(cardId) ? 2 : 1;\n      v += DOUBLE_WORLD.has(cardId) ? 24 : 7;\n      if (wc + add >= 10) v += 20;\n      if (wc >= 10) v += 12 * add;\n    }\n\n    return v;\n  }\n\n  function marginalBundleValue(cardIds, captured) {\n    const before = scoreTotal(captured);\n    const afterSet = addCards(captured, cardIds);\n    const after = scoreTotal(afterSet);\n\n    let v = (after - before) * 100;\n\n    // Add potential using a rolling set so the second/third captured card can see\n    // the first card already added.\n    const rolling = new Set(captured || []);\n    for (const id of cardIds || []) {\n      v += cardPotentialValue(id, rolling);\n      rolling.add(id);\n    }\n\n    return v;\n  }\n\n  function marginalValue(cardId, captured) {\n    return marginalBundleValue([cardId], captured);\n  }\n\n  function isCapturableStack(slot) {\n    return !!slot && (slot.ids.length === 1 || slot.ids.length === 3);\n  }\n\n  function isSpecialRiskStack(slot) {\n    return !!slot && slot.ids.length === 2;\n  }\n\n  function isAbnormalStack(slot) {\n    return !!slot && slot.ids.length >= 4;\n  }\n\n  function getStrategyWeights(playerScore, opponentScore) {\n    const pGap = 50 - playerScore;\n    const oGap = 50 - opponentScore;\n\n    if (oGap <= 10) return { selfMult: 0.5, denialMult: 2.5 };\n    if (pGap <= 10) return { selfMult: 2.0, denialMult: 0.8 };\n    if (pGap < oGap) return { selfMult: 1.3, denialMult: 1.0 };\n    return { selfMult: 1.0, denialMult: 1.2 };\n  }\n\n\n  // == Etapa 3: JN Strategy helpers ==\n  function sameWorldHandSupport(card, playerCards) {\n    if (!card) return [];\n    return (playerCards || []).filter(other => other.id !== card.id && other.month === card.month);\n  }\n\n  function hasProtectedNeopetsPairInHand(card, playerCards) {\n    return !!card && NEOPETS_CARDS.has(card.id) && sameWorldHandSupport(card, playerCards).length > 0;\n  }\n\n  function groupProgressValue(cardId, captured, defensive = false) {\n    if (!cardId) return 0;\n    let value = 0;\n\n    if (NEOPETS_CARDS.has(cardId)) {\n      const have = setCount(captured, NEOPETS_CARDS);\n      if (have >= 4) value += defensive ? 150 : 130;\n      else if (have === 3) value += defensive ? 78 : 62;\n      else if (have === 2) value += defensive ? 96 : 76;\n      else if (have === 1) value += defensive ? 32 : 24;\n\n      // Haunted Woods/deck is weaker for a clean 3-card set, but it is still\n      // highly relevant once a 4/5-card route exists.\n      if (cardId === 'deck' && have < 2) value -= 18;\n    }\n\n    if (FLYING_PETPETS.has(cardId)) {\n      const have = setCount(captured, FLYING_PETPETS);\n      if (have === 2) value += defensive ? 130 : 108;\n      else if (have === 1) value += defensive ? 44 : 34;\n      else value += 12;\n    }\n\n    for (const trio of AC_TRIOS) {\n      if (!trio.has(cardId)) continue;\n      const have = setCount(captured, trio);\n      if (have === 2) value += defensive ? 96 : 78;\n      else if (have === 1) value += defensive ? 32 : 24;\n      else value += 8;\n    }\n\n    if (BASE_PETPETS.has(cardId) || cardId === SPECIAL_SEPY) {\n      const petCount = countPetpetCards(captured, false);\n      if (petCount >= 4) value += defensive ? 34 : 28;\n      else if (petCount === 3) value += defensive ? 16 : 12;\n    }\n\n    const canBeWorld = cardId === SPECIAL_SEPY\n      || (!NEOPETS_CARDS.has(cardId) && !BASE_PETPETS.has(cardId) && !ALL_AC.has(cardId));\n\n    if (canBeWorld) {\n      const add = DOUBLE_WORLD.has(cardId) ? 2 : 1;\n      const wc = countWorldCards(captured, false);\n      if (wc + add >= 10) value += defensive ? 38 : 30;\n      if (DOUBLE_WORLD.has(cardId)) value += defensive ? 42 : 34;\n    }\n\n    return Math.max(0, value);\n  }\n\n  function bundleProgressValue(cardIds, captured, defensive = false) {\n    const rolling = new Set(captured || []);\n    let value = 0;\n    for (const id of cardIds || []) {\n      value += groupProgressValue(id, rolling, defensive);\n      rolling.add(id);\n    }\n    return value;\n  }\n\n  function handPreservationValue(card, playerCards, playerCaptured) {\n    if (!card) return 0;\n    let value = 0;\n\n    if (NEOPETS_CARDS.has(card.id)) {\n      value += card.id === 'deck' ? 52 : 78;\n      if (hasProtectedNeopetsPairInHand(card, playerCards)) value += 92;\n\n      const have = setCount(playerCaptured, NEOPETS_CARDS);\n      if (have === 2 && card.id !== 'deck') value += 54;\n      if (have >= 3) value += 28;\n    }\n\n    if (FLYING_PETPETS.has(card.id)) {\n      const have = setCount(playerCaptured, FLYING_PETPETS);\n      value += have === 2 ? 82 : have === 1 ? 38 : 18;\n    }\n\n    for (const trio of AC_TRIOS) {\n      if (!trio.has(card.id)) continue;\n      const have = setCount(playerCaptured, trio);\n      value += have === 2 ? 64 : have === 1 ? 26 : 10;\n    }\n\n    if (DOUBLE_WORLD.has(card.id)) value += 38;\n    if (card.id === SPECIAL_SEPY) value += 30;\n\n    return value;\n  }\n\n  function opponentExposureRisk(cardId, opponentCaptured) {\n    if (!cardId) return 0;\n    return marginalValue(cardId, opponentCaptured) * 0.35\n      + groupProgressValue(cardId, opponentCaptured, true);\n  }\n\n  function captureStrategyTags(captureIds, pc, slot, playerCards, playerCaptured, opponentCaptured) {\n    const tags = [];\n\n    if ((slot?.ids || []).some(id => NEOPETS_CARDS.has(id))) tags.push('Neopets exposto');\n    if ((slot?.ids || []).some(id => DOUBLE_WORLD.has(id))) tags.push('mundo duplo');\n    if (captureIds.some(id => FLYING_PETPETS.has(id))) tags.push('Flying Petpet');\n\n    for (const trio of AC_TRIOS) {\n      if (captureIds.some(id => trio.has(id))) {\n        const ownHave = setCount(playerCaptured, trio);\n        const oppHave = setCount(opponentCaptured, trio);\n        if (ownHave >= 2) tags.push('fecha trio AC');\n        if (oppHave >= 2) tags.push('nega trio AC');\n      }\n    }\n\n    if (captureIds.some(id => NEOPETS_CARDS.has(id))) {\n      const ownNeo = setCount(playerCaptured, NEOPETS_CARDS);\n      const oppNeo = setCount(opponentCaptured, NEOPETS_CARDS);\n      if (ownNeo >= 2) tags.push('progresso Neopets');\n      if (oppNeo >= 2) tags.push('nega Neopets');\n    }\n\n    if (captureIds.some(id => FLYING_PETPETS.has(id))) {\n      if (setCount(playerCaptured, FLYING_PETPETS) >= 2) tags.push('fecha Flying');\n      if (setCount(opponentCaptured, FLYING_PETPETS) >= 2) tags.push('nega Flying');\n    }\n\n    if (hasProtectedNeopetsPairInHand(pc, playerCards) && !(slot?.ids || []).some(id => NEOPETS_CARDS.has(id))) {\n      tags.push('par Neopets protegido');\n    }\n\n    return tags;\n  }\n\n  function chooseBestPlay(playerCards, tableSlots, playerCaptured, opponentCaptured, playerScore, opponentScore) {\n    const { selfMult, denialMult } = getStrategyWeights(playerScore, opponentScore);\n\n    const slotByMonth = {};\n    for (const slot of tableSlots) {\n      // The game should have only one stack per world. If an abnormal duplicate\n      // ever appears, keep the largest stack as the safest representation.\n      if (!slotByMonth[slot.month] || slot.ids.length > slotByMonth[slot.month].ids.length) {\n        slotByMonth[slot.month] = slot;\n      }\n    }\n\n    // == Real captures: only 2-card or 4-card captures are valid. ==\n    // If the table has 1 card, your hand card forms a pair.\n    // If the table has 3 cards, your hand card completes all four cards.\n    // If the table has 2 cards, your hand card creates a 3-stack and is treated\n    // as a special-risk move, not as a capture.\n    const captures = [];\n    for (const pc of playerCards) {\n      const slot = slotByMonth[pc.month];\n      if (!isCapturableStack(slot)) continue;\n\n      const captureIds = [...slot.ids, pc.id];\n      const ownGain = marginalBundleValue(captureIds, playerCaptured);\n      const denyGain = marginalBundleValue(captureIds, opponentCaptured);\n\n      const exposedNeopets = slot.ids.some(id => NEOPETS_CARDS.has(id));\n      const exposedFlying = slot.ids.some(id => FLYING_PETPETS.has(id));\n      const exposedDoubleWorld = slot.ids.some(id => DOUBLE_WORLD.has(id));\n\n      const stackSizeBonus = slot.ids.length === 3 ? 50 : 22;\n      const ownStrategy = bundleProgressValue(captureIds, playerCaptured, false);\n      const denialStrategy = bundleProgressValue(captureIds, opponentCaptured, true);\n\n      // JN Strategy: exposed Neopets cards should be taken quickly; protected\n      // Neopets cards in hand should not be spent casually when no key card is\n      // exposed on the table.\n      const keyCardBonus = (exposedNeopets ? 135 : 0)\n                         + (exposedFlying ? 35 : 0)\n                         + (exposedDoubleWorld ? 28 : 0);\n      const protectedNeopetsPenalty = hasProtectedNeopetsPairInHand(pc, playerCards) && !exposedNeopets\n        ? 55\n        : 0;\n\n      const tags = captureStrategyTags(captureIds, pc, slot, playerCards, playerCaptured, opponentCaptured);\n\n      const score = ownGain * selfMult\n                  + denyGain * denialMult\n                  + ownStrategy * selfMult\n                  + denialStrategy * denialMult\n                  + stackSizeBonus\n                  + keyCardBonus\n                  - protectedNeopetsPenalty;\n\n      captures.push({\n        handCard: pc,\n        slot,\n        score,\n        reason: `capture-${slot.ids.length + 1} ${pc.id} -> table_${slot.pos} [${shortCardList(slot.ids)}]${tags.length ? ' \u00b7 ' + tags.join(', ') : ''}`,\n      });\n    }\n\n    if (captures.length > 0) {\n      captures.sort((a, b) => b.score - a.score);\n      return {\n        handCard: captures[0].handCard,\n        tableSlot: captures[0].slot,\n        reason: `${captures[0].reason} \u00b7 score ${Math.round(captures[0].score)}`,\n      };\n    }\n\n    // == No immediate capture: choose the least harmful play. ==\n    const candidates = [];\n\n    for (const pc of playerCards) {\n      const existingSlot = slotByMonth[pc.month];\n      const ownLoss = marginalValue(pc.id, playerCaptured) * selfMult;\n      const oppValue = marginalValue(pc.id, opponentCaptured) * denialMult;\n      const preservationPenalty = handPreservationValue(pc, playerCards, playerCaptured);\n      const exposureRisk = opponentExposureRisk(pc.id, opponentCaptured) * denialMult;\n      const protectedTag = hasProtectedNeopetsPairInHand(pc, playerCards) ? ' \u00b7 preserva par Neopets' : '';\n\n      if (!existingSlot) {\n        const penalty = ownLoss + preservationPenalty + exposureRisk;\n        candidates.push({\n          handCard: pc,\n          tableSlot: null,\n          netCost: penalty,\n          reason: `empty-dump ${pc.id}${protectedTag} \u00b7 cost ${Math.round(penalty)}`,\n        });\n        continue;\n      }\n\n      if (isSpecialRiskStack(existingSlot)) {\n        const stackValueForOpponent = marginalBundleValue([...existingSlot.ids, pc.id], opponentCaptured);\n        const newThreeStackKeyRisk = bundleProgressValue([...existingSlot.ids, pc.id], opponentCaptured, true);\n        const penalty = ownLoss\n                      + preservationPenalty\n                      + exposureRisk\n                      + oppValue\n                      + stackValueForOpponent * denialMult * 1.75\n                      + newThreeStackKeyRisk * denialMult\n                      + 190;\n\n        candidates.push({\n          handCard: pc,\n          tableSlot: existingSlot,\n          netCost: penalty,\n          reason: `special-risk-3stack ${pc.id} -> table_${existingSlot.pos} [${shortCardList(existingSlot.ids)}]${protectedTag} \u00b7 cost ${Math.round(penalty)}`,\n        });\n        continue;\n      }\n\n      if (isAbnormalStack(existingSlot)) {\n        candidates.push({\n          handCard: pc,\n          tableSlot: existingSlot,\n          netCost: 50000,\n          reason: `abnormal-stack-block ${pc.id} -> table_${existingSlot.pos}`,\n        });\n        continue;\n      }\n\n      // This branch should rarely be reached because 1/3 stacks are capture candidates.\n      // Keep a guarded fallback to avoid returning nothing if the state mutates mid-read.\n      candidates.push({\n        handCard: pc,\n        tableSlot: existingSlot,\n        netCost: ownLoss + preservationPenalty + exposureRisk + 900,\n        reason: `guarded-existing-stack ${pc.id} -> table_${existingSlot.pos}${protectedTag}`, \n      });\n    }\n\n    candidates.sort((a, b) => a.netCost - b.netCost);\n    const best = candidates[0];\n\n    return {\n      handCard: best?.handCard,\n      tableSlot: best?.tableSlot || null,\n      reason: best?.reason || 'no candidate',\n    };\n  }\n\n  // == Click routing ==\n  function getProcessClick() {\n    const uw = getUnsafeWindow();\n    try {\n      if (typeof uw.process_click === 'function') return uw.process_click.bind(uw);\n    } catch {}\n    if (typeof window.process_click === 'function') return window.process_click.bind(window);\n    return null;\n  }\n\n  function clickEl(el) {\n    if (!el) return false;\n    const pc = getProcessClick();\n    if (pc) {\n      pc(el);\n      return true;\n    }\n    el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n    return true;\n  }\n\n  function forceSafeGameSpeed() {\n    if (!CFG.forceMediumSpeed) return false;\n\n    let changed = false;\n    const uw = getUnsafeWindow();\n    try {\n      if (typeof uw.set_game_speed === 'function') {\n        uw.set_game_speed('med');\n        changed = true;\n      }\n    } catch {}\n\n    const selectors = [\n      '#med',\n      '#medium',\n      'input[type=\"radio\"][value=\"med\"]',\n      'input[type=\"radio\"][value=\"medium\"]',\n      'input[type=\"radio\"][name*=\"speed\" i][value*=\"med\" i]',\n      'input[type=\"radio\"][name*=\"speed\" i][value*=\"medium\" i]'\n    ];\n\n    let speedInput = null;\n    for (const selector of selectors) {\n      try {\n        speedInput = document.querySelector(selector);\n      } catch {\n        speedInput = null;\n      }\n      if (speedInput) break;\n    }\n\n    if (!speedInput) {\n      try {\n        speedInput = [...document.querySelectorAll('input[type=\"radio\"]')]\n          .find(input => /med|medium/i.test(String(input.id || input.name || input.value || input.title || input.labels?.[0]?.textContent || '')))\n          || [...document.querySelectorAll('input[type=\"radio\"]')]\n            .find(input => /fast/i.test(String(input.id || input.name || input.value || input.title || input.labels?.[0]?.textContent || '')));\n      } catch {\n        speedInput = null;\n      }\n    }\n\n    if (speedInput && !speedInput.checked) {\n      try { speedInput.checked = true; changed = true; } catch {}\n      try { speedInput.dispatchEvent(new Event('change', { bubbles: true })); } catch {}\n      try { speedInput.click(); } catch {}\n    }\n\n    try {\n      const speedSelect = [...document.querySelectorAll('select')]\n        .find(select => /speed|veloc/i.test(String(select.id || select.name || select.getAttribute('aria-label') || '')));\n      if (speedSelect) {\n        const option = [...speedSelect.options].find(opt => /med|medium/i.test(String(opt.value || opt.textContent || '')))\n          || [...speedSelect.options].find(opt => /fast/i.test(String(opt.value || opt.textContent || '')));\n        if (option && speedSelect.value !== option.value) {\n          speedSelect.value = option.value;\n          speedSelect.dispatchEvent(new Event('change', { bubbles: true }));\n          changed = true;\n        }\n      }\n    } catch {}\n\n    if (changed) {\n      logUI('Game speed ajustado para Medium.');\n    }\n\n    return changed;\n  }\n\n  // == Automation loop ==\n  const sleep = ms => new Promise(r => setTimeout(r, ms));\n  let busy = false;\n  let replayTriggered = false;\n\n  async function doPlay() {\n    if (!CFG.enabled || busy) return;\n    if (gameBusy()) {\n      renderUI('Processando jogada nativa...');\n      return;\n    }\n\n    busy = true;\n    try {\n      renderUI('Preparando jogada...');\n      await sleep(CFG.delayBeforePlay);\n\n      if (!CFG.enabled) return;\n      if (!isTurn()) {\n        renderUI('Aguardando turno...');\n        return;\n      }\n      if (gameBusy()) {\n        renderUI('Processando jogada nativa...');\n        return;\n      }\n\n      const playerCards = getPlayerCards();\n      const tableSlots = getTableSlots();\n\n      if (playerCards.length === 0) {\n        renderUI('Sem cartas detectadas.');\n        logUI('M\u00e3o vazia ou DOM ainda carregando.');\n        return;\n      }\n\n      const playerCaptured = getPlayerCaptured();\n      const opponentCaptured = getOpponentCaptured();\n      const { playerScore, opponentScore } = getScores();\n\n      const play = chooseBestPlay(\n        playerCards,\n        tableSlots,\n        playerCaptured,\n        opponentCaptured,\n        playerScore,\n        opponentScore\n      );\n\n      lastDecisionText = play?.reason || '';\n      publishState({ lastDecision: lastDecisionText });\n\n      if (!play?.handCard?.div) {\n        renderUI('Sem jogada v\u00e1lida.');\n        logUI('Nenhuma carta jog\u00e1vel detectada.');\n        return;\n      }\n\n      renderUI(`Selecionando ${play.handCard.id}...`);\n      for (let i = 0; i < CFG.selectionRetries; i++) {\n        if (!CFG.enabled || !isTurn() || gameBusy()) return;\n        clickEl(play.handCard.div);\n        await sleep(CFG.selectionRetryMs);\n        if (isHandCardSelected(play.handCard.div)) break;\n      }\n\n      if (!isHandCardSelected(play.handCard.div)) {\n        renderUI('Sele\u00e7\u00e3o falhou.');\n        logUI(`Abortado: ${play.handCard.id} n\u00e3o ficou selecionada.`);\n        return;\n      }\n\n      await sleep(CFG.delayHandToTable);\n\n      if (!CFG.enabled || !isTurn() || gameBusy()) return;\n\n      if (play.tableSlot?.div) {\n        clickEl(play.tableSlot.div);\n        renderUI('Jogada enviada.');\n        lastMoveText = `${play.handCard.id} -> table_${play.tableSlot.pos}`;\n        lastReasonText = play.reason || '';\n        logUI(`${lastMoveText}. ${lastReasonText}`);\n      } else {\n        const empty = getEmptyTableSlot();\n        if (!empty) {\n          renderUI('Sem espa\u00e7o vazio.');\n          logUI(`Abortado: sem espa\u00e7o vazio para ${play.handCard.id}.`);\n          return;\n        }\n        clickEl(empty);\n        renderUI('Descarte enviado.');\n        lastMoveText = `${play.handCard.id} -> empty slot`;\n        lastReasonText = play.reason || '';\n        logUI(`${lastMoveText}. ${lastReasonText}`);\n      }\n    } catch (err) {\n      renderUI('Erro no autoplayer.');\n      logUI(`Erro: ${err?.message || err}`);\n      nccGodoriNoop.error('[Godori NCC Integration] doPlay error:', err);\n    } finally {\n      busy = false;\n      publishState();\n    }\n  }\n\n  async function handlePlayAgain(linkElement) {\n    if (replayTriggered || !CFG.enabled) return;\n\n    const stats = readAvatarStats();\n    renderAvatarProgress();\n    if (enforceAvatarStop(stats)) return;\n\n    if (!CFG.autoPlayAgain) {\n      renderUI('Play Again detectado.');\n      lastMoveText = 'Play Again detectado';\n      lastReasonText = 'Auto Play Again OFF';\n      logUI(`Auto Play Again OFF \u00b7 ${stats.found ? `${stats.handsWon}/${stats.targetHands} hands` : 'stats indispon\u00edveis'}.`);\n      return;\n    }\n\n    replayTriggered = true;\n    renderUI('Auto Play Again...');\n    lastMoveText = 'Auto Play Again';\n    lastReasonText = 'rein\u00edcio controlado pelo Avatar Helper';\n    logUI(`Reiniciando em ${Math.round(CFG.replayDelayMs / 1000)}s.`);\n    await sleep(CFG.replayDelayMs);\n    replayTriggered = false;\n\n    if (!CFG.enabled) return;\n    if (linkElement?.href) window.location.href = linkElement.href;\n    else window.location.href = '/games/godori/godori.phtml';\n  }\n\n\n  // == Etapa 6: Continuity / Start Page Recovery ==\n  function isBoardReady() {\n    return !!(document.getElementById('player_hand') && document.getElementById('game_status'));\n  }\n\n  function isGodoriPage() {\n    return /\\/games\\/godori\\//i.test(location.pathname || '');\n  }\n\n  function isLikelyStartPage() {\n    if (!isGodoriPage() || isBoardReady()) return false;\n    const text = (document.body?.textContent || '').toLowerCase();\n    return text.includes('play godori')\n      || text.includes('godori')\n      || /index\\.phtml$/i.test(location.pathname || '');\n  }\n\n  function elementLabel(el) {\n    if (!el) return '';\n    return String(el.textContent || el.value || el.alt || el.title || el.getAttribute?.('aria-label') || '').replace(/\\s+/g, ' ').trim();\n  }\n\n  function safeHref(el) {\n    try { return el.href || el.getAttribute('href') || ''; } catch { return ''; }\n  }\n\n  function isBadGodoriNavigation(el) {\n    const label = elementLabel(el).toLowerCase();\n    const href = safeHref(el).toLowerCase();\n    return label.includes('hall of fame')\n      || label.includes('back to')\n      || label.includes('games room')\n      || label.includes('shenkuu')\n      || href.includes('hall_of_fame')\n      || href.includes('arcade.phtml')\n      || href.includes('/shenkuu/');\n  }\n\n  function findGodoriStartControl() {\n    const exactControls = [...document.querySelectorAll('a[href], input[type=\"submit\"], input[type=\"button\"], button')]\n      .filter(el => !isBadGodoriNavigation(el))\n      .filter(el => /^play\\s*godori$/i.test(elementLabel(el)) || /play\\s*godori/i.test(elementLabel(el)));\n    if (exactControls.length) return { type: 'click', el: exactControls[0] };\n\n    const explicitInputs = [...document.querySelectorAll('input[type=\"submit\"], input[type=\"button\"], button')]\n      .filter(el => /play\\s*godori|start|continue/i.test(elementLabel(el)) && !isBadGodoriNavigation(el));\n    if (explicitInputs.length) return { type: 'click', el: explicitInputs[0] };\n\n    const explicitLinks = [...document.querySelectorAll('a[href]')]\n      .filter(el => {\n        const label = elementLabel(el);\n        const href = safeHref(el);\n        if (isBadGodoriNavigation(el)) return false;\n        return /play\\s*godori|start|continue/i.test(label)\n          || /godori\\.phtml|process_move\\.phtml/i.test(href);\n      });\n    if (explicitLinks.length) return { type: 'click', el: explicitLinks[0] };\n\n    const forms = [...document.querySelectorAll('form')]\n      .filter(form => {\n        const action = String(form.getAttribute('action') || form.action || '').toLowerCase();\n        const txt = elementLabel(form).toLowerCase();\n        return action.includes('godori')\n          || action.includes('process_move.phtml')\n          || txt.includes('play godori');\n      });\n    if (forms.length) {\n      const submit = forms[0].querySelector('input[type=\"submit\"], button[type=\"submit\"], button, input[type=\"button\"]');\n      if (submit) return { type: 'click', el: submit };\n      return { type: 'submit', el: forms[0] };\n    }\n\n    return null;\n  }\n\n  function getSafeGodoriStartHref(el) {\n    const href = safeHref(el);\n    if (!href || /^javascript:/i.test(href)) return '';\n    try {\n      const url = new URL(href, location.href);\n      if (!/\\/games\\/godori\\//i.test(url.pathname || '') && !/godori\\.phtml/i.test(url.pathname || '')) return '';\n      if (/hall_of_fame|arcade|shenkuu/i.test(url.href)) return '';\n      return url.href;\n    } catch {\n      return '';\n    }\n  }\n\n  function activateGodoriStartControl(control) {\n    if (!control?.el) return false;\n\n    forceSafeGameSpeed();\n\n    try {\n      if (control.type === 'submit') {\n        if (typeof control.el.requestSubmit === 'function') control.el.requestSubmit();\n        else if (typeof control.el.submit === 'function') control.el.submit();\n        else return false;\n      } else {\n        const form = control.el.closest?.('form');\n        try { control.el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true })); } catch {}\n        try { control.el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true })); } catch {}\n        control.el.click();\n\n        const href = getSafeGodoriStartHref(control.el);\n        if (href) {\n          setTimeout(() => {\n            try {\n              if (!isBoardReady() && isLikelyStartPage() && CFG.enabled) {\n                window.location.href = href;\n              }\n            } catch {}\n          }, 900);\n        } else if (form) {\n          setTimeout(() => {\n            try {\n              if (!isBoardReady() && isLikelyStartPage() && CFG.enabled) {\n                if (typeof form.requestSubmit === 'function') form.requestSubmit();\n                else if (typeof form.submit === 'function') form.submit();\n              }\n            } catch {}\n          }, 900);\n        }\n      }\n      return true;\n    } catch {\n      return false;\n    }\n  }\n\n  async function tryStartFromStartPage(reason = 'recovery') {\n    if (!CFG.enabled) return false;\n    if (!isLikelyStartPage()) return false;\n    if (startPageClickPending) return false;\n\n    const now = Date.now();\n    if (now - lastStartPageActionAt < CFG.startPageClickCooldownMs) return false;\n\n    forceSafeGameSpeed();\n\n    const control = findGodoriStartControl();\n    if (!control?.el) {\n      renderUI('Tela inicial: Play n\u00e3o encontrado.');\n      logUI('Tela inicial detectada, mas n\u00e3o achei bot\u00e3o Play Godori.');\n      return false;\n    }\n\n    startPageClickPending = true;\n    lastStartPageActionAt = now;\n    renderUI('Tela inicial detectada. Preparando Play...');\n    lastMoveText = 'Start Page Auto Play';\n    lastReasonText = reason;\n    logUI(`Play Godori em ${Math.round(CFG.startPageDelayMs / 1000)}s.`);\n    publishState({ startPageDetected: true, startPageReason: reason });\n\n    await sleep(CFG.startPageDelayMs);\n\n    try {\n      if (!CFG.enabled || isBoardReady()) return false;\n\n      const activated = activateGodoriStartControl(control);\n      if (!activated) {\n        logUI('Falha ao acionar Play Godori.');\n        return false;\n      }\n      logUI('Bot\u00e3o Play Godori acionado.');\n      publishState({ startPageAction: 'clickedPlay' });\n      return true;\n    } catch (err) {\n      logUI(`Falha ao apertar Play: ${err?.message || err}`);\n      return false;\n    } finally {\n      setTimeout(() => { startPageClickPending = false; }, 2500);\n    }\n  }\n\n  function startContinuityWatcher() {\n    if (continuityWatcherStarted) return;\n    continuityWatcherStarted = true;\n\n    setInterval(() => {\n      try {\n        installSmartAlertHandler();\n\n        if (isBoardReady()) {\n          if (!watcherStarted) startWatcher();\n          if (CFG.enabled) checkGame();\n          return;\n        }\n\n        if (CFG.enabled) {\n          renderAvatarProgress();\n          if (isLikelyStartPage()) {\n            tryStartFromStartPage('continuity watcher');\n          } else {\n            renderUI('Aguardando tabuleiro...');\n          }\n        }\n      } catch (err) {\n        if (CFG.debug) nccGodoriNoop.warn('[Godori Continuity] watcher error:', err);\n      }\n    }, CFG.recoveryPollInterval);\n  }\n\n  // == Watcher ==\n  function checkGame() {\n    const statusEl = document.getElementById('game_status');\n    if (!statusEl) {\n      if (CFG.enabled && isLikelyStartPage()) tryStartFromStartPage('checkGame no status');\n      return;\n    }\n\n    const status = getStatus();\n    const lower = status.toLowerCase();\n\n    if (!CFG.enabled) {\n      renderUI('Desligado.');\n      return;\n    }\n\n    const stats = readAvatarStats();\n    renderAvatarProgress();\n    if (enforceAvatarStop(stats)) return;\n\n    if (gameBusy() || busy) {\n      renderUI('Processando...');\n      return;\n    }\n\n    if (lower.includes('play again')) {\n      handlePlayAgain(statusEl.querySelector('a'));\n      return;\n    }\n\n    if (isTurn()) {\n      doPlay();\n      return;\n    }\n\n    renderUI(status ? `Aguardando: ${status.slice(0, 38)}` : 'Aguardando status...');\n  }\n\n  function startWatcher() {\n    const statusEl = document.getElementById('game_status');\n    if (!statusEl) return false;\n    if (watcherStarted) return true;\n\n    new MutationObserver(checkGame).observe(statusEl, {\n      childList: true,\n      characterData: true,\n      subtree: true,\n    });\n\n    setInterval(checkGame, CFG.pollInterval);\n    setTimeout(checkGame, 800);\n    watcherStarted = true;\n    return true;\n  }\n\n  function publishState(extra) {\n    try {\n      const avatarStats = readAvatarStats();\n      const state = {\n        version: VERSION,\n        moduleId: MODULE_ID,\n        moduleName: MODULE_NAME,\n        integration: {\n          bridgeReady: bridgeInstalled,\n          stateEvent: NCC_STATE_EVENT,\n          commandEvent: NCC_COMMAND_EVENT,\n          apiName: 'NCC_GODORI_API',\n        },\n        enabled: CFG.enabled,\n        autoPlayAgain: CFG.autoPlayAgain,\n        autoStopAtTarget: CFG.autoStopAtTarget,\n        avatarTargetHands: CFG.avatarTargetHands,\n        avatarStats,\n        handsWon: avatarStats.handsWon,\n        remainingHands: avatarStats.remainingHands,\n        avatarProgressPct: avatarStats.progressPct,\n        status: getStatus(),\n        gameBusy: gameBusy(),\n        internalBusy: busy,\n        replayTriggered,\n        watcherStarted,\n        continuityWatcherStarted,\n        startPageClickPending,\n        lastSuppressedAlertText,\n        playerScore: parseScoreEl('user_score'),\n        opponentScore: parseScoreEl('comp_score'),\n        lastLog: lastLogText,\n        lastMove: lastMoveText,\n        lastReason: lastReasonText,\n        lastDecision: lastDecisionText,\n        lastCommand: lastCommandText,\n        logs: logBuffer.slice(),\n        controls: {\n          canEnable: true,\n          canDisable: true,\n          canToggle: true,\n          canSetAutoPlayAgain: true,\n          canSetAutoStopAtTarget: true,\n          canPlayOnce: true,\n          canForceMediumSpeed: true,\n          canStartFromStartPage: true,\n        },\n        selectors: {\n          root: '#ncc-godori-safe-core',\n          gameStatus: '#game_status',\n          statsTable: 'table.g_stats_table',\n        },\n        updatedAt: Date.now(),\n        ...(extra || {}),\n      };\n\n      const uw = getUnsafeWindow();\n      uw.NCC_GODORI_STATE = state;\n      window.NCC_GODORI_STATE = state;\n\n      try {\n        window.dispatchEvent(new CustomEvent(NCC_STATE_EVENT, { detail: state }));\n      } catch {}\n      try {\n        if (uw && uw !== window && typeof uw.dispatchEvent === 'function') {\n          uw.dispatchEvent(new CustomEvent(NCC_STATE_EVENT, { detail: state }));\n        }\n      } catch {}\n\n      return state;\n    } catch (err) {\n      if (CFG.debug) nccGodoriNoop.warn('[Godori NCC Integration] publishState failed:', err);\n      return null;\n    }\n  }\n\n  function init() {\n    if (!document.body) return false;\n\n    installSmartAlertHandler();\n    installUI();\n    installBridge();\n    startContinuityWatcher();\n\n    if (!document.getElementById('player_hand')) {\n      if (CFG.enabled && isLikelyStartPage()) {\n        renderUI('Tela inicial detectada.');\n        tryStartFromStartPage('init');\n      } else {\n        renderUI(isLikelyStartPage() ? 'Tela inicial detectada.' : 'Aguardando tabuleiro...');\n      }\n      publishState({ boardReady: false, startPage: isLikelyStartPage() });\n      return false;\n    }\n\n    if (CFG.enabled) forceSafeGameSpeed();\n\n    const ok = startWatcher();\n    if (ok) {\n      renderUI(CFG.enabled ? 'Ligado. Aguardando turno.' : 'Desligado.');\n      logUI('Watcher iniciado \u00b7 Continuity + NCC bridge ativos.');\n      publishState();\n    }\n    return ok;\n  }\n\n  function boot() {\n    let tries = 0;\n    const timer = setInterval(() => {\n      tries++;\n      if (init() || tries > 40) clearInterval(timer);\n    }, 250);\n  }\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', boot);\n  } else {\n    boot();\n  }\n})();";

    const TRAINING_PIRATE_PATH = "/pirates/academy.phtml";
    const TRAINING_PIRATE_URL = "https://www.neopets.com/pirates/academy.phtml?type=status";
    const TRAINING_MYSTERY_PATH = "/island/training.phtml";
    const TRAINING_MYSTERY_URL = "https://www.neopets.com/island/training.phtml?type=status";
    const TRAINING_NINJA_PATH = "/island/fight_training.phtml";
    const TRAINING_NINJA_URL = "https://www.neopets.com/island/fight_training.phtml?type=status";
    const BATTLEDOME_HOME_PATH = "/dome/";
    const BATTLEDOME_FIGHT_PATH = "/dome/fight.phtml";
    const BATTLEDOME_ARENA_PATH = "/dome/arena.phtml";
    const BATTLEDOME_RECORD_PATH = "/dome/record.phtml";
    const BATTLEDOME_EQUIPMENT_PATH = "/dome/neopets.phtml";
    const BATTLEDOME_HOME_URL = "https://www.neopets.com/dome/";
    const BATTLEDOME_FIGHT_URL = "https://www.neopets.com/dome/fight.phtml";
    const BATTLEDOME_ARENA_URL = "https://www.neopets.com/dome/arena.phtml";
    const BATTLEDOME_RECORD_URL = "https://www.neopets.com/dome/record.phtml";
    const BATTLEDOME_EQUIPMENT_URL = "https://www.neopets.com/dome/neopets.phtml";
    const NCC_AAA_ICON_URL = "https://images.neopets.com/neoboards/avatars/avinroo.gif";
    const CELLBLOCK_ICON_URL = "https://images.neopets.com/new_shopkeepers/t_222.gif";
    const NEOQUESTS_ICON_URL = "https://images.neopets.com/nq2/x/a2_5596a_r.gif";
    const DICEAROO_ICON_URL = "https://images.neopets.com/neoboards/avatars/luckyblumaroo.gif";
    const KIKO_POP_ICON_URL = "https://images.neopets.com/neoboards/avatars/kikopop.gif";
    const TRAINING_ICON_URL = "https://images.neopets.com/items/codestone5.gif";

    const TROPHY_TRACKER_PRIZES_PATH = "/prizes.phtml";
    const TROPHY_TRACKER_PRIZES_URL = "https://www.neopets.com/prizes.phtml";
    const TROPHY_TRACKER_SOURCE_NAME = "Karla's Trophy Tracker";
    const TROPHY_TRACKER_SOURCE_VERSION = "0.0.4";
    const TROPHY_TRACKER_SOURCE_LICENSE = "GPL-3.0";
    const TROPHY_TRACKER_SOURCE_AUTHOR = "Karla";
    const TROPHY_TRACKER_SOURCE_HOMEPAGE = "https://neopointskarla.com";
    const TROPHY_TRACKER_ENTRY_URL = `${ENTRY_POINT}?nccaaa_view=profile`;
    const TROPHY_TRACKER_STATE_SCHEMA = "ncc-aaa-trophy-tracker-state-v1";
    const TROPHY_TRACKER_REPORT_SCHEMA = "ncc-aaa-trophy-tracker-report-v1";
    const TROPHY_TRACKER_STORAGE_KEY_PREFIX = "ncc.aaa.trophyTracker.state.v1";
    const TROPHY_TRACKER_REPORT_KEY = "ncc.aaa.trophyTracker.report.v1";

    // Trophy database adapted from Karla's Trophy Tracker v0.0.4 (GPL3), integrated as a local/read-only NCC AAA view.
    const flashgameHighScoreTrophies = [
        {
            "name": "200m Peanut Dash",
            "images": [
                "https://images.neopets.com/trophies/987_3.gif",
                "https://images.neopets.com/trophies/987_2.gif",
                "https://images.neopets.com/trophies/987_1.gif"
            ]
        },
        {
            "name": "AAA's Revenge",
            "images": [
                "https://images.neopets.com/trophies/1375_3.gif",
                "https://images.neopets.com/trophies/1375_2.gif",
                "https://images.neopets.com/trophies/1375_1.gif"
            ]
        },
        {
            "name": "Advert Attack",
            "images": [
                "https://images.neopets.com/trophies/204_3.gif",
                "https://images.neopets.com/trophies/204_2.gif",
                "https://images.neopets.com/trophies/204_1.gif"
            ]
        },
        {
            "name": "Assignment 53",
            "images": [
                "https://images.neopets.com/trophies/1347_3.gif",
                "https://images.neopets.com/trophies/1347_2.gif",
                "https://images.neopets.com/trophies/1347_1.gif"
            ]
        },
        {
            "name": "Attack of the Gummy Dice",
            "images": [
                "https://images.neopets.com/trophies/685_3.gif",
                "https://images.neopets.com/trophies/685_2.gif",
                "https://images.neopets.com/trophies/685_1.gif"
            ]
        },
        {
            "name": "Attack of the Marblemen",
            "images": [
                "https://images.neopets.com/trophies/201_3.gif",
                "https://images.neopets.com/trophies/201_2.gif",
                "https://images.neopets.com/trophies/201_1.gif"
            ]
        },
        {
            "name": "Attack of the Revenge",
            "images": [
                "https://images.neopets.com/trophies/527_3.gif",
                "https://images.neopets.com/trophies/527_2.gif",
                "https://images.neopets.com/trophies/527_1.gif"
            ]
        },
        {
            "name": "Attack of the Slorgs",
            "images": [
                "https://images.neopets.com/trophies/386_3.gif",
                "https://images.neopets.com/trophies/386_2.gif",
                "https://images.neopets.com/trophies/386_1.gif"
            ]
        },
        {
            "name": "Barf Boat",
            "images": [
                "https://images.neopets.com/trophies/1026_3.gif",
                "https://images.neopets.com/trophies/1026_2.gif",
                "https://images.neopets.com/trophies/1026_1.gif"
            ]
        },
        {
            "name": "Berry Bash",
            "images": [
                "https://images.neopets.com/trophies/968_3.gif",
                "https://images.neopets.com/trophies/968_2.gif",
                "https://images.neopets.com/trophies/968_1.gif"
            ]
        },
        {
            "name": "Biscuit Brigade: Hagan's Last Stand",
            "images": [
                "https://images.neopets.com/trophies/941_3.gif",
                "https://images.neopets.com/trophies/941_2.gif",
                "https://images.neopets.com/trophies/941_1.gif"
            ]
        },
        {
            "name": "Black Pawkeet Slots",
            "images": [
                "https://images.neopets.com/trophies/1099_3.gif",
                "https://images.neopets.com/trophies/1099_2.gif",
                "https://images.neopets.com/trophies/1099_1.gif"
            ]
        },
        {
            "name": "Bouncy Supreme",
            "images": [
                "https://images.neopets.com/trophies/532_3.gif",
                "https://images.neopets.com/trophies/532_2.gif",
                "https://images.neopets.com/trophies/532_1.gif"
            ]
        },
        {
            "name": "Brucey B Slots",
            "images": [
                "https://images.neopets.com/trophies/1121_3.gif",
                "https://images.neopets.com/trophies/1121_2.gif",
                "https://images.neopets.com/trophies/1121_1.gif"
            ]
        },
        {
            "name": "Bruno's Backwoods Breakaway",
            "images": [
                "https://images.neopets.com/trophies/734_3.gif",
                "https://images.neopets.com/trophies/734_2.gif",
                "https://images.neopets.com/trophies/734_1.gif"
            ]
        },
        {
            "name": "Bumble Beams",
            "images": [
                "https://images.neopets.com/trophies/799_3.gif",
                "https://images.neopets.com/trophies/799_2.gif",
                "https://images.neopets.com/trophies/799_1.gif"
            ]
        },
        {
            "name": "Carnival of Terror",
            "images": [
                "https://images.neopets.com/trophies/902_3.gif",
                "https://images.neopets.com/trophies/902_2.gif",
                "https://images.neopets.com/trophies/902_1.gif"
            ]
        },
        {
            "name": "Castle Battles",
            "images": [
                "https://images.neopets.com/trophies/430_3.gif",
                "https://images.neopets.com/trophies/430_2.gif",
                "https://images.neopets.com/trophies/430_1.gif"
            ]
        },
        {
            "name": "Cave Glider",
            "images": [
                "https://images.neopets.com/trophies/1156_3.gif",
                "https://images.neopets.com/trophies/1156_2.gif",
                "https://images.neopets.com/trophies/1156_1.gif"
            ]
        },
        {
            "name": "Caves and Corridors: Mystery Island",
            "images": [
                "https://images.neopets.com/trophies/627_3.gif",
                "https://images.neopets.com/trophies/627_2.gif",
                "https://images.neopets.com/trophies/627_1.gif"
            ]
        },
        {
            "name": "Chariot Chase",
            "images": [
                "https://images.neopets.com/trophies/1148_3.gif",
                "https://images.neopets.com/trophies/1148_2.gif",
                "https://images.neopets.com/trophies/1148_1.gif"
            ]
        },
        {
            "name": "Cheeseroller",
            "images": [
                "https://images.neopets.com/trophies/155_3.gif",
                "https://images.neopets.com/trophies/155_2.gif",
                "https://images.neopets.com/trophies/155_1.gif"
            ]
        },
        {
            "name": "Chemistry for Beginners",
            "images": [
                "https://images.neopets.com/trophies/239_3.gif",
                "https://images.neopets.com/trophies/239_2.gif",
                "https://images.neopets.com/trophies/239_1.gif"
            ]
        },
        {
            "name": "Chia Bomber 2",
            "images": [
                "https://images.neopets.com/trophies/539_3.gif",
                "https://images.neopets.com/trophies/539_2.gif",
                "https://images.neopets.com/trophies/539_1.gif"
            ]
        },
        {
            "name": "Clockwork Codebreaker",
            "images": [
                "https://images.neopets.com/trophies/1173_3.gif",
                "https://images.neopets.com/trophies/1173_2.gif",
                "https://images.neopets.com/trophies/1173_1.gif"
            ]
        },
        {
            "name": "Cloud Raiders",
            "images": [
                "https://images.neopets.com/trophies/1149_3.gif",
                "https://images.neopets.com/trophies/1149_2.gif",
                "https://images.neopets.com/trophies/1149_1.gif"
            ]
        },
        {
            "name": "Coal War Tactics",
            "images": [
                "https://images.neopets.com/trophies/1370_3.gif",
                "https://images.neopets.com/trophies/1370_2.gif",
                "https://images.neopets.com/trophies/1370_1.gif"
            ]
        },
        {
            "name": "Cooty Wars",
            "images": [
                "https://images.neopets.com/trophies/796_3.gif",
                "https://images.neopets.com/trophies/796_2.gif",
                "https://images.neopets.com/trophies/796_1.gif"
            ]
        },
        {
            "name": "Crisis Courier",
            "images": [
                "https://images.neopets.com/trophies/773_3.gif",
                "https://images.neopets.com/trophies/773_2.gif",
                "https://images.neopets.com/trophies/773_1.gif"
            ]
        },
        {
            "name": "Dar-BLAT!!!",
            "images": [
                "https://images.neopets.com/trophies/895_3.gif",
                "https://images.neopets.com/trophies/895_2.gif",
                "https://images.neopets.com/trophies/895_1.gif"
            ]
        },
        {
            "name": "Darigan Dodgeball",
            "images": [
                "https://images.neopets.com/trophies/1139_3.gif",
                "https://images.neopets.com/trophies/1139_2.gif",
                "https://images.neopets.com/trophies/1139_1.gif"
            ]
        },
        {
            "name": "Deckball",
            "images": [
                "https://images.neopets.com/trophies/82_3.gif",
                "https://images.neopets.com/trophies/82_2.gif",
                "https://images.neopets.com/trophies/82_1.gif"
            ]
        },
        {
            "name": "Defender Trainer",
            "images": [
                "https://images.neopets.com/trophies/258_3.gif",
                "https://images.neopets.com/trophies/258_2.gif",
                "https://images.neopets.com/trophies/258_1.gif"
            ]
        },
        {
            "name": "Destruct-O-Match III",
            "images": [
                "https://images.neopets.com/trophies/999_3.gif",
                "https://images.neopets.com/trophies/999_2.gif",
                "https://images.neopets.com/trophies/999_1.gif"
            ]
        },
        {
            "name": "Dice Escape",
            "images": [
                "https://images.neopets.com/trophies/356_3.gif",
                "https://images.neopets.com/trophies/356_2.gif",
                "https://images.neopets.com/trophies/356_1.gif"
            ]
        },
        {
            "name": "Dice of Destiny",
            "images": [
                "https://images.neopets.com/trophies/1126_3.gif",
                "https://images.neopets.com/trophies/1126_2.gif",
                "https://images.neopets.com/trophies/1126_1.gif"
            ]
        },
        {
            "name": "Dice-A-Roo",
            "images": [
                "https://images.neopets.com/trophies/10_3.gif",
                "https://images.neopets.com/trophies/10_2.gif",
                "https://images.neopets.com/trophies/10_1.gif"
            ]
        },
        {
            "name": "Double or Nothing",
            "images": [
                "https://images.neopets.com/trophies/178_3.gif",
                "https://images.neopets.com/trophies/178_2.gif",
                "https://images.neopets.com/trophies/178_1.gif"
            ]
        },
        {
            "name": "Dubloon Disaster",
            "images": [
                "https://images.neopets.com/trophies/772_3.gif",
                "https://images.neopets.com/trophies/772_2.gif",
                "https://images.neopets.com/trophies/772_1.gif"
            ]
        },
        {
            "name": "Dueling Decks",
            "images": [
                "https://images.neopets.com/trophies/1182_3.gif",
                "https://images.neopets.com/trophies/1182_2.gif",
                "https://images.neopets.com/trophies/1182_1.gif"
            ]
        },
        {
            "name": "Dungeon Dash",
            "images": [
                "https://images.neopets.com/trophies/962_3.gif",
                "https://images.neopets.com/trophies/962_2.gif",
                "https://images.neopets.com/trophies/962_1.gif"
            ]
        },
        {
            "name": "Edna's Shadow",
            "images": [
                "https://images.neopets.com/trophies/821_3.gif",
                "https://images.neopets.com/trophies/821_2.gif",
                "https://images.neopets.com/trophies/821_1.gif"
            ]
        },
        {
            "name": "Escape from Meridell Castle",
            "images": [
                "https://images.neopets.com/trophies/197_3.gif",
                "https://images.neopets.com/trophies/197_2.gif",
                "https://images.neopets.com/trophies/197_1.gif"
            ]
        },
        {
            "name": "Escape to Kreludor",
            "images": [
                "https://images.neopets.com/trophies/400_3.gif",
                "https://images.neopets.com/trophies/400_2.gif",
                "https://images.neopets.com/trophies/400_1.gif"
            ]
        },
        {
            "name": "Evil Fuzzles from Beyond the Stars",
            "images": [
                "https://images.neopets.com/trophies/585_3.gif",
                "https://images.neopets.com/trophies/585_2.gif",
                "https://images.neopets.com/trophies/585_1.gif"
            ]
        },
        {
            "name": "Extreme Faerie Cloud Racers",
            "images": [
                "https://images.neopets.com/trophies/1155_3.gif",
                "https://images.neopets.com/trophies/1155_2.gif",
                "https://images.neopets.com/trophies/1155_1.gif"
            ]
        },
        {
            "name": "Extreme Herder",
            "images": [
                "https://images.neopets.com/trophies/149_3.gif",
                "https://images.neopets.com/trophies/149_2.gif",
                "https://images.neopets.com/trophies/149_1.gif"
            ]
        },
        {
            "name": "Extreme Herder 2",
            "images": [
                "https://images.neopets.com/trophies/1117_3.gif",
                "https://images.neopets.com/trophies/1117_2.gif",
                "https://images.neopets.com/trophies/1117_1.gif"
            ]
        },
        {
            "name": "Extreme Potato Counter",
            "images": [
                "https://images.neopets.com/trophies/226_3.gif",
                "https://images.neopets.com/trophies/226_2.gif",
                "https://images.neopets.com/trophies/226_1.gif"
            ]
        },
        {
            "name": "Eye of the Storm",
            "images": [
                "https://images.neopets.com/trophies/720_3.gif",
                "https://images.neopets.com/trophies/720_2.gif",
                "https://images.neopets.com/trophies/720_1.gif"
            ]
        },
        {
            "name": "Faerie Bubbles",
            "images": [
                "https://images.neopets.com/trophies/358_3.gif",
                "https://images.neopets.com/trophies/358_2.gif",
                "https://images.neopets.com/trophies/358_1.gif"
            ]
        },
        {
            "name": "Faerie Caves II - Fyora's Quest",
            "images": [
                "https://images.neopets.com/trophies/489_3.gif",
                "https://images.neopets.com/trophies/489_2.gif",
                "https://images.neopets.com/trophies/489_1.gif"
            ]
        },
        {
            "name": "Faerie Cloud Racers",
            "images": [
                "https://images.neopets.com/trophies/586_3.gif",
                "https://images.neopets.com/trophies/586_2.gif",
                "https://images.neopets.com/trophies/586_1.gif"
            ]
        },
        {
            "name": "Feed Florg",
            "images": [
                "https://images.neopets.com/trophies/645_3.gif",
                "https://images.neopets.com/trophies/645_2.gif",
                "https://images.neopets.com/trophies/645_1.gif"
            ]
        },
        {
            "name": "Fetch!",
            "images": [
                "https://images.neopets.com/trophies/97_3.gif",
                "https://images.neopets.com/trophies/97_2.gif",
                "https://images.neopets.com/trophies/97_1.gif"
            ]
        },
        {
            "name": "Flycatcher",
            "images": [
                "https://images.neopets.com/trophies/570_3.gif",
                "https://images.neopets.com/trophies/570_2.gif",
                "https://images.neopets.com/trophies/570_1.gif"
            ]
        },
        {
            "name": "Freaky Factory",
            "images": [
                "https://images.neopets.com/trophies/390_3.gif",
                "https://images.neopets.com/trophies/390_2.gif",
                "https://images.neopets.com/trophies/390_1.gif"
            ]
        },
        {
            "name": "Frumball",
            "images": [
                "https://images.neopets.com/trophies/313_3.gif",
                "https://images.neopets.com/trophies/313_2.gif",
                "https://images.neopets.com/trophies/313_1.gif"
            ]
        },
        {
            "name": "Gadgadsgame",
            "images": [
                "https://images.neopets.com/trophies/159_3.gif",
                "https://images.neopets.com/trophies/159_2.gif",
                "https://images.neopets.com/trophies/159_1.gif"
            ]
        },
        {
            "name": "Ghost Bopper",
            "images": [
                "https://images.neopets.com/trophies/614_3.gif",
                "https://images.neopets.com/trophies/614_2.gif",
                "https://images.neopets.com/trophies/614_1.gif"
            ]
        },
        {
            "name": "Goparokko",
            "images": [
                "https://images.neopets.com/trophies/887_3.gif",
                "https://images.neopets.com/trophies/887_2.gif",
                "https://images.neopets.com/trophies/887_1.gif"
            ]
        },
        {
            "name": "Gormball",
            "images": [
                "https://images.neopets.com/trophies/6_3.gif",
                "https://images.neopets.com/trophies/6_2.gif",
                "https://images.neopets.com/trophies/6_1.gif"
            ]
        },
        {
            "name": "Gourmet Club Bowls",
            "images": [
                "https://images.neopets.com/trophies/330_3.gif",
                "https://images.neopets.com/trophies/330_2.gif",
                "https://images.neopets.com/trophies/330_1.gif"
            ]
        },
        {
            "name": "Grand Theft Ummagine",
            "images": [
                "https://images.neopets.com/trophies/212_3.gif",
                "https://images.neopets.com/trophies/212_2.gif",
                "https://images.neopets.com/trophies/212_1.gif"
            ]
        },
        {
            "name": "Grarrl Keno",
            "images": [
                "https://images.neopets.com/trophies/48_3.gif",
                "https://images.neopets.com/trophies/48_2.gif",
                "https://images.neopets.com/trophies/48_1.gif"
            ]
        },
        {
            "name": "Gwyl's Great Escape",
            "images": [
                "https://images.neopets.com/trophies/668_3.gif",
                "https://images.neopets.com/trophies/668_2.gif",
                "https://images.neopets.com/trophies/668_1.gif"
            ]
        },
        {
            "name": "Hannah and the Ice Caves",
            "images": [
                "https://images.neopets.com/trophies/473_3.gif",
                "https://images.neopets.com/trophies/473_2.gif",
                "https://images.neopets.com/trophies/473_1.gif"
            ]
        },
        {
            "name": "Hannah and the Kreludor Caves",
            "images": [
                "https://images.neopets.com/trophies/1252_3.gif",
                "https://images.neopets.com/trophies/1252_2.gif",
                "https://images.neopets.com/trophies/1252_1.gif"
            ]
        },
        {
            "name": "Hannah and the Pirate Caves",
            "images": [
                "https://images.neopets.com/trophies/349_3.gif",
                "https://images.neopets.com/trophies/349_2.gif",
                "https://images.neopets.com/trophies/349_1.gif"
            ]
        },
        {
            "name": "Hannah and the Wardrobe of Adventure",
            "images": [
                "https://images.neopets.com/trophies/1229_3.gif",
                "https://images.neopets.com/trophies/1229_2.gif",
                "https://images.neopets.com/trophies/1229_1.gif"
            ]
        },
        {
            "name": "Hasee Bounce",
            "images": [
                "https://images.neopets.com/trophies/368_3.gif",
                "https://images.neopets.com/trophies/368_2.gif",
                "https://images.neopets.com/trophies/368_1.gif"
            ]
        },
        {
            "name": "Hot Dog Hero",
            "images": [
                "https://images.neopets.com/trophies/965_3.gif",
                "https://images.neopets.com/trophies/965_2.gif",
                "https://images.neopets.com/trophies/965_1.gif"
            ]
        },
        {
            "name": "Hubrid's Hero Heist",
            "images": [
                "https://images.neopets.com/trophies/314_3.gif",
                "https://images.neopets.com/trophies/314_2.gif",
                "https://images.neopets.com/trophies/314_1.gif"
            ]
        },
        {
            "name": "Hungry Skeith",
            "images": [
                "https://images.neopets.com/trophies/538_3.gif",
                "https://images.neopets.com/trophies/538_2.gif",
                "https://images.neopets.com/trophies/538_1.gif"
            ]
        },
        {
            "name": "Ice Cream Machine",
            "images": [
                "https://images.neopets.com/trophies/507_3.gif",
                "https://images.neopets.com/trophies/507_2.gif",
                "https://images.neopets.com/trophies/507_1.gif"
            ]
        },
        {
            "name": "Igloo Garage Sale - The Game",
            "images": [
                "https://images.neopets.com/trophies/676_3.gif",
                "https://images.neopets.com/trophies/676_2.gif",
                "https://images.neopets.com/trophies/676_1.gif"
            ]
        },
        {
            "name": "Illusens Glade",
            "images": [
                "https://images.neopets.com/trophies/160_3.gif",
                "https://images.neopets.com/trophies/160_2.gif",
                "https://images.neopets.com/trophies/160_1.gif"
            ]
        },
        {
            "name": "Imperial Exam",
            "images": [
                "https://images.neopets.com/trophies/656_3.gif",
                "https://images.neopets.com/trophies/656_2.gif",
                "https://images.neopets.com/trophies/656_1.gif"
            ]
        },
        {
            "name": "Invasion: Blastoids",
            "images": [
                "https://images.neopets.com/trophies/1330_3.gif",
                "https://images.neopets.com/trophies/1330_2.gif",
                "https://images.neopets.com/trophies/1330_1.gif"
            ]
        },
        {
            "name": "Island Chef Academy",
            "images": [
                "https://images.neopets.com/trophies/1205_3.gif",
                "https://images.neopets.com/trophies/1205_2.gif",
                "https://images.neopets.com/trophies/1205_1.gif"
            ]
        },
        {
            "name": "Itchy Invasion",
            "images": [
                "https://images.neopets.com/trophies/713_3.gif",
                "https://images.neopets.com/trophies/713_2.gif",
                "https://images.neopets.com/trophies/713_1.gif"
            ]
        },
        {
            "name": "Jelly Blobs of Doom",
            "images": [
                "https://images.neopets.com/trophies/359_3.gif",
                "https://images.neopets.com/trophies/359_2.gif",
                "https://images.neopets.com/trophies/359_1.gif"
            ]
        },
        {
            "name": "Jhudoras Bluff",
            "images": [
                "https://images.neopets.com/trophies/126_3.gif",
                "https://images.neopets.com/trophies/126_2.gif",
                "https://images.neopets.com/trophies/126_1.gif"
            ]
        },
        {
            "name": "Jolly Jugglers",
            "images": [
                "https://images.neopets.com/trophies/615_3.gif",
                "https://images.neopets.com/trophies/615_2.gif",
                "https://images.neopets.com/trophies/615_1.gif"
            ]
        },
        {
            "name": "Jubble Bubble",
            "images": [
                "https://images.neopets.com/trophies/619_3.gif",
                "https://images.neopets.com/trophies/619_2.gif",
                "https://images.neopets.com/trophies/619_1.gif"
            ]
        },
        {
            "name": "Jumpin' Gem Heist",
            "images": [
                "https://images.neopets.com/trophies/1191_3.gif",
                "https://images.neopets.com/trophies/1191_2.gif",
                "https://images.neopets.com/trophies/1191_1.gif"
            ]
        },
        {
            "name": "Jungle Raiders",
            "images": [
                "https://images.neopets.com/trophies/1064_3.gif",
                "https://images.neopets.com/trophies/1064_2.gif",
                "https://images.neopets.com/trophies/1064_1.gif"
            ]
        },
        {
            "name": "Kass Basher",
            "images": [
                "https://images.neopets.com/trophies/381_3.gif",
                "https://images.neopets.com/trophies/381_2.gif",
                "https://images.neopets.com/trophies/381_1.gif"
            ]
        },
        {
            "name": "Kiko Match II",
            "images": [
                "https://images.neopets.com/trophies/519_3.gif",
                "https://images.neopets.com/trophies/519_2.gif",
                "https://images.neopets.com/trophies/519_1.gif"
            ]
        },
        {
            "name": "Kiss the Mortog",
            "images": [
                "https://images.neopets.com/trophies/154_3.gif",
                "https://images.neopets.com/trophies/154_2.gif",
                "https://images.neopets.com/trophies/154_1.gif"
            ]
        },
        {
            "name": "Kookia",
            "images": [
                "https://images.neopets.com/trophies/1189_3.gif",
                "https://images.neopets.com/trophies/1189_2.gif",
                "https://images.neopets.com/trophies/1189_1.gif"
            ]
        },
        {
            "name": "Korbats Lab",
            "images": [
                "https://images.neopets.com/trophies/801_3.gif",
                "https://images.neopets.com/trophies/801_2.gif",
                "https://images.neopets.com/trophies/801_1.gif"
            ]
        },
        {
            "name": "Kou-Jong",
            "images": [
                "https://images.neopets.com/trophies/707_3.gif",
                "https://images.neopets.com/trophies/707_2.gif",
                "https://images.neopets.com/trophies/707_1.gif"
            ]
        },
        {
            "name": "Kreludan Mining Corp.",
            "images": [
                "https://images.neopets.com/trophies/404_3.gif",
                "https://images.neopets.com/trophies/404_2.gif",
                "https://images.neopets.com/trophies/404_1.gif"
            ]
        },
        {
            "name": "Legends of Pinball",
            "images": [
                "https://images.neopets.com/trophies/1118_3.gif",
                "https://images.neopets.com/trophies/1118_2.gif",
                "https://images.neopets.com/trophies/1118_1.gif"
            ]
        },
        {
            "name": "Let It Slide",
            "images": [
                "https://images.neopets.com/trophies/970_3.gif",
                "https://images.neopets.com/trophies/970_2.gif",
                "https://images.neopets.com/trophies/970_1.gif"
            ]
        },
        {
            "name": "Lost City Lanes",
            "images": [
                "https://images.neopets.com/trophies/1108_3.gif",
                "https://images.neopets.com/trophies/1108_2.gif",
                "https://images.neopets.com/trophies/1108_1.gif"
            ]
        },
        {
            "name": "Lost in Space Fungus",
            "images": [
                "https://images.neopets.com/trophies/774_3.gif",
                "https://images.neopets.com/trophies/774_2.gif",
                "https://images.neopets.com/trophies/774_1.gif"
            ]
        },
        {
            "name": "MAGAX: Destroyer II",
            "images": [
                "https://images.neopets.com/trophies/763_3.gif",
                "https://images.neopets.com/trophies/763_2.gif",
                "https://images.neopets.com/trophies/763_1.gif"
            ]
        },
        {
            "name": "Magma Blaster",
            "images": [
                "https://images.neopets.com/trophies/571_3.gif",
                "https://images.neopets.com/trophies/571_2.gif",
                "https://images.neopets.com/trophies/571_1.gif"
            ]
        },
        {
            "name": "Maths Nightmare",
            "images": [
                "https://images.neopets.com/trophies/885_3.gif",
                "https://images.neopets.com/trophies/885_2.gif",
                "https://images.neopets.com/trophies/885_1.gif"
            ]
        },
        {
            "name": "Meepit Juice Break",
            "images": [
                "https://images.neopets.com/trophies/379_3.gif",
                "https://images.neopets.com/trophies/379_2.gif",
                "https://images.neopets.com/trophies/379_1.gif"
            ]
        },
        {
            "name": "Meepit vs. Feepit",
            "images": [
                "https://images.neopets.com/trophies/540_3.gif",
                "https://images.neopets.com/trophies/540_2.gif",
                "https://images.neopets.com/trophies/540_1.gif"
            ]
        },
        {
            "name": "Meerca Chase II",
            "images": [
                "https://images.neopets.com/trophies/500_3.gif",
                "https://images.neopets.com/trophies/500_2.gif",
                "https://images.neopets.com/trophies/500_1.gif"
            ]
        },
        {
            "name": "Moon Rock Rampage",
            "images": [
                "https://images.neopets.com/trophies/444_3.gif",
                "https://images.neopets.com/trophies/444_2.gif",
                "https://images.neopets.com/trophies/444_1.gif"
            ]
        },
        {
            "name": "Mootix Drop",
            "images": [
                "https://images.neopets.com/trophies/396_3.gif",
                "https://images.neopets.com/trophies/396_2.gif",
                "https://images.neopets.com/trophies/396_1.gif"
            ]
        },
        {
            "name": "Mop 'n' Bop",
            "images": [
                "https://images.neopets.com/trophies/904_3.gif",
                "https://images.neopets.com/trophies/904_2.gif",
                "https://images.neopets.com/trophies/904_1.gif"
            ]
        },
        {
            "name": "Mutant Graveyard of Doom II",
            "images": [
                "https://images.neopets.com/trophies/1042_3.gif",
                "https://images.neopets.com/trophies/1042_2.gif",
                "https://images.neopets.com/trophies/1042_1.gif"
            ]
        },
        {
            "name": "Mynci Beach Volleyball",
            "images": [
                "https://images.neopets.com/trophies/315_3.gif",
                "https://images.neopets.com/trophies/315_2.gif",
                "https://images.neopets.com/trophies/315_1.gif"
            ]
        },
        {
            "name": "NeggSweeper",
            "images": [
                "https://images.neopets.com/trophies/54_3.gif",
                "https://images.neopets.com/trophies/54_2.gif",
                "https://images.neopets.com/trophies/54_1.gif"
            ]
        },
        {
            "name": "NeoQuest II Race",
            "images": [
                "https://images.neopets.com/trophies/373_3.gif",
                "https://images.neopets.com/trophies/373_2.gif",
                "https://images.neopets.com/trophies/373_1.gif"
            ]
        },
        {
            "name": "Neopian Battlefield Legends",
            "images": [
                "https://images.neopets.com/trophies/1221_3.gif",
                "https://images.neopets.com/trophies/1221_2.gif",
                "https://images.neopets.com/trophies/1221_1.gif"
            ]
        },
        {
            "name": "Neverending Boss Battle",
            "images": [
                "https://images.neopets.com/trophies/552_3.gif",
                "https://images.neopets.com/trophies/552_2.gif",
                "https://images.neopets.com/trophies/552_1.gif"
            ]
        },
        {
            "name": "Nimmos Pond",
            "images": [
                "https://images.neopets.com/trophies/1048_3.gif",
                "https://images.neopets.com/trophies/1048_2.gif",
                "https://images.neopets.com/trophies/1048_1.gif"
            ]
        },
        {
            "name": "Nova Defender",
            "images": [
                "https://images.neopets.com/trophies/1223_3.gif",
                "https://images.neopets.com/trophies/1223_2.gif",
                "https://images.neopets.com/trophies/1223_1.gif"
            ]
        },
        {
            "name": "Pakiko",
            "images": [
                "https://images.neopets.com/trophies/1369_3.gif",
                "https://images.neopets.com/trophies/1369_2.gif",
                "https://images.neopets.com/trophies/1369_1.gif"
            ]
        },
        {
            "name": "Petpet Battles",
            "images": [
                "https://images.neopets.com/trophies/231_3.gif",
                "https://images.neopets.com/trophies/231_2.gif",
                "https://images.neopets.com/trophies/231_1.gif"
            ]
        },
        {
            "name": "Petpet Cannonball",
            "images": [
                "https://images.neopets.com/trophies/553_3.gif",
                "https://images.neopets.com/trophies/553_2.gif",
                "https://images.neopets.com/trophies/553_1.gif"
            ]
        },
        {
            "name": "Petpet Plunge",
            "images": [
                "https://images.neopets.com/trophies/1078_3.gif",
                "https://images.neopets.com/trophies/1078_2.gif",
                "https://images.neopets.com/trophies/1078_1.gif"
            ]
        },
        {
            "name": "Petpet Rescue",
            "images": [
                "https://images.neopets.com/trophies/228_3.gif",
                "https://images.neopets.com/trophies/228_2.gif",
                "https://images.neopets.com/trophies/228_1.gif"
            ]
        },
        {
            "name": "Petpetsitter",
            "images": [
                "https://images.neopets.com/trophies/428_3.gif",
                "https://images.neopets.com/trophies/428_2.gif",
                "https://images.neopets.com/trophies/428_1.gif"
            ]
        },
        {
            "name": "Piper Panic",
            "images": [
                "https://images.neopets.com/trophies/973_3.gif",
                "https://images.neopets.com/trophies/973_2.gif",
                "https://images.neopets.com/trophies/973_1.gif"
            ]
        },
        {
            "name": "Pterattack",
            "images": [
                "https://images.neopets.com/trophies/587_3.gif",
                "https://images.neopets.com/trophies/587_2.gif",
                "https://images.neopets.com/trophies/587_1.gif"
            ]
        },
        {
            "name": "Raiders of Maraqua",
            "images": [
                "https://images.neopets.com/trophies/248_3.gif",
                "https://images.neopets.com/trophies/248_2.gif",
                "https://images.neopets.com/trophies/248_1.gif"
            ]
        },
        {
            "name": "Ready to Roll",
            "images": [
                "https://images.neopets.com/trophies/934_3.gif",
                "https://images.neopets.com/trophies/934_2.gif",
                "https://images.neopets.com/trophies/934_1.gif"
            ]
        },
        {
            "name": "Revel Roundup",
            "images": [
                "https://images.neopets.com/trophies/794_3.gif",
                "https://images.neopets.com/trophies/794_2.gif",
                "https://images.neopets.com/trophies/794_1.gif"
            ]
        },
        {
            "name": "Rink Runner",
            "images": [
                "https://images.neopets.com/trophies/220_3.gif",
                "https://images.neopets.com/trophies/220_2.gif",
                "https://images.neopets.com/trophies/220_1.gif"
            ]
        },
        {
            "name": "Roodoku",
            "images": [
                "https://images.neopets.com/trophies/820_3.gif",
                "https://images.neopets.com/trophies/820_2.gif",
                "https://images.neopets.com/trophies/820_1.gif"
            ]
        },
        {
            "name": "Round Table Poker",
            "images": [
                "https://images.neopets.com/trophies/177_3.gif",
                "https://images.neopets.com/trophies/177_2.gif",
                "https://images.neopets.com/trophies/177_1.gif"
            ]
        },
        {
            "name": "Ruins Rampage",
            "images": [
                "https://images.neopets.com/trophies/600_3.gif",
                "https://images.neopets.com/trophies/600_2.gif",
                "https://images.neopets.com/trophies/600_1.gif"
            ]
        },
        {
            "name": "S.M.E.L.T.",
            "images": [
                "https://images.neopets.com/trophies/1292_3.gif",
                "https://images.neopets.com/trophies/1292_2.gif",
                "https://images.neopets.com/trophies/1292_1.gif"
            ]
        },
        {
            "name": "Scarab 21",
            "images": [
                "https://images.neopets.com/trophies/70_3.gif",
                "https://images.neopets.com/trophies/70_2.gif",
                "https://images.neopets.com/trophies/70_1.gif"
            ]
        },
        {
            "name": "Scorchy Slots",
            "images": [
                "https://images.neopets.com/trophies/8_3.gif",
                "https://images.neopets.com/trophies/8_2.gif",
                "https://images.neopets.com/trophies/8_1.gif"
            ]
        },
        {
            "name": "Scourge of the Lab Jellies",
            "images": [
                "https://images.neopets.com/trophies/760_3.gif",
                "https://images.neopets.com/trophies/760_2.gif",
                "https://images.neopets.com/trophies/760_1.gif"
            ]
        },
        {
            "name": "Sewage Surfer",
            "images": [
                "https://images.neopets.com/trophies/157_3.gif",
                "https://images.neopets.com/trophies/157_2.gif",
                "https://images.neopets.com/trophies/157_1.gif"
            ]
        },
        {
            "name": "Shapeshifter",
            "images": [
                "https://images.neopets.com/trophies/151_3.gif",
                "https://images.neopets.com/trophies/151_2.gif",
                "https://images.neopets.com/trophies/151_1.gif"
            ]
        },
        {
            "name": "Shenkuu River Rush",
            "images": [
                "https://images.neopets.com/trophies/877_3.gif",
                "https://images.neopets.com/trophies/877_2.gif",
                "https://images.neopets.com/trophies/877_1.gif"
            ]
        },
        {
            "name": "Shenkuu Tangram",
            "images": [
                "https://images.neopets.com/trophies/1075_3.gif",
                "https://images.neopets.com/trophies/1075_2.gif",
                "https://images.neopets.com/trophies/1075_1.gif"
            ]
        },
        {
            "name": "Shenkuu Warrior",
            "images": [
                "https://images.neopets.com/trophies/786_3.gif",
                "https://images.neopets.com/trophies/786_2.gif",
                "https://images.neopets.com/trophies/786_1.gif"
            ]
        },
        {
            "name": "Shenkuu Warrior II",
            "images": [
                "https://images.neopets.com/trophies/1266_3.gif",
                "https://images.neopets.com/trophies/1266_2.gif",
                "https://images.neopets.com/trophies/1266_1.gif"
            ]
        },
        {
            "name": "Skies Over Meridell",
            "images": [
                "https://images.neopets.com/trophies/340_3.gif",
                "https://images.neopets.com/trophies/340_2.gif",
                "https://images.neopets.com/trophies/340_1.gif"
            ]
        },
        {
            "name": "Slorgs in Space",
            "images": [
                "https://images.neopets.com/trophies/1146_3.gif",
                "https://images.neopets.com/trophies/1146_2.gif",
                "https://images.neopets.com/trophies/1146_1.gif"
            ]
        },
        {
            "name": "Smug Bug Smite",
            "images": [
                "https://images.neopets.com/trophies/933_3.gif",
                "https://images.neopets.com/trophies/933_2.gif",
                "https://images.neopets.com/trophies/933_1.gif"
            ]
        },
        {
            "name": "Snot Splatter",
            "images": [
                "https://images.neopets.com/trophies/1100_3.gif",
                "https://images.neopets.com/trophies/1100_2.gif",
                "https://images.neopets.com/trophies/1100_1.gif"
            ]
        },
        {
            "name": "Snow Roller",
            "images": [
                "https://images.neopets.com/trophies/1076_3.gif",
                "https://images.neopets.com/trophies/1076_2.gif",
                "https://images.neopets.com/trophies/1076_1.gif"
            ]
        },
        {
            "name": "Snow Wars II",
            "images": [
                "https://images.neopets.com/trophies/544_3.gif",
                "https://images.neopets.com/trophies/544_2.gif",
                "https://images.neopets.com/trophies/544_1.gif"
            ]
        },
        {
            "name": "Snowball Fight",
            "images": [
                "https://images.neopets.com/trophies/633_3.gif",
                "https://images.neopets.com/trophies/633_2.gif",
                "https://images.neopets.com/trophies/633_1.gif"
            ]
        },
        {
            "name": "Snowbeast Snackrifice",
            "images": [
                "https://images.neopets.com/trophies/818_3.gif",
                "https://images.neopets.com/trophies/818_2.gif",
                "https://images.neopets.com/trophies/818_1.gif"
            ]
        },
        {
            "name": "Snowmuncher",
            "images": [
                "https://images.neopets.com/trophies/412_3.gif",
                "https://images.neopets.com/trophies/412_2.gif",
                "https://images.neopets.com/trophies/412_1.gif"
            ]
        },
        {
            "name": "Sophie's Stew",
            "images": [
                "https://images.neopets.com/trophies/659_3.gif",
                "https://images.neopets.com/trophies/659_2.gif",
                "https://images.neopets.com/trophies/659_1.gif"
            ]
        },
        {
            "name": "Sorcerers' Skirmish",
            "images": [
                "https://images.neopets.com/trophies/1202_3.gif",
                "https://images.neopets.com/trophies/1202_2.gif",
                "https://images.neopets.com/trophies/1202_1.gif"
            ]
        },
        {
            "name": "Spacerocked!",
            "images": [
                "https://images.neopets.com/trophies/964_3.gif",
                "https://images.neopets.com/trophies/964_2.gif",
                "https://images.neopets.com/trophies/964_1.gif"
            ]
        },
        {
            "name": "Spell-Or-Starve",
            "images": [
                "https://images.neopets.com/trophies/202_3.gif",
                "https://images.neopets.com/trophies/202_2.gif",
                "https://images.neopets.com/trophies/202_1.gif"
            ]
        },
        {
            "name": "Spellseeker",
            "images": [
                "https://images.neopets.com/trophies/1157_3.gif",
                "https://images.neopets.com/trophies/1157_2.gif",
                "https://images.neopets.com/trophies/1157_1.gif"
            ]
        },
        {
            "name": "Spinacles",
            "images": [
                "https://images.neopets.com/trophies/1134_3.gif",
                "https://images.neopets.com/trophies/1134_2.gif",
                "https://images.neopets.com/trophies/1134_1.gif"
            ]
        },
        {
            "name": "Splat-A-Sloth",
            "images": [
                "https://images.neopets.com/trophies/81_3.gif",
                "https://images.neopets.com/trophies/81_2.gif",
                "https://images.neopets.com/trophies/81_1.gif"
            ]
        },
        {
            "name": "Stowaway Sting",
            "images": [
                "https://images.neopets.com/trophies/852_3.gif",
                "https://images.neopets.com/trophies/852_2.gif",
                "https://images.neopets.com/trophies/852_1.gif"
            ]
        },
        {
            "name": "Super Hasee Bounce",
            "images": [
                "https://images.neopets.com/trophies/1061_3.gif",
                "https://images.neopets.com/trophies/1061_2.gif",
                "https://images.neopets.com/trophies/1061_1.gif"
            ]
        },
        {
            "name": "Sutek's Tomb",
            "images": [
                "https://images.neopets.com/trophies/306_3.gif",
                "https://images.neopets.com/trophies/306_2.gif",
                "https://images.neopets.com/trophies/306_1.gif"
            ]
        },
        {
            "name": "Swarm - The Bugs Strike Back",
            "images": [
                "https://images.neopets.com/trophies/562_3.gif",
                "https://images.neopets.com/trophies/562_2.gif",
                "https://images.neopets.com/trophies/562_1.gif"
            ]
        },
        {
            "name": "Techo Says",
            "images": [
                "https://images.neopets.com/trophies/1000_3.gif",
                "https://images.neopets.com/trophies/1000_2.gif",
                "https://images.neopets.com/trophies/1000_1.gif"
            ]
        },
        {
            "name": "Terror Mountain Tilt",
            "images": [
                "https://images.neopets.com/trophies/925_3.gif",
                "https://images.neopets.com/trophies/925_2.gif",
                "https://images.neopets.com/trophies/925_1.gif"
            ]
        },
        {
            "name": "The Buzzer Game",
            "images": [
                "https://images.neopets.com/trophies/307_3.gif",
                "https://images.neopets.com/trophies/307_2.gif",
                "https://images.neopets.com/trophies/307_1.gif"
            ]
        },
        {
            "name": "The Castle of Eliv Thade",
            "images": [
                "https://images.neopets.com/trophies/230_3.gif",
                "https://images.neopets.com/trophies/230_2.gif",
                "https://images.neopets.com/trophies/230_1.gif"
            ]
        },
        {
            "name": "The Great Desert Race",
            "images": [
                "https://images.neopets.com/trophies/830_3.gif",
                "https://images.neopets.com/trophies/830_2.gif",
                "https://images.neopets.com/trophies/830_1.gif"
            ]
        },
        {
            "name": "The Great Qasalan Caper",
            "images": [
                "https://images.neopets.com/trophies/660_3.gif",
                "https://images.neopets.com/trophies/660_2.gif",
                "https://images.neopets.com/trophies/660_1.gif"
            ]
        },
        {
            "name": "The Last Blast",
            "images": [
                "https://images.neopets.com/trophies/926_3.gif",
                "https://images.neopets.com/trophies/926_2.gif",
                "https://images.neopets.com/trophies/926_1.gif"
            ]
        },
        {
            "name": "The Return of the Return of Dr. Sloth",
            "images": [
                "https://images.neopets.com/trophies/480_3.gif",
                "https://images.neopets.com/trophies/480_2.gif",
                "https://images.neopets.com/trophies/480_1.gif"
            ]
        },
        {
            "name": "The Search for Princess Lunara",
            "images": [
                "https://images.neopets.com/trophies/874_3.gif",
                "https://images.neopets.com/trophies/874_2.gif",
                "https://images.neopets.com/trophies/874_1.gif"
            ]
        },
        {
            "name": "The Usul Suspects",
            "images": [
                "https://images.neopets.com/trophies/789_3.gif",
                "https://images.neopets.com/trophies/789_2.gif",
                "https://images.neopets.com/trophies/789_1.gif"
            ]
        },
        {
            "name": "Time Tunnel",
            "images": [
                "https://images.neopets.com/trophies/536_3.gif",
                "https://images.neopets.com/trophies/536_2.gif",
                "https://images.neopets.com/trophies/536_1.gif"
            ]
        },
        {
            "name": "TNT Staff Smasher",
            "images": [
                "https://images.neopets.com/trophies/198_3.gif",
                "https://images.neopets.com/trophies/198_2.gif",
                "https://images.neopets.com/trophies/198_1.gif"
            ]
        },
        {
            "name": "Top Chop",
            "images": [
                "https://images.neopets.com/trophies/1095_3.gif",
                "https://images.neopets.com/trophies/1095_2.gif",
                "https://images.neopets.com/trophies/1095_1.gif"
            ]
        },
        {
            "name": "Toy Box Escape",
            "images": [
                "https://images.neopets.com/trophies/367_3.gif",
                "https://images.neopets.com/trophies/367_2.gif",
                "https://images.neopets.com/trophies/367_1.gif"
            ]
        },
        {
            "name": "Trouble at the National Neopian",
            "images": [
                "https://images.neopets.com/trophies/371_3.gif",
                "https://images.neopets.com/trophies/371_2.gif",
                "https://images.neopets.com/trophies/371_1.gif"
            ]
        },
        {
            "name": "Tubular Kiko Racing",
            "images": [
                "https://images.neopets.com/trophies/606_3.gif",
                "https://images.neopets.com/trophies/606_2.gif",
                "https://images.neopets.com/trophies/606_1.gif"
            ]
        },
        {
            "name": "Tug 'O' War",
            "images": [
                "https://images.neopets.com/trophies/909_3.gif",
                "https://images.neopets.com/trophies/909_2.gif",
                "https://images.neopets.com/trophies/909_1.gif"
            ]
        },
        {
            "name": "Tunnel Tumble",
            "images": [
                "https://images.neopets.com/trophies/1175_3.gif",
                "https://images.neopets.com/trophies/1175_2.gif",
                "https://images.neopets.com/trophies/1175_1.gif"
            ]
        },
        {
            "name": "Turmac Roll",
            "images": [
                "https://images.neopets.com/trophies/366_3.gif",
                "https://images.neopets.com/trophies/366_2.gif",
                "https://images.neopets.com/trophies/366_1.gif"
            ]
        },
        {
            "name": "Typing Terror",
            "images": [
                "https://images.neopets.com/trophies/574_3.gif",
                "https://images.neopets.com/trophies/574_2.gif",
                "https://images.neopets.com/trophies/574_1.gif"
            ]
        },
        {
            "name": "Tyrannian Mini Golf",
            "images": [
                "https://images.neopets.com/trophies/648_3.gif",
                "https://images.neopets.com/trophies/648_2.gif",
                "https://images.neopets.com/trophies/648_1.gif"
            ]
        },
        {
            "name": "Tyranu Evavu",
            "images": [
                "https://images.neopets.com/trophies/47_3.gif",
                "https://images.neopets.com/trophies/47_2.gif",
                "https://images.neopets.com/trophies/47_1.gif"
            ]
        },
        {
            "name": "Ugga Drop",
            "images": [
                "https://images.neopets.com/trophies/1204_3.gif",
                "https://images.neopets.com/trophies/1204_2.gif",
                "https://images.neopets.com/trophies/1204_1.gif"
            ]
        },
        {
            "name": "Ugga Smash",
            "images": [
                "https://images.neopets.com/trophies/726_3.gif",
                "https://images.neopets.com/trophies/726_2.gif",
                "https://images.neopets.com/trophies/726_1.gif"
            ]
        },
        {
            "name": "Ultimate Bullseye II",
            "images": [
                "https://images.neopets.com/trophies/903_3.gif",
                "https://images.neopets.com/trophies/903_2.gif",
                "https://images.neopets.com/trophies/903_1.gif"
            ]
        },
        {
            "name": "Usuki Frenzy",
            "images": [
                "https://images.neopets.com/trophies/884_3.gif",
                "https://images.neopets.com/trophies/884_2.gif",
                "https://images.neopets.com/trophies/884_1.gif"
            ]
        },
        {
            "name": "Volcano Run II",
            "images": [
                "https://images.neopets.com/trophies/761_3.gif",
                "https://images.neopets.com/trophies/761_2.gif",
                "https://images.neopets.com/trophies/761_1.gif"
            ]
        },
        {
            "name": "Warf Rescue Team",
            "images": [
                "https://images.neopets.com/trophies/305_3.gif",
                "https://images.neopets.com/trophies/305_2.gif",
                "https://images.neopets.com/trophies/305_1.gif"
            ]
        },
        {
            "name": "Web of Vernax",
            "images": [
                "https://images.neopets.com/trophies/353_3.gif",
                "https://images.neopets.com/trophies/353_2.gif",
                "https://images.neopets.com/trophies/353_1.gif"
            ]
        },
        {
            "name": "Wheeler's Wild Ride",
            "images": [
                "https://images.neopets.com/trophies/1080_3.gif",
                "https://images.neopets.com/trophies/1080_2.gif",
                "https://images.neopets.com/trophies/1080_1.gif"
            ]
        },
        {
            "name": "Whirlpool",
            "images": [
                "https://images.neopets.com/trophies/927_3.gif",
                "https://images.neopets.com/trophies/927_2.gif",
                "https://images.neopets.com/trophies/927_1.gif"
            ]
        },
        {
            "name": "Wicked Wocky Wobble",
            "images": [
                "https://images.neopets.com/trophies/881_3.gif",
                "https://images.neopets.com/trophies/881_2.gif",
                "https://images.neopets.com/trophies/881_1.gif"
            ]
        },
        {
            "name": "Wingoball",
            "images": [
                "https://images.neopets.com/trophies/771_3.gif",
                "https://images.neopets.com/trophies/771_2.gif",
                "https://images.neopets.com/trophies/771_1.gif"
            ]
        },
        {
            "name": "Word Poker",
            "images": [
                "https://images.neopets.com/trophies/229_3.gif",
                "https://images.neopets.com/trophies/229_2.gif",
                "https://images.neopets.com/trophies/229_1.gif"
            ]
        },
        {
            "name": "Word Pyramid",
            "images": [
                "https://images.neopets.com/trophies/575_3.gif",
                "https://images.neopets.com/trophies/575_2.gif",
                "https://images.neopets.com/trophies/575_1.gif"
            ]
        },
        {
            "name": "Wrath of the Snowager",
            "images": [
                "https://images.neopets.com/trophies/1269_3.gif",
                "https://images.neopets.com/trophies/1269_2.gif",
                "https://images.neopets.com/trophies/1269_1.gif"
            ]
        },
        {
            "name": "Zurroball",
            "images": [
                "https://images.neopets.com/trophies/207_3.gif",
                "https://images.neopets.com/trophies/207_2.gif",
                "https://images.neopets.com/trophies/207_1.gif"
            ]
        }
    ];

    const cumulativeHighScoreTrophies = [
        {
            "name": "NeggSweeper Cumulative",
            "images": [
                "https://images.neopets.com/trophies/84_3.gif",
                "https://images.neopets.com/trophies/84_2.gif",
                "https://images.neopets.com/trophies/84_1.gif"
            ]
        },
        {
            "name": "Pyramids",
            "images": [
                "https://images.neopets.com/trophies/67_3.gif",
                "https://images.neopets.com/trophies/67_2.gif",
                "https://images.neopets.com/trophies/67_1.gif"
            ]
        },
        {
            "name": "Sakhmet Solitaire",
            "images": [
                "https://images.neopets.com/trophies/76_3.gif",
                "https://images.neopets.com/trophies/76_2.gif",
                "https://images.neopets.com/trophies/76_1.gif"
            ]
        },
        {
            "name": "Scarab 21 Cumulative",
            "images": [
                "https://images.neopets.com/trophies/69_3.gif",
                "https://images.neopets.com/trophies/69_2.gif",
                "https://images.neopets.com/trophies/69_1.gif"
            ]
        },
        {
            "name": "Slots Big Losers",
            "images": [
                "https://images.neopets.com/trophies/448_3.gif",
                "https://images.neopets.com/trophies/448_2.gif",
                "https://images.neopets.com/trophies/448_1.gif"
            ]
        }
    ];

    const otherHighScoreTrophies = [
        {
            "name": "Angry Tax Beast",
            "images": [
                "https://images.neopets.com/trophies/487_3.gif",
                "https://images.neopets.com/trophies/487_2.gif",
                "https://images.neopets.com/trophies/487_1.gif"
            ]
        },
        {
            "name": "Brain Tree Quest",
            "images": [
                "https://images.neopets.com/trophies/75_3.gif",
                "https://images.neopets.com/trophies/75_2.gif",
                "https://images.neopets.com/trophies/75_1.gif"
            ]
        },
        {
            "name": "Card Collector",
            "images": [
                "https://images.neopets.com/trophies/1329_3.gif",
                "https://images.neopets.com/trophies/1329_2.gif",
                "https://images.neopets.com/trophies/1329_1.gif"
            ]
        },
        {
            "name": "Food Club",
            "images": [
                "https://images.neopets.com/trophies/88_3.gif",
                "https://images.neopets.com/trophies/88_2.gif",
                "https://images.neopets.com/trophies/88_1.gif"
            ]
        },
        {
            "name": "Grumpy Old King",
            "images": [
                "https://images.neopets.com/trophies/218_3.gif",
                "https://images.neopets.com/trophies/218_2.gif",
                "https://images.neopets.com/trophies/218_1.gif"
            ]
        },
        {
            "name": "NeoBoard Avatar Collector",
            "images": [
                "https://images.neopets.com/trophies/342_3.gif",
                "https://images.neopets.com/trophies/342_2.gif",
                "https://images.neopets.com/trophies/342_1.gif"
            ]
        },
        {
            "name": "Sloth's Invasion Tax",
            "images": [
                "https://images.neopets.com/trophies/341_3.gif",
                "https://images.neopets.com/trophies/341_2.gif",
                "https://images.neopets.com/trophies/341_1.gif"
            ]
        },
        {
            "name": "Stamp Collector",
            "images": [
                "https://images.neopets.com/trophies/346_3.gif",
                "https://images.neopets.com/trophies/346_2.gif",
                "https://images.neopets.com/trophies/346_1.gif"
            ]
        },
        {
            "name": "Tax Beast",
            "images": [
                "https://images.neopets.com/trophies/196_3.gif",
                "https://images.neopets.com/trophies/196_2.gif",
                "https://images.neopets.com/trophies/196_1.gif"
            ]
        },
        {
            "name": "Test Your Strength",
            "images": [
                "https://images.neopets.com/trophies/331_3.gif",
                "https://images.neopets.com/trophies/331_2.gif",
                "https://images.neopets.com/trophies/331_1.gif"
            ]
        },
        {
            "name": "Vending Machine",
            "images": [
                "https://images.neopets.com/trophies/36_3.gif",
                "https://images.neopets.com/trophies/36_2.gif",
                "https://images.neopets.com/trophies/36_1.gif"
            ]
        },
        {
            "name": "Wise Old King",
            "images": [
                "https://images.neopets.com/trophies/493_3.gif",
                "https://images.neopets.com/trophies/493_2.gif",
                "https://images.neopets.com/trophies/493_1.gif"
            ]
        }
    ];

    const pvpTrophies = [
        {
            "name": "Armada",
            "images": [
                "https://images.neopets.com/trophies/18_3.gif",
                "https://images.neopets.com/trophies/18_2.gif",
                "https://images.neopets.com/trophies/18_1.gif"
            ]
        },
        {
            "name": "Geos",
            "images": [
                "https://images.neopets.com/trophies/113_3.gif",
                "https://images.neopets.com/trophies/113_2.gif",
                "https://images.neopets.com/trophies/113_1.gif"
            ]
        },
        {
            "name": "Kacheekers",
            "images": [
                "https://images.neopets.com/trophies/111_3.gif",
                "https://images.neopets.com/trophies/111_2.gif",
                "https://images.neopets.com/trophies/111_1.gif"
            ]
        }
    ];

    const nonCompetitiveTrophies = [
        {
            "name": "Beating Punchbag Bob",
            "images": [
                "https://images.neopets.com/trophies/115_1.gif"
            ]
        },
        {
            "name": "Cellblock",
            "images": [
                "https://images.neopets.com/trophies/216_3.gif",
                "https://images.neopets.com/trophies/216_2.gif",
                "https://images.neopets.com/trophies/216_1.gif"
            ]
        },
        {
            "name": "Cheat!",
            "images": [
                "https://images.neopets.com/trophies/109_3.gif",
                "https://images.neopets.com/trophies/109_2.gif",
                "https://images.neopets.com/trophies/109_1.gif"
            ]
        },
        {
            "name": "Cosy Campfire Collection",
            "images": [
                "https://images.neopets.com/trophies/1409_3.gif",
                "https://images.neopets.com/trophies/1409_2.gif",
                "https://images.neopets.com/trophies/1409_1.gif"
            ]
        },
        {
            "name": "Go! Go! Go!",
            "images": [
                "https://images.neopets.com/trophies/108_3.gif",
                "https://images.neopets.com/trophies/108_2.gif",
                "https://images.neopets.com/trophies/108_1.gif"
            ]
        },
        {
            "name": "The Neopian Lottery",
            "images": [
                "https://images.neopets.com/trophies/58_3.gif",
                "https://images.neopets.com/trophies/58_2.gif",
                "https://images.neopets.com/trophies/58_1.gif"
            ]
        },
        {
            "name": "NeoQuest",
            "images": [
                "https://images.neopets.com/trophies/91_3.gif",
                "https://images.neopets.com/trophies/91_2.gif",
                "https://images.neopets.com/trophies/91_1.gif"
            ]
        },
        {
            "name": "NeoQuest II",
            "images": [
                "https://images.neopets.com/trophies/372_3.gif",
                "https://images.neopets.com/trophies/372_2.gif",
                "https://images.neopets.com/trophies/372_1.gif"
            ]
        },
        {
            "name": "Plushie Tycoon",
            "images": [
                "https://images.neopets.com/trophies/170_3.gif",
                "https://images.neopets.com/trophies/170_2.gif",
                "https://images.neopets.com/trophies/170_1.gif"
            ]
        },
        {
            "name": "Pyramid Bonus",
            "images": [
                "https://images.neopets.com/trophies/68_3.gif",
                "https://images.neopets.com/trophies/68_2.gif",
                "https://images.neopets.com/trophies/68_1.gif"
            ]
        },
        {
            "name": "Sakhmet Solitaire Bonus",
            "images": [
                "https://images.neopets.com/trophies/77_3.gif",
                "https://images.neopets.com/trophies/77_2.gif",
                "https://images.neopets.com/trophies/77_1.gif"
            ]
        },
        {
            "name": "Snow Wars",
            "images": [
                "https://images.neopets.com/trophies/55_3.gif",
                "https://images.neopets.com/trophies/55_2.gif",
                "https://images.neopets.com/trophies/55_1.gif"
            ]
        }
    ];

    const retiredGameTrophies = [
        {
            "name": "200m Peanut Dash",
            "images": [
                "https://images.neopets.com/trophies/189_3.gif",
                "https://images.neopets.com/trophies/189_2.gif",
                "https://images.neopets.com/trophies/189_1.gif"
            ]
        },
        {
            "name": "Aisha Puzzle",
            "images": [
                "https://images.neopets.com/trophies/21_3.gif",
                "https://images.neopets.com/trophies/21_2.gif",
                "https://images.neopets.com/trophies/21_1.gif"
            ]
        },
        {
            "name": "Alien Invasion",
            "images": [
                "https://images.neopets.com/trophies/4_3.gif",
                "https://images.neopets.com/trophies/4_2.gif",
                "https://images.neopets.com/trophies/4_1.gif"
            ]
        },
        {
            "name": "Alpine Challenge",
            "images": [
                "https://images.neopets.com/trophies/35_3.gif",
                "https://images.neopets.com/trophies/35_2.gif",
                "https://images.neopets.com/trophies/35_1.gif"
            ]
        },
        {
            "name": "Balthazar Basher",
            "images": [
                "https://images.neopets.com/trophies/482_3.gif",
                "https://images.neopets.com/trophies/482_2.gif",
                "https://images.neopets.com/trophies/482_1.gif"
            ]
        },
        {
            "name": "Beast Smack",
            "images": [
                "https://images.neopets.com/trophies/33_3.gif",
                "https://images.neopets.com/trophies/33_2.gif",
                "https://images.neopets.com/trophies/33_1.gif"
            ]
        },
        {
            "name": "Bilge Dice",
            "images": [
                "https://images.neopets.com/trophies/351_3.gif",
                "https://images.neopets.com/trophies/351_2.gif",
                "https://images.neopets.com/trophies/351_1.gif"
            ]
        },
        {
            "name": "Bilge Dice Streak",
            "images": [
                "https://images.neopets.com/trophies/352_3.gif",
                "https://images.neopets.com/trophies/352_2.gif",
                "https://images.neopets.com/trophies/352_1.gif"
            ]
        },
        {
            "name": "Black Pawkeet Slots",
            "images": [
                "https://images.neopets.com/trophies/309_3.gif",
                "https://images.neopets.com/trophies/309_2.gif",
                "https://images.neopets.com/trophies/309_1.gif"
            ]
        },
        {
            "name": "Brucey B Slots",
            "images": [
                "https://images.neopets.com/trophies/300_3.gif",
                "https://images.neopets.com/trophies/300_2.gif",
                "https://images.neopets.com/trophies/300_1.gif"
            ]
        },
        {
            "name": "BUBBLE YUM",
            "images": [
                "https://images.neopets.com/trophies/171_3.gif",
                "https://images.neopets.com/trophies/171_2.gif",
                "https://images.neopets.com/trophies/171_1.gif"
            ]
        },
        {
            "name": "Bullseye",
            "images": [
                "https://images.neopets.com/trophies/39_3.gif",
                "https://images.neopets.com/trophies/39_2.gif",
                "https://images.neopets.com/trophies/39_1.gif"
            ]
        },
        {
            "name": "Bumper Cars",
            "images": [
                "https://images.neopets.com/trophies/61_3.gif",
                "https://images.neopets.com/trophies/61_2.gif",
                "https://images.neopets.com/trophies/61_1.gif"
            ]
        },
        {
            "name": "Carnival of Terror",
            "images": [
                "https://images.neopets.com/trophies/131_3.gif",
                "https://images.neopets.com/trophies/131_2.gif",
                "https://images.neopets.com/trophies/131_1.gif"
            ]
        },
        {
            "name": "Chia Bingo",
            "images": [
                "https://images.neopets.com/trophies/9_3.gif",
                "https://images.neopets.com/trophies/9_2.gif",
                "https://images.neopets.com/trophies/9_1.gif"
            ]
        },
        {
            "name": "Chia Bomber",
            "images": [
                "https://images.neopets.com/trophies/62_3.gif",
                "https://images.neopets.com/trophies/62_2.gif",
                "https://images.neopets.com/trophies/62_1.gif"
            ]
        },
        {
            "name": "Chiazilla Puzzle",
            "images": [
                "https://images.neopets.com/trophies/23_3.gif",
                "https://images.neopets.com/trophies/23_2.gif",
                "https://images.neopets.com/trophies/23_1.gif"
            ]
        },
        {
            "name": "Chomby and the Fungus Balls",
            "images": [
                "https://images.neopets.com/trophies/49_3.gif",
                "https://images.neopets.com/trophies/49_2.gif",
                "https://images.neopets.com/trophies/49_1.gif"
            ]
        },
        {
            "name": "Chute",
            "images": [
                "https://images.neopets.com/trophies/60_3.gif",
                "https://images.neopets.com/trophies/60_2.gif",
                "https://images.neopets.com/trophies/60_1.gif"
            ]
        },
        {
            "name": "CodeBreakers",
            "images": [
                "https://images.neopets.com/trophies/2_3.gif",
                "https://images.neopets.com/trophies/2_2.gif",
                "https://images.neopets.com/trophies/2_1.gif"
            ]
        },
        {
            "name": "Dark Faerie Puzzle",
            "images": [
                "https://images.neopets.com/trophies/24_3.gif",
                "https://images.neopets.com/trophies/24_2.gif",
                "https://images.neopets.com/trophies/24_1.gif"
            ]
        },
        {
            "name": "Deckball Timed",
            "images": [
                "https://images.neopets.com/trophies/83_3.gif",
                "https://images.neopets.com/trophies/83_2.gif",
                "https://images.neopets.com/trophies/83_1.gif"
            ]
        },
        {
            "name": "Deckswabber",
            "images": [
                "https://images.neopets.com/trophies/19_3.gif",
                "https://images.neopets.com/trophies/19_2.gif",
                "https://images.neopets.com/trophies/19_1.gif"
            ]
        },
        {
            "name": "Destruct-O-Match",
            "images": [
                "https://images.neopets.com/trophies/53_3.gif",
                "https://images.neopets.com/trophies/53_2.gif",
                "https://images.neopets.com/trophies/53_1.gif"
            ]
        },
        {
            "name": "Destruct-O-Match II",
            "images": [
                "https://images.neopets.com/trophies/453_3.gif",
                "https://images.neopets.com/trophies/453_2.gif",
                "https://images.neopets.com/trophies/453_1.gif"
            ]
        },
        {
            "name": "Destruct-O-Match II",
            "images": [
                "https://images.neopets.com/trophies/759_3.gif",
                "https://images.neopets.com/trophies/759_2.gif",
                "https://images.neopets.com/trophies/759_1.gif"
            ]
        },
        {
            "name": "Dubloon Disaster",
            "images": [
                "https://images.neopets.com/trophies/143_3.gif",
                "https://images.neopets.com/trophies/143_2.gif",
                "https://images.neopets.com/trophies/143_1.gif"
            ]
        },
        {
            "name": "Earth Faerie Aces",
            "images": [
                "https://images.neopets.com/trophies/40_3.gif",
                "https://images.neopets.com/trophies/40_2.gif",
                "https://images.neopets.com/trophies/40_1.gif"
            ]
        },
        {
            "name": "Earth Faerie Aces - Expert",
            "images": [
                "https://images.neopets.com/trophies/41_3.gif",
                "https://images.neopets.com/trophies/41_2.gif",
                "https://images.neopets.com/trophies/41_1.gif"
            ]
        },
        {
            "name": "Evil Fuzzles from Beyond the Stars",
            "images": [
                "https://images.neopets.com/trophies/128_3.gif",
                "https://images.neopets.com/trophies/128_2.gif",
                "https://images.neopets.com/trophies/128_1.gif"
            ]
        },
        {
            "name": "Faerie Caves",
            "images": [
                "https://images.neopets.com/trophies/43_3.gif",
                "https://images.neopets.com/trophies/43_2.gif",
                "https://images.neopets.com/trophies/43_1.gif"
            ]
        },
        {
            "name": "Faerie Cloud Racers",
            "images": [
                "https://images.neopets.com/trophies/137_3.gif",
                "https://images.neopets.com/trophies/137_2.gif",
                "https://images.neopets.com/trophies/137_1.gif"
            ]
        },
        {
            "name": "Faerie Quest",
            "images": [
                "https://images.neopets.com/trophies/37_3.gif",
                "https://images.neopets.com/trophies/37_2.gif",
                "https://images.neopets.com/trophies/37_1.gif"
            ]
        },
        {
            "name": "Feed Florg",
            "images": [
                "https://images.neopets.com/trophies/156_3.gif",
                "https://images.neopets.com/trophies/156_2.gif",
                "https://images.neopets.com/trophies/156_1.gif"
            ]
        },
        {
            "name": "Fruit Machine",
            "images": [
                "https://images.neopets.com/trophies/20_3.gif",
                "https://images.neopets.com/trophies/20_2.gif",
                "https://images.neopets.com/trophies/20_1.gif"
            ]
        },
        {
            "name": "Grundo Snowthrow",
            "images": [
                "https://images.neopets.com/trophies/31_3.gif",
                "https://images.neopets.com/trophies/31_2.gif",
                "https://images.neopets.com/trophies/31_1.gif"
            ]
        },
        {
            "name": "Ice Caves Puzzle",
            "images": [
                "https://images.neopets.com/trophies/32_3.gif",
                "https://images.neopets.com/trophies/32_2.gif",
                "https://images.neopets.com/trophies/32_1.gif"
            ]
        },
        {
            "name": "Ice Cream Factory",
            "images": [
                "https://images.neopets.com/trophies/57_3.gif",
                "https://images.neopets.com/trophies/57_2.gif",
                "https://images.neopets.com/trophies/57_1.gif"
            ]
        },
        {
            "name": "Igloo Garage Sale - The Game",
            "images": [
                "https://images.neopets.com/trophies/169_3.gif",
                "https://images.neopets.com/trophies/169_2.gif",
                "https://images.neopets.com/trophies/169_1.gif"
            ]
        },
        {
            "name": "Invasion of Meridell",
            "images": [
                "https://images.neopets.com/trophies/182_3.gif",
                "https://images.neopets.com/trophies/182_2.gif",
                "https://images.neopets.com/trophies/182_1.gif"
            ]
        },
        {
            "name": "Jelly Processing Plant",
            "images": [
                "https://images.neopets.com/trophies/95_3.gif",
                "https://images.neopets.com/trophies/95_2.gif",
                "https://images.neopets.com/trophies/95_1.gif"
            ]
        },
        {
            "name": "JubJub Scramble",
            "images": [
                "https://images.neopets.com/trophies/56_3.gif",
                "https://images.neopets.com/trophies/56_2.gif",
                "https://images.neopets.com/trophies/56_1.gif"
            ]
        },
        {
            "name": "Kau Korrall",
            "images": [
                "https://images.neopets.com/trophies/15_3.gif",
                "https://images.neopets.com/trophies/15_2.gif",
                "https://images.neopets.com/trophies/15_1.gif"
            ]
        },
        {
            "name": "Kauvaras Potion Cumulative",
            "images": [
                "https://images.neopets.com/trophies/99_3.gif",
                "https://images.neopets.com/trophies/99_2.gif",
                "https://images.neopets.com/trophies/99_1.gif"
            ]
        },
        {
            "name": "Kauvaras Potion Game",
            "images": [
                "https://images.neopets.com/trophies/98_3.gif",
                "https://images.neopets.com/trophies/98_2.gif",
                "https://images.neopets.com/trophies/98_1.gif"
            ]
        },
        {
            "name": "Kiko Match",
            "images": [
                "https://images.neopets.com/trophies/3_3.gif",
                "https://images.neopets.com/trophies/3_2.gif",
                "https://images.neopets.com/trophies/3_1.gif"
            ]
        },
        {
            "name": "Kiko Match II",
            "images": [
                "https://images.neopets.com/trophies/93_3.gif",
                "https://images.neopets.com/trophies/93_2.gif",
                "https://images.neopets.com/trophies/93_1.gif"
            ]
        },
        {
            "name": "Korbats Lab",
            "images": [
                "https://images.neopets.com/trophies/85_3.gif",
                "https://images.neopets.com/trophies/85_2.gif",
                "https://images.neopets.com/trophies/85_1.gif"
            ]
        },
        {
            "name": "MAGAX: Destroyer",
            "images": [
                "https://images.neopets.com/trophies/162_3.gif",
                "https://images.neopets.com/trophies/162_2.gif",
                "https://images.neopets.com/trophies/162_1.gif"
            ]
        },
        {
            "name": "Maraqua Puzzle",
            "images": [
                "https://images.neopets.com/trophies/22_3.gif",
                "https://images.neopets.com/trophies/22_2.gif",
                "https://images.neopets.com/trophies/22_1.gif"
            ]
        },
        {
            "name": "Maths Nightmare",
            "images": [
                "https://images.neopets.com/trophies/150_3.gif",
                "https://images.neopets.com/trophies/150_2.gif",
                "https://images.neopets.com/trophies/150_1.gif"
            ]
        },
        {
            "name": "Meerca Chase",
            "images": [
                "https://images.neopets.com/trophies/46_3.gif",
                "https://images.neopets.com/trophies/46_2.gif",
                "https://images.neopets.com/trophies/46_1.gif"
            ]
        },
        {
            "name": "Meriball",
            "images": [
                "https://images.neopets.com/trophies/173_3.gif",
                "https://images.neopets.com/trophies/173_2.gif",
                "https://images.neopets.com/trophies/173_1.gif"
            ]
        },
        {
            "name": "Meristones",
            "images": [
                "https://images.neopets.com/trophies/180_3.gif",
                "https://images.neopets.com/trophies/180_2.gif",
                "https://images.neopets.com/trophies/180_1.gif"
            ]
        },
        {
            "name": "Meristones Cumulative",
            "images": [
                "https://images.neopets.com/trophies/181_3.gif",
                "https://images.neopets.com/trophies/181_2.gif",
                "https://images.neopets.com/trophies/181_1.gif"
            ]
        },
        {
            "name": "Moon Puzzle",
            "images": [
                "https://images.neopets.com/trophies/25_3.gif",
                "https://images.neopets.com/trophies/25_2.gif",
                "https://images.neopets.com/trophies/25_1.gif"
            ]
        },
        {
            "name": "Mummy Maze",
            "images": [
                "https://images.neopets.com/trophies/71_3.gif",
                "https://images.neopets.com/trophies/71_2.gif",
                "https://images.neopets.com/trophies/71_1.gif"
            ]
        },
        {
            "name": "Mutant Graveyard of DOOM",
            "images": [
                "https://images.neopets.com/trophies/65_3.gif",
                "https://images.neopets.com/trophies/65_2.gif",
                "https://images.neopets.com/trophies/65_1.gif"
            ]
        },
        {
            "name": "Neo DJ",
            "images": [
                "https://images.neopets.com/trophies/30_3.gif",
                "https://images.neopets.com/trophies/30_2.gif",
                "https://images.neopets.com/trophies/30_1.gif"
            ]
        },
        {
            "name": "Neoknights",
            "images": [
                "https://images.neopets.com/trophies/34_3.gif",
                "https://images.neopets.com/trophies/34_2.gif",
                "https://images.neopets.com/trophies/34_1.gif"
            ]
        },
        {
            "name": "NeoQuest II Artists",
            "images": [
                "https://images.neopets.com/trophies/374_2.gif",
                "https://images.neopets.com/trophies/374_1.gif"
            ]
        },
        {
            "name": "NeoQuest Race",
            "images": [
                "https://images.neopets.com/trophies/90_3.gif",
                "https://images.neopets.com/trophies/90_2.gif",
                "https://images.neopets.com/trophies/90_1.gif"
            ]
        },
        {
            "name": "Nimmos Pond",
            "images": [
                "https://images.neopets.com/trophies/74_3.gif",
                "https://images.neopets.com/trophies/74_2.gif",
                "https://images.neopets.com/trophies/74_1.gif"
            ]
        },
        {
            "name": "Nova Battle",
            "images": [
                "https://images.neopets.com/trophies/1188_3.gif",
                "https://images.neopets.com/trophies/1188_2.gif",
                "https://images.neopets.com/trophies/1188_1.gif"
            ]
        },
        {
            "name": "Omelette Defender",
            "images": [
                "https://images.neopets.com/trophies/127_3.gif",
                "https://images.neopets.com/trophies/127_2.gif",
                "https://images.neopets.com/trophies/127_1.gif"
            ]
        },
        {
            "name": "Pterattack",
            "images": [
                "https://images.neopets.com/trophies/63_3.gif",
                "https://images.neopets.com/trophies/63_2.gif",
                "https://images.neopets.com/trophies/63_1.gif"
            ]
        },
        {
            "name": "Pumpkin Patch",
            "images": [
                "https://images.neopets.com/trophies/64_3.gif",
                "https://images.neopets.com/trophies/64_2.gif",
                "https://images.neopets.com/trophies/64_1.gif"
            ]
        },
        {
            "name": "Skarl's Scramble",
            "images": [
                "https://images.neopets.com/trophies/243_3.gif",
                "https://images.neopets.com/trophies/243_2.gif",
                "https://images.neopets.com/trophies/243_1.gif"
            ]
        },
        {
            "name": "Snow Dogs",
            "images": [
                "https://images.neopets.com/trophies/96_3.gif",
                "https://images.neopets.com/trophies/96_2.gif",
                "https://images.neopets.com/trophies/96_1.gif"
            ]
        },
        {
            "name": "Solo Says",
            "images": [
                "https://images.neopets.com/trophies/11_3.gif",
                "https://images.neopets.com/trophies/11_2.gif",
                "https://images.neopets.com/trophies/11_1.gif"
            ]
        },
        {
            "name": "Super Bullseye",
            "images": [
                "https://images.neopets.com/trophies/42_3.gif",
                "https://images.neopets.com/trophies/42_2.gif",
                "https://images.neopets.com/trophies/42_1.gif"
            ]
        },
        {
            "name": "Swarm",
            "images": [
                "https://images.neopets.com/trophies/66_3.gif",
                "https://images.neopets.com/trophies/66_2.gif",
                "https://images.neopets.com/trophies/66_1.gif"
            ]
        },
        {
            "name": "Techo Says",
            "images": [
                "https://images.neopets.com/trophies/1_3.gif",
                "https://images.neopets.com/trophies/1_2.gif",
                "https://images.neopets.com/trophies/1_1.gif"
            ]
        },
        {
            "name": "The Haunted Shootery",
            "images": [
                "https://images.neopets.com/trophies/1114_3.gif",
                "https://images.neopets.com/trophies/1114_2.gif",
                "https://images.neopets.com/trophies/1114_1.gif"
            ]
        },
        {
            "name": "The Last Smiley",
            "images": [
                "https://images.neopets.com/trophies/576_3.gif",
                "https://images.neopets.com/trophies/576_2.gif",
                "https://images.neopets.com/trophies/576_1.gif"
            ]
        },
        {
            "name": "The Warehouse Of Lost Plushies",
            "images": [
                "https://images.neopets.com/trophies/322_3.gif",
                "https://images.neopets.com/trophies/322_2.gif",
                "https://images.neopets.com/trophies/322_1.gif"
            ]
        },
        {
            "name": "Theme Parks",
            "images": [
                "https://images.neopets.com/trophies/116_3.gif",
                "https://images.neopets.com/trophies/116_2.gif",
                "https://images.neopets.com/trophies/116_1.gif"
            ]
        },
        {
            "name": "Thinkway Toybox Escape",
            "images": [
                "https://images.neopets.com/trophies/236_3.gif",
                "https://images.neopets.com/trophies/236_2.gif",
                "https://images.neopets.com/trophies/236_1.gif"
            ]
        },
        {
            "name": "Tower of Turnips",
            "images": [
                "https://images.neopets.com/trophies/172_3.gif",
                "https://images.neopets.com/trophies/172_2.gif",
                "https://images.neopets.com/trophies/172_1.gif"
            ]
        },
        {
            "name": "Tug-O-War",
            "images": [
                "https://images.neopets.com/trophies/52_3.gif",
                "https://images.neopets.com/trophies/52_2.gif",
                "https://images.neopets.com/trophies/52_1.gif"
            ]
        },
        {
            "name": "Turmac Roll",
            "images": [
                "https://images.neopets.com/trophies/323_3.gif",
                "https://images.neopets.com/trophies/323_2.gif",
                "https://images.neopets.com/trophies/323_1.gif"
            ]
        },
        {
            "name": "Tyrannian Mini Golf",
            "images": [
                "https://images.neopets.com/trophies/395_3.gif",
                "https://images.neopets.com/trophies/395_2.gif",
                "https://images.neopets.com/trophies/395_1.gif"
            ]
        },
        {
            "name": "Uber Faerie Caves",
            "images": [
                "https://images.neopets.com/trophies/44_3.gif",
                "https://images.neopets.com/trophies/44_2.gif",
                "https://images.neopets.com/trophies/44_1.gif"
            ]
        },
        {
            "name": "Ultimate Bullseye",
            "images": [
                "https://images.neopets.com/trophies/152_3.gif",
                "https://images.neopets.com/trophies/152_2.gif",
                "https://images.neopets.com/trophies/152_1.gif"
            ]
        },
        {
            "name": "Usuki Frenzy",
            "images": [
                "https://images.neopets.com/trophies/129_3.gif",
                "https://images.neopets.com/trophies/129_2.gif",
                "https://images.neopets.com/trophies/129_1.gif"
            ]
        },
        {
            "name": "Volcano Run",
            "images": [
                "https://images.neopets.com/trophies/140_3.gif",
                "https://images.neopets.com/trophies/140_2.gif",
                "https://images.neopets.com/trophies/140_1.gif"
            ]
        }
    ];

    const spotlightsAndCompetitionsTrophies = [
        {
            "name": "Caption Competition",
            "images": [
                "https://images.neopets.com/trophies/100_3.gif",
                "https://images.neopets.com/trophies/100_2.gif",
                "https://images.neopets.com/trophies/100_1.gif"
            ]
        },
        {
            "name": "Gallery Spotlight",
            "images": [
                "https://images.neopets.com/trophies/222_1.gif"
            ]
        },
        {
            "name": "Lookup of the Week",
            "images": [
                "https://images.neopets.com/trophies/467_1.gif"
            ]
        },
        {
            "name": "Mystery Pic",
            "images": [
                "https://images.neopets.com/trophies/105_3.gif",
                "https://images.neopets.com/trophies/105_2.gif",
                "https://images.neopets.com/trophies/105_1.gif"
            ]
        },
        {
            "name": "Neopian Times",
            "images": [
                "https://images.neopets.com/trophies/107_1.gif"
            ]
        },
        {
            "name": "Picture Competition",
            "images": [
                "https://images.neopets.com/trophies/102_3.gif",
                "https://images.neopets.com/trophies/102_2.gif",
                "https://images.neopets.com/trophies/102_1.gif"
            ]
        },
        {
            "name": "Poetry Competition",
            "images": [
                "https://images.neopets.com/trophies/103_3.gif",
                "https://images.neopets.com/trophies/103_2.gif",
                "https://images.neopets.com/trophies/103_1.gif"
            ]
        },
        {
            "name": "Storytelling",
            "images": [
                "https://images.neopets.com/trophies/104_3.gif",
                "https://images.neopets.com/trophies/104_2.gif",
                "https://images.neopets.com/trophies/104_1.gif"
            ]
        }
    ];

    const TROPHY_TRACKER_CATEGORIES = Object.freeze([
        Object.freeze({ id: "flashgame", title: "Flash Game High Score Trophies", shortTitle: "Flash Games", trophies: flashgameHighScoreTrophies }),
        Object.freeze({ id: "cumulative", title: "Cumulative High Score Trophies", shortTitle: "Cumulativos", trophies: cumulativeHighScoreTrophies }),
        Object.freeze({ id: "other-high-score", title: "Other High Score Trophies", shortTitle: "Outros rankings", trophies: otherHighScoreTrophies }),
        Object.freeze({ id: "non-competitive", title: "Non Competitive Trophies", shortTitle: "Não competitivos", trophies: nonCompetitiveTrophies }),
        Object.freeze({ id: "spotlights", title: "Spotlights & Competitions Trophies", shortTitle: "Competições", trophies: spotlightsAndCompetitionsTrophies }),
        Object.freeze({ id: "pvp", title: "PVP Trophies (Currently Unobtainable)", shortTitle: "PVP", trophies: pvpTrophies }),
        Object.freeze({ id: "retired", title: "Retired Game Trophies", shortTitle: "Aposentados", trophies: retiredGameTrophies })
    ]);

    const STATE_SCHEMA = "ncc-aaa-state-v1";
    const EXTERNAL_STATE_SCHEMA = "ncc-aaa-external-state-v1";
    const DIAGNOSTIC_SCHEMA = "ncc-aaa-diagnostic-v1";
    const IDENTITY_SCHEMA = "ncc-aaa-identity-v1";

    const IDENTITY_KEY = "ncc.aaa.identity.v1";
    const LOCAL_GUEST_ID_KEY = "ncc.aaa.localGuestId.v1";
    const PLAYER_ID_KEY = "ncc.aaa.playerId.v1";
    const PLAYER_USERNAME_KEY = "ncc.aaa.username.v1";
    const LATEST_STATE_KEY = "ncc.aaa.latestState.v1";
    const STATE_KEY_PREFIX = "ncc.aaa.state.v1";
    const EXTERNAL_STATE_KEY = "ncc.aaa.externalState.v1";
    const EXTERNAL_STATE_EVENT = "ncc:aaa:external-state";
    const STATE_EVENT = "ncc:aaa:state";
    const GLOBAL_EXTERNAL_STATE_NAME = "NCC_AAA_EXTERNAL_STATE";
    const GLOBAL_BRIDGE_NAME = "NCCAAA";
    const EXTERNAL_STATE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
    const STORAGE_MANIFEST_KEY = "ncc.aaa.storageManifest.v1";
    const STORAGE_AUDIT_KEY = "ncc.aaa.storageAudit.v1";
    const VISUAL_PREFS_KEY_PREFIX = "ncc.aaa.visualPreferences.v1";
    const LAST_DIAGNOSTIC_KEY = "ncc.aaa.lastDiagnostic.v1";
    const LAST_HEALTH_REPORT_KEY = "ncc.aaa.lastHealthReport.v1";
    const LAST_SELF_TEST_KEY = "ncc.aaa.lastSelfTest.v1";
    const BASE_ENTRY_CONTRACT_KEY = "ncc.aaa.baseEntryContract.v1";
    const BASE_ENTRY_READINESS_KEY = "ncc.aaa.baseEntryReadiness.v1";
    const BASE_ENTRY_EVENT = "ncc:aaa:base-entry-ready";
    const GLOBAL_BASE_ENTRY_CONTRACT_NAME = "NCC_AAA_BASE_ENTRY_CONTRACT";
    const GLOBAL_BASE_ENTRY_READINESS_NAME = "NCC_AAA_BASE_ENTRY_READINESS";
    const TRAINING_STATE_SCHEMA = "ncc-aaa-training-state-v1";
    const TRAINING_EXTERNAL_STATE_KEY = "ncc.aaa.training.externalState.v1";
    const TRAINING_EXTERNAL_STATE_EVENT = "ncc:aaa:training-state";
    const GLOBAL_TRAINING_STATE_NAME = "NCC_AAA_TRAINING_STATE";
    const TRAINING_LOCAL_STATE_KEY = "ncc.aaa.training.readerState.v1";
    const TRAINING_READER_REPORT_KEY = "ncc.aaa.training.readerReport.v1";
    const TRAINING_PAGE_PANEL_ID = "ncc-aaa-training-page-panel";
    const TRAINING_PAGE_PANEL_STYLE_ID = "ncc-aaa-training-page-panel-style";
    const TRAINING_PAGE_PANEL_REPORT_KEY = "ncc.aaa.training.pagePanelReport.v1";
    const TRAINING_SDB_REPORT_KEY = "ncc.aaa.training.sdbReport.v1";
    const TRAINING_COURSE_ACTION_REPORT_KEY = "ncc.aaa.training.courseActionReport.v1";
    const TRAINING_BATCH_SUMMARY_KEY = "ncc.aaa.training.batchSummary.v1";
    const TRAINING_EQUIVALENCE_AUDIT_KEY = "ncc.aaa.training.equivalenceAudit.v1";
    const BATTLEDOME_STATE_SCHEMA = "ncc-aaa-battledome-tracker-state-v1";
    const BATTLEDOME_STORAGE_KEY = "ncc.aaa.battledome.tracker.v1";
    const BATTLEDOME_REPORT_KEY = "ncc.aaa.battledome.report.v1";
    const BATTLEDOME_EXTERNAL_STATE_KEY = "ncc.aaa.battledome.externalState.v1";
    const BATTLEDOME_EXTERNAL_STATE_EVENT = "ncc:aaa:battledome-state";
    const GLOBAL_BATTLEDOME_STATE_NAME = "NCC_AAA_BATTLEDOME_STATE";
    const BATTLEDOME_FIGHT_FAVORITE_STYLE_ID = "ncc-aaa-battledome-fight-favorite-style";
    const BATTLEDOME_PRIZE_OBSERVER_TARGET_ID = "bd_rewardsloot";
    const BATTLEDOME_LOADOUT_STYLE_ID = "ncc-aaa-battledome-loadout-style";
    const BATTLEDOME_LOADOUT_TOAST_ID = "ncc-aaa-battledome-loadout-toast";
    const BATTLEDOME_AUDIT_KEY = "ncc.aaa.battledome.audit.v1";
    const BATTLEDOME_MANUAL_TEST_PLAN_KEY = "ncc.aaa.battledome.manualTestPlan.v1";
    const BATTLEDOME_ASSISTED_TEST_KEY = "ncc.aaa.battledome.assistedTest.v1";
    const TRAINING_MODERN_SDB_ITEMS_ENDPOINT = "/np-templates/ajax/safetydeposit/get-items.php";
    const TRAINING_MODERN_SDB_MOVE_ENDPOINT = "/np-templates/ajax/safetydeposit/move-items.php";
    const TRAINING_LEGACY_SDB_MOVE_ENDPOINT = "/process_safetydeposit.phtml?checksub=scan";
    const CELLBLOCK_LATEST_KEY = "ncc.aaa.game.cellblock.latest.v1";
    const CELLBLOCK_EVENT = "ncc:aaa:game:cellblock";
    const CELLBLOCK_HINT_CLASS = "ncc-aaa-cellblock-hint";
    const CELLBLOCK_OVERLAY_CLASS = "ncc-aaa-cellblock-hint-overlay";
    const CELLBLOCK_STATUS_NOTE_ID = "ncc-aaa-cellblock-status-note";
    const KIKO_POP_FIX_ID = "ncc-aaa-kiko-pop-fix";
    const KIKO_POP_STYLE_ID = "ncc-aaa-kiko-pop-fix-style";
    const NEOQUEST_ONE_LATEST_KEY = "ncc.aaa.game.neoquest1.latest.v1";
    const NEOQUEST_ONE_EVENT = "ncc:aaa:game:neoquest1";
    const NEOQUEST_ONE_GM_PREFIX = "ncc.aaa.game.neoquest1.helper.";
    const NEOQUEST_TWO_LATEST_KEY = "ncc.aaa.game.neoquest2.latest.v1";
    const NEOQUEST_TWO_EVENT = "ncc:aaa:game:neoquest2";
    const NEOQUEST_TWO_GM_PREFIX = "ncc.aaa.game.neoquest2.helper.";

    const SECONDARY_STORAGE_WRITE_DELAY_MS = 350;
    const SESSION_ID = createSessionId();

    const CSS_THEME_COLOR_FALLBACKS = Object.freeze({
        primary: "#42a7dd",
        dark: "#154870",
        accent: "#ffe58a",
        text: "#0f2d45",
        soft: "#dff7ff"
    });
    const CSS_THEME_COLOR_KEYS = Object.freeze(["primary", "dark", "accent", "text", "soft"]);
    const CSS_SAFE_HEX_COLOR_RE = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;

    const FALLBACK_AVATAR_ID = "avinroo";
    const FALLBACK_TEAM_ID = "altador";
    const FALLBACK_THEME_ID = "winterholiday";
    const FALLBACK_VIEW_ID = "dashboard";

    /*
     * Core Organization v0.1.20:
     * O userscript continua em arquivo único, mas agora declara blocos internos
     * de responsabilidade e um registry vazio de módulos de Jogos. Isso prepara
     * o core para receber módulos futuros sem misturar template, storage e bridge.
     */
    const FOUNDATION = Object.freeze({
        schema: 100,
        version: VERSION,
        feature: "ncc-aaa-kiss-the-mortog-module-install",
        releaseStatus: "kiss-the-mortog-module-install-active",
        architectureId: "0.1.63-ncc-aaa",
        source: "ncc-aaa",
        sourceScript: "NCC AAA",
        sourceModule: "foundation",
        mountPage: MOUNT_PATH,
        legacyMountPage: LEGACY_MOUNT_PATH,
        supportedMountPages: MOUNT_PATHS,
        entryPoint: ENTRY_POINT,
        legacyEntryPoint: LEGACY_ENTRY_POINT,
        purpose: "future-games-and-arena-logic-host",
        targetBaseCategory: "Jogos/Arena",
        status: "ready-kiss-the-mortog-module-install-active",
        moduleCount: 6,
        migratedGameModules: Object.freeze(["cellblock", "kiko-pop", "neoquest-one", "neoquest-two", "tyranu-evavu", "kiss-the-mortog"]),
        visualOnly: false,
        mirrorOnly: false,
        bridgePublished: true,
        externalStatePublished: true,
        baseEntryContractPublished: true,
        baseMirrorReadinessPublished: true,
        stateStorageHardened: true,
        cssThemeToneSanitized: true,
        fullWidthMountFix: true,
        renderPartialEnabled: true,
        coreOrganized: true,
        emptyGameModuleRegistry: false,
        arenaFoundationReady: true,
        trainingContractPublished: true,
        trainingStateReaderEnabled: true,
        trainingOperationalEnabled: true,
        trainingSdbOperationsEnabled: true,
        trainingCourseActionsEnabled: true,
        battledomeOperationalEnabled: true,
        trophyTrackerOperationalEnabled: true,
        trophyTrackerCreditsPublished: true,
        tyranuEvavuAutoplayerEnabled: true,
        kissTheMortogAutoplayerEnabled: true,
        godoriAutoplayerEnabled: true,
        payloadPolicy: Object.freeze({
            slimFoundation: true,
            changelogInPayload: false,
            changelogLocation: "version-audit-report",
            previousTopLevelFoundationFields: 88,
            currentTopLevelFoundationFields: 31
        }),
        mountStrategy: Object.freeze({
            pageOnly: MOUNT_PATH,
            legacyPage: LEGACY_MOUNT_PATH,
            supportedPages: MOUNT_PATHS,
            replacesMainContent: true,
            fullWidthOnMountPage: true,
            preservesSiteChrome: true,
            petpageAnchor: true
        }),
        contracts: Object.freeze({
            externalStateSchema: EXTERNAL_STATE_SCHEMA,
            externalStateKey: EXTERNAL_STATE_KEY,
            communicationContract: "published-after-definition",
            baseEntryContractKey: BASE_ENTRY_CONTRACT_KEY,
            baseEntryReadinessKey: BASE_ENTRY_READINESS_KEY,
            trainingStateSchema: TRAINING_STATE_SCHEMA,
            trainingExternalStateKey: TRAINING_EXTERNAL_STATE_KEY,
            trainingExternalStateEvent: TRAINING_EXTERNAL_STATE_EVENT,
            trainingGlobalStateName: GLOBAL_TRAINING_STATE_NAME,
            battledomeExternalStateKey: BATTLEDOME_EXTERNAL_STATE_KEY,
            battledomeExternalStateEvent: BATTLEDOME_EXTERNAL_STATE_EVENT,
            battledomeGlobalStateName: GLOBAL_BATTLEDOME_STATE_NAME,
            trophyTrackerStateSchema: TROPHY_TRACKER_STATE_SCHEMA,
            trophyTrackerReportSchema: TROPHY_TRACKER_REPORT_SCHEMA,
            trophyTrackerReportKey: TROPHY_TRACKER_REPORT_KEY,
            coreOrganizationSchema: "ncc-aaa-core-organization-v1"
        }),
        trainingContract: Object.freeze({
            schema: TRAINING_STATE_SCHEMA,
            externalStateKey: TRAINING_EXTERNAL_STATE_KEY,
            event: TRAINING_EXTERNAL_STATE_EVENT,
            globalName: GLOBAL_TRAINING_STATE_NAME,
            stateReaderEnabled: true,
            pagePanelEnabled: true,
            neutralPanelOnly: false,
            operationalEnabled: true,
            sdbOperationsEnabled: true,
            courseActionsEnabled: true,
            officialActionAvailable: true
        }),
        safety: Object.freeze({
            automaticOfficialAction: false,
            automaticGameAction: "conditional-user-toggle",
            clickAutomationAdded: "session-toggle-autoplayers-only",
            keyboardAutomationAdded: false,
            formSubmitAdded: "session-toggle-autoplayers-only",
            networkAccessAdded: "sdb-and-course-actions-after-user-click-confirmation",
            storageDeletion: false,
            nccBaseChanged: false,
            nccRpgChanged: false,
            automationDisclosure: "Tyranu Evavu, Kiss The Mortog e Godori podem clicar/submeter apenas nas próprias páginas, após botão flutuante ligado na sessão."
        }),
        renderPolicy: Object.freeze({
            shellRenderOnBoot: true,
            partialViewSwitch: true,
            preservesWindowScroll: true,
            preservesFocusedAction: true,
            fallbackToFullRender: true
        }),
        nextStage: "manual-neopets-page-testing"
    });

    const CORE_AREAS = Object.freeze([
        Object.freeze({ id: "boot", label: "Boot", responsibility: "montagem segura da página e fallback visual" }),
        Object.freeze({ id: "identity", label: "Identity", responsibility: "detecção de usuário, playerId e chaves por conta" }),
        Object.freeze({ id: "state", label: "State", responsibility: "estado imutável, revision e sessão por aba" }),
        Object.freeze({ id: "storage", label: "Storage", responsibility: "persistência isolada em ncc.aaa.*" }),
        Object.freeze({ id: "bridge", label: "Bridge", responsibility: "API pública window.NCCAAA e unsafeWindow.NCCAAA" }),
        Object.freeze({ id: "external-state", label: "External State", responsibility: "estado público para o NCC Base espelhar" }),
        Object.freeze({ id: "templates", label: "Templates", responsibility: "markup visual e views internas" }),
        Object.freeze({ id: "styles", label: "Styles", responsibility: "CSS injetado e tokens sanitizados" }),
        Object.freeze({ id: "events", label: "Events", responsibility: "delegação de ações visuais permitidas" }),
        Object.freeze({ id: "diagnostics", label: "Diagnostics", responsibility: "health, self-test e backstage técnico" }),
        Object.freeze({ id: "game-registry", label: "Game Registry", responsibility: "registry com Cellblock Helper, Kiko Pop Fix, NeoQuest I, NeoQuest II, Tyranu Evavu, Kiss The Mortog e Godori integrados" }),
        Object.freeze({ id: "arena-registry", label: "Arena Registry", responsibility: "setor Arena com módulos de treino e combate separados da Central de Módulos" }),
        Object.freeze({ id: "training-state-reader", label: "Training State Reader", responsibility: "leitura read-only das academias e publicação de estado externo sem ações oficiais" }),
        Object.freeze({ id: "training-course-actions", label: "Training Course Actions", responsibility: "pagamento, conclusão e início de cursos apenas por clique real e confirmação" }),
        Object.freeze({ id: "battledome-tracker", label: "Battledome Tracker", responsibility: "favoritos, logger e loadout visual/local sem ações oficiais de batalha" }),
        Object.freeze({ id: "trophy-tracker", label: "Trophy Tracker", responsibility: "leitura local de /prizes.phtml, catálogo de troféus e créditos GPL preservados" }),
        Object.freeze({ id: "tyranu-evavu", label: "Tyranu Evavu", responsibility: "autoplayer com botão flutuante liga/desliga, padrão desligado e atuação apenas na página oficial" }),
        Object.freeze({ id: "kiss-the-mortog", label: "Kiss The Mortog", responsibility: "autoplayer com painel flutuante, alvo de NP e atuação apenas nas páginas oficiais do jogo" }),
        Object.freeze({ id: "godori", label: "Godori", responsibility: "autoplayer com safe core, avatar helper, recovery e atuação apenas na página oficial do jogo" })
    ]);

    const GAME_MODULE_REGISTRY = Object.freeze([
        Object.freeze({
            id: "cellblock",
            name: "Cellblock Helper",
            iconUrl: CELLBLOCK_ICON_URL,
            status: "active",
            type: "visual-helper",
            pageOnly: CELLBLOCK_PATH,
            href: CELLBLOCK_URL,
            description: "Lê o tabuleiro e colore sugestões. Sem clique, sem submit e sem jogar pelo usuário."
        }),
        Object.freeze({
            id: "kiko-pop",
            name: "Kiko Pop Fix",
            iconUrl: KIKO_POP_ICON_URL,
            status: "active",
            type: "manual-fix",
            pageOnly: KIKO_POP_PATH,
            href: KIKO_POP_URL,
            description: "Substitui a etapa Flash por botões HTML manuais; usa setDifficulty original quando disponível e só pede prêmio após clique do usuário."
        }),

        Object.freeze({
            id: "tyranu-evavu",
            name: "Tyranu Evavu Autoplayer",
            iconUrl: TYRANU_EVAVU_ICON_URL,
            status: "toggle-controlled",
            type: "autoplayer-explicit-toggle",
            pageOnly: TYRANU_EVAVU_PATH,
            href: TYRANU_EVAVU_URL,
            description: "Conta cartas, decide Tyranu/Evavu e joga apenas quando o botão flutuante estiver ligado. Padrão desligado."
        }),
        Object.freeze({
            id: "kiss-the-mortog",
            name: "Kiss The Mortog Autoplayer",
            iconUrl: KISS_THE_MORTOG_ICON_URL,
            status: "toggle-controlled",
            type: "autoplayer-explicit-toggle",
            pageOnly: KISS_THE_MORTOG_PLAY_PATH,
            href: KISS_THE_MORTOG_URL,
            description: "Escolhe Mortogs, continua a rodada e coleta ao atingir a meta definida. Padrão desligado e controle por sessão."
        }),
        Object.freeze({
            id: "godori-autoplayer",
            name: "Godori Autoplayer",
            iconUrl: GODORI_ICON_URL,
            status: "toggle-controlled",
            type: "autoplayer-explicit-toggle",
            pageOnly: GODORI_PATH_PREFIX,
            href: GODORI_URL,
            description: "Joga Godori com Safe Core, estratégia de cartas, Avatar Helper e recovery. Padrão desligado e controle por sessão."
        }),
        Object.freeze({
            id: "neoquest-one",
            name: "NeoQuest I Helper",
            iconUrl: NEOQUESTS_ICON_URL,
            status: "active",
            type: "guide-helper",
            pageOnly: NEOQUEST_ONE_PATH,
            href: NEOQUEST_ONE_URL,
            description: "Integra o helper NeoQuest.Guide: objetivos por nível, informações de combate e atalhos de teclado apenas na página do NeoQuest I."
        }),
        Object.freeze({
            id: "neoquest-two",
            name: "NeoQuest II Helper",
            iconUrl: NEOQUESTS_ICON_URL,
            status: "active",
            type: "guide-helper",
            pageOnly: NEOQUEST_TWO_PATH,
            href: NEOQUEST_TWO_URL,
            description: "Integra o helper NeoQuest.Guide: atalhos de mapa/ação, marcação de alvo/ação e leitura simples de combate apenas na página do NeoQuest II."
        })
    ]);

    const CORE_ORGANIZATION = Object.freeze({
        schema: "ncc-aaa-core-organization-v1",
        version: VERSION,
        source: "ncc-aaa",
        status: "organized-core-with-arena-battledome-and-trophy-tracker",
        fileMode: "single-userscript",
        buildSystemRequired: false,
        sectionCount: CORE_AREAS.length,
        sections: CORE_AREAS,
        templatePolicy: Object.freeze({
            templatesSeparatedByFunction: true,
            renderPartialPreserved: true,
            shellRenderFallbackPreserved: true
        }),
        moduleRegistry: Object.freeze({
            ready: true,
            realModules: true,
            total: GAME_MODULE_REGISTRY.length,
            items: GAME_MODULE_REGISTRY,
            acceptsFutureModules: true,
            requiredModulePolicy: "mixed-manual-visual-and-session-toggle-autoplayer-no-background-official-actions"
        }),
        operationalAreas: Object.freeze({
            gameModules: GAME_MODULE_REGISTRY.length,
            arenaModules: 3,
            trainingAssistant: true,
            battledomeTracker: true,
            trophyTracker: true,
            tyranuEvavuAutoplayer: true,
            kissTheMortogAutoplayer: true,
            godoriAutoplayer: true,
            diagnosticsCreditsUxFinal: true,
            petpageMountMigration: true
        }),
        boundaries: Object.freeze({
            nccBaseChanged: false,
            nccRpgChanged: false,
            networkAccessAdded: "training-sdb-after-user-confirmation",
            officialClickAutomationAdded: "session-toggle-autoplayers-only",
            keyboardAutomationAdded: false,
            formSubmitAdded: "session-toggle-autoplayers-only"
        })
    });

    const COMMUNICATION_CONTRACT = Object.freeze({
        schema: "ncc-aaa-communication-contract-v1",
        version: VERSION,
        source: "ncc-aaa",
        sourceScript: "NCC AAA",
        mountPage: MOUNT_PATH,
        legacyMountPage: LEGACY_MOUNT_PATH,
        supportedMountPages: MOUNT_PATHS,
        entryPoint: ENTRY_POINT,
        legacyEntryPoint: LEGACY_ENTRY_POINT,
        externalStateSchema: EXTERNAL_STATE_SCHEMA,
        externalStateKey: EXTERNAL_STATE_KEY,
        globalBridgeName: GLOBAL_BRIDGE_NAME,
        globalExternalStateName: GLOBAL_EXTERNAL_STATE_NAME,
        globalBaseEntryContractName: GLOBAL_BASE_ENTRY_CONTRACT_NAME,
        globalBaseEntryReadinessName: GLOBAL_BASE_ENTRY_READINESS_NAME,
        events: Object.freeze([EXTERNAL_STATE_EVENT, STATE_EVENT, BASE_ENTRY_EVENT, TRAINING_EXTERNAL_STATE_EVENT]),
        mirrorOnly: false,
        futureConsumer: "NCC Base",
        baseCategory: "Jogos",
        baseEntryContractSchema: "ncc-aaa-base-entry-contract-v1",
        baseEntryContractKey: BASE_ENTRY_CONTRACT_KEY,
        baseEntryReadinessKey: BASE_ENTRY_READINESS_KEY,
        suggestedBaseCard: Object.freeze({
            title: "NCC AAA · Jogos",
            subtitle: "Central auxiliar pronta",
            actionLabel: "Abrir NCC AAA",
            href: ENTRY_POINT,
            targetCategory: "Jogos",
            mirrorMode: "external-state-only",
            icon: NCC_AAA_ICON_URL
        }),
        channels: Object.freeze({
            localStorage: true,
            windowGlobal: true,
            unsafeWindowGlobal: true,
            windowEvent: true,
            unsafeWindowEvent: true,
            documentEvent: true
        }),
        safety: Object.freeze({
            automaticOfficialAction: false,
            automaticGameAction: "conditional-user-toggle",
            clickAutomation: "neoquest-user-keypress-and-session-toggle-autoplayers",
            keyboardAutomation: "neoquest-map-shortcuts-only",
            formSubmit: "autoplayers-only-after-session-toggle",
            networkAccess: "kiko-pop-jquery-and-neoquest2-user-keypress-xhr-plus-training-sdb-after-user-confirmation",
            automationModules: Object.freeze({
                tyranuEvavu: Object.freeze({
                    pageOnly: TYRANU_EVAVU_PATH,
                    enabledByDefault: false,
                    sessionOnlyToggle: true,
                    canClickGameAction: true,
                    canSubmitGameForm: true
                }),
                kissTheMortog: Object.freeze({
                    pageOnly: KISS_THE_MORTOG_PLAY_PATH,
                    processPage: KISS_THE_MORTOG_PROCESS_PATH,
                    enabledByDefault: false,
                    sessionOnlyToggle: true,
                    canClickGameAction: true,
                    canSubmitGameForm: true,
                    maxSafeScore: 5900
                }),
                godori: Object.freeze({
                    pageOnly: GODORI_PATH_PREFIX,
                    enabledByDefault: false,
                    sessionOnlyToggle: true,
                    canClickGameAction: true,
                    canSubmitGameForm: true,
                    smartAlertHandler: true,
                    avatarHelperTargetHands: 250
                })
            })
        })
    });

    const AAA_AVATARS = Object.freeze([
        { id: "avinroo", label: "NCC AAA", imageUrl: NCC_AAA_ICON_URL },
        { id: "sketch-lupe", label: "Lupe Sketch", imageUrl: "https://images.neopets.com/backgrounds/sketch/150_lupe.gif" },
        { id: "sketch-shoyru", label: "Shoyru Sketch", imageUrl: "https://images.neopets.com/backgrounds/sketch/150_shoyru.gif" },
        { id: "sketch-aisha", label: "Aisha Sketch", imageUrl: "https://images.neopets.com/backgrounds/sketch/150_aisha.gif" },
        { id: "sketch-krawk", label: "Krawk Sketch", imageUrl: "https://images.neopets.com/backgrounds/sketch/150_krawk.gif" },
        { id: "sketch-fyora", label: "Fyora Sketch", imageUrl: "https://images.neopets.com/backgrounds/sketch/150_fyora.gif" },
        { id: "sketch-symol", label: "Symol Sketch", imageUrl: "https://images.neopets.com/backgrounds/sketch/150_symol.gif" },
        { id: "sketch-turmac", label: "Turmac Sketch", imageUrl: "https://images.neopets.com/backgrounds/sketch/150_turmac.gif" },
        { id: "sketch-xweetok", label: "Xweetok Sketch", imageUrl: "https://images.neopets.com/backgrounds/sketch/150_xweetok.gif" }
    ]);

    const TEAMS = Object.freeze([
        { id: "altador", name: "Altador", icon: "☀️" },
        { id: "hauntedwoods", name: "Haunted Woods", icon: "🌙" },
        { id: "faerieland", name: "Faerieland", icon: "🪽" },
        { id: "darigancitadel", name: "Darigan Citadel", icon: "🛡️" },
        { id: "brightvale", name: "Brightvale", icon: "📚" },
        { id: "lostdesert", name: "Lost Desert", icon: "🏜️" },
        { id: "kreludor", name: "Kreludor", icon: "🌕" },
        { id: "krawkisland", name: "Krawk Island", icon: "🏴‍☠️" },
        { id: "kikolake", name: "Kiko Lake", icon: "💧" },
        { id: "mysteryisland", name: "Mystery Island", icon: "🌴" },
        { id: "tyrannia", name: "Tyrannia", icon: "🦴" },
        { id: "moltara", name: "Moltara", icon: "🔥" },
        { id: "terrormountain", name: "Terror Mountain", icon: "❄️" },
        { id: "meridell", name: "Meridell", icon: "🌾" },
        { id: "shenkuu", name: "Shenkuu", icon: "🎐" },
        { id: "maraqua", name: "Maraqua", icon: "🌊" },
        { id: "rooisland", name: "Roo Island", icon: "🎡" },
        { id: "virtupets", name: "Virtupets", icon: "🚀" }
    ]);

    const THEMES = Object.freeze([
        { id: "winterholiday", label: "Winter Holiday" },
        { id: "basic", label: "Basic" },
        { id: "premium", label: "Premium" },
        { id: "altadorcup", label: "Altador Cup" },
        { id: "constellations", label: "Constellations" },
        { id: "birthday", label: "Birthday" },
        { id: "grey", label: "Grey Day" },
        { id: "hauntedwoods", label: "Haunted Woods" },
        { id: "meridell", label: "Meridell" },
        { id: "mysteryisland", label: "Mystery Island" },
        { id: "tyrannia", label: "Tyrannia" },
        { id: "valentines", label: "Valentines" }
    ]);

    const THEME_TONES = Object.freeze({
        winterholiday: { primary: "#42a7dd", dark: "#154870", accent: "#ffe58a", text: "#0f2d45", soft: "#dff7ff" },
        basic: { primary: "#5377b8", dark: "#31487a", accent: "#ffdf6e", text: "#172033", soft: "#e8f1ff" },
        premium: { primary: "#5b45a6", dark: "#2f255f", accent: "#f6d26c", text: "#1f173d", soft: "#eee9ff" },
        altadorcup: { primary: "#d9972a", dark: "#533012", accent: "#ffe48c", text: "#24160a", soft: "#fff2cf" },
        constellations: { primary: "#4656bd", dark: "#151c55", accent: "#d8f0ff", text: "#11183a", soft: "#e7ecff" },
        birthday: { primary: "#f2a43b", dark: "#975225", accent: "#fff0a8", text: "#3e2411", soft: "#fff2d9" },
        grey: { primary: "#7b7b86", dark: "#42434e", accent: "#e8e8ef", text: "#24242d", soft: "#ededf3" },
        hauntedwoods: { primary: "#5e4565", dark: "#25182d", accent: "#b8e46a", text: "#26192b", soft: "#eee6f1" },
        meridell: { primary: "#6a9b39", dark: "#36521d", accent: "#ffe17a", text: "#203213", soft: "#f0ffdf" },
        mysteryisland: { primary: "#2d9c91", dark: "#14524c", accent: "#ffdf62", text: "#103530", soft: "#e1fff9" },
        tyrannia: { primary: "#a4763e", dark: "#4b311a", accent: "#ffd18a", text: "#2d1c0d", soft: "#fff0d9" },
        valentines: { primary: "#d94d92", dark: "#7c2451", accent: "#ffe0ef", text: "#4c1631", soft: "#ffe7f3" }
    });

    const VIEWS = Object.freeze([
        { id: "dashboard", label: "Painel", icon: "🏰" },
        { id: "games", label: "Módulos", icon: "🧩" },
        { id: "arena", label: "Arena", icon: "⚔️" },
        { id: "profile", label: "Trophy Tracker", icon: "🏆" },
        { id: "diagnostic", label: "Diagnóstico", icon: "🧭" }
    ]);

    const PLANNED_GAME_SLOTS = Object.freeze([
        {
            id: "cellblock",
            name: "Cellblock Helper",
            icon: "♟️",
            iconUrl: CELLBLOCK_ICON_URL,
            status: "ativo",
            role: "Leitura e apoio visual",
            futureSource: "NCC AAA",
            note: "Ativo: roda somente na página oficial do Cellblock e colore sugestões."
        },
        {
            id: "kiko-pop",
            name: "Kiko Pop Fix",
            icon: "🎯",
            iconUrl: KIKO_POP_ICON_URL,
            status: "ativo",
            role: "Fix manual de Flash",
            futureSource: "NCC AAA",
            note: "Ativo: roda somente na página oficial do Kiko Pop; tenta setDifficulty original e cria botões manuais."
        },
        {
            id: "dicearoo",
            name: "Dice-A-Roo",
            icon: "🎲",
            iconUrl: DICEAROO_ICON_URL,
            status: "planejado",
            role: "Atalhos controlados",
            futureSource: "NCC Base · Jogos",
            note: "Só entra depois de contrato manual/seguro para teclado."
        },
        {
            id: "neoquest-one",
            name: "NeoQuest I Helper",
            icon: "🗺️",
            iconUrl: NEOQUESTS_ICON_URL,
            status: "ativo",
            role: "Guia e atalhos de teclado",
            futureSource: "NCC AAA",
            note: "Ativo: roda somente no NeoQuest I; mostra objetivos e informações de combate. Atalhos são por tecla do usuário."
        },
        {
            id: "neoquest-two",
            name: "NeoQuest II Helper",
            icon: "⚔️",
            iconUrl: NEOQUESTS_ICON_URL,
            status: "ativo",
            role: "Guia e atalhos de teclado",
            futureSource: "NCC AAA",
            note: "Ativo: roda somente no NeoQuest II; marca alvo/ação e mantém atalhos por tecla do usuário."
        },
        {
            id: "high-score-tools",
            name: "Trophy / High Score Helpers",
            icon: "🏆",
            status: "planejado",
            role: "Resumo e acompanhamento",
            futureSource: "NCC Base · Jogos",
            note: "Somente leitura/visual quando entrar."
        },
        {
            id: "future-games",
            name: "Outros Jogos",
            icon: "⭐",
            status: "reservado",
            role: "Slot de expansão",
            futureSource: "NCC AAA",
            note: "Espaço reservado para módulos menores."
        }
    ]);



    const TRAINING_SCHOOLS = Object.freeze({
        pirate: Object.freeze({
            id: "pirate",
            name: "Swashbuckling Academy",
            shortName: "Swashbuckling",
            statusPath: TRAINING_PIRATE_PATH,
            processPath: "/pirates/process_academy.phtml",
            href: TRAINING_PIRATE_URL,
            currency: "Dubloon Coin",
            icon: "https://images.neopets.com/items/dubloon5.gif",
            maxLevel: 40,
            sdbMode: "manual-search",
            operationalEnabled: true
        }),
        mystery: Object.freeze({
            id: "mystery",
            name: "Mystery Island Training School",
            shortName: "Mystery Island",
            statusPath: TRAINING_MYSTERY_PATH,
            processPath: "/island/process_training.phtml",
            href: TRAINING_MYSTERY_URL,
            currency: "Codestone",
            icon: TRAINING_ICON_URL,
            maxLevel: 250,
            sdbMode: "modern-ajax-plus-legacy-fallback",
            operationalEnabled: true
        }),
        ninja: Object.freeze({
            id: "ninja",
            name: "Secret Ninja Training School",
            shortName: "Secret Ninja",
            statusPath: TRAINING_NINJA_PATH,
            processPath: "/island/process_fight_training.phtml",
            href: TRAINING_NINJA_URL,
            currency: "Red Codestone",
            icon: "https://images.neopets.com/items/codestone15.gif",
            maxLevel: Infinity,
            sdbMode: "modern-ajax-plus-legacy-fallback",
            operationalEnabled: true
        })
    });

    const BATTLEDOME_TRACKER_CONTRACT = Object.freeze({
        schema: BATTLEDOME_STATE_SCHEMA,
        version: VERSION,
        moduleId: "battledomeTracker",
        title: "Battledome Tracker & Fav",
        source: "NCC 4.7.1 Battledome Tracker & Fav handoff",
        safetyClass: "visual-helper/client-local-assist",
        activationPolicy: "opponent-favorites-prize-logger-loadout-final-audit-consumer-ui-surface-cleanup-v0.1.63",
        allowedPaths: Object.freeze([BATTLEDOME_HOME_PATH]),
        pages: Object.freeze({
            home: BATTLEDOME_HOME_PATH,
            opponents: BATTLEDOME_FIGHT_PATH,
            arena: BATTLEDOME_ARENA_PATH,
            record: BATTLEDOME_RECORD_PATH,
            equipment: BATTLEDOME_EQUIPMENT_PATH
        }),
        officialActions: Object.freeze({
            startsFight: false,
            choosesOpponent: false,
            usesAbility: false,
            collectsPrize: false,
            submitsForm: false,
            postRequests: false,
            clicksFight: false,
            clientLocalWeaponPreselection: true
        }),
        stageOneRuntime: Object.freeze({
            observerEnabled: true,
            opponentFavoritesEnabled: true,
            prizeLoggerEnabled: true,
            loadoutEnabled: true,
            networkAccess: "none",
            formSubmit: false,
            programmaticClick: false,
            pageDomMutation: "favorite-stars-visual-reordering-local-prize-observer-and-client-local-weapon-preselection",
            loadoutPolicy: "max-two-weapons-local-fields-only-no-fight-click"
        })
    });

    const TRAINING_PAYMENT_ITEM_IDS = Object.freeze({
        "Mau Codestone": "7458",
        "Tai-Kai Codestone": "7459",
        "Lu Codestone": "7460",
        "Vo Codestone": "7461",
        "Eo Codestone": "7462",
        "Main Codestone": "7463",
        "Zei Codestone": "7464",
        "Orn Codestone": "7465",
        "Har Codestone": "7466",
        "Bri Codestone": "7467",
        "Mag Codestone": "22208",
        "Vux Codestone": "22209",
        "Cui Codestone": "22210",
        "Kew Codestone": "22211",
        "Sho Codestone": "22212",
        "Zed Codestone": "22213"
    });

    const ARENA_MODULE_REGISTRY = Object.freeze([
        Object.freeze({
            id: "training-assistant",
            name: "Training Assistant / Academias",
            icon: "🏋️",
            iconUrl: TRAINING_ICON_URL,
            status: "course-actions-audited",
            type: "confirmed-write",
            scope: "training-pages",
            operationalEnabled: true,
            officialActionAvailable: true,
            stateOnly: false,
            stateReaderEnabled: true,
            pagePanelEnabled: true,
            sdbOperationsEnabled: true,
            courseActionsEnabled: true,
            sourceMaterial: "Training Assistant handoff from NCC 4.7.1",
            description: "Painel operacional nas academias: SDB, pagamento, conclusão e início de cursos por clique real e confirmação."
        }),
        Object.freeze({
            id: "battledome-tracker",
            name: "Battledome Tracker & Fav",
            icon: "⚔️",
            status: "loadout-audited-active",
            type: "visual-helper/client-local-assist",
            scope: "battledome-pages",
            operationalEnabled: true,
            foundationInstalled: true,
            officialActionAvailable: false,
            stateOnly: false,
            opponentFavoritesEnabled: true,
            prizeLoggerEnabled: true,
            loadoutEnabled: true,
            sourceMaterial: "Battledome Tracker & Fav handoff from NCC 4.7.1",
            description: "Favoritos de oponentes, logger local de recompensas visíveis e loadout visual de até 2 armas ativos sem ação oficial de batalha."
        }),
        Object.freeze({
            id: "training-state-reader",
            name: "Training State Reader",
            icon: "⏱️",
            iconUrl: TRAINING_ICON_URL,
            status: "reader-active",
            type: "read-only-page-reader",
            scope: "training-status-pages",
            operationalEnabled: false,
            officialActionAvailable: false,
            stateOnly: true,
            stateReaderEnabled: true,
            description: "Lê as páginas de status das academias, captura timers e publica estado externo seguro para mirror."
        })
    ]);

    const PLANNED_ARENA_SLOTS = Object.freeze([
        Object.freeze({
            id: "training-assistant",
            name: "Training Assistant / Academias",
            icon: "🏋️",
            iconUrl: TRAINING_ICON_URL,
            status: "ações auditadas",
            role: "Treino e cursos",
            note: "Lê páginas de status, injeta painel e permite SDB, pagamento, conclusão e início de cursos por clique real e confirmação."
        }),
        Object.freeze({
            id: "training-state",
            name: "Training State Reader",
            icon: "⏱️",
            iconUrl: TRAINING_ICON_URL,
            status: "leitor ativo",
            role: "Estado externo",
            note: "Captura estado read-only das academias e publica window.NCC_AAA_TRAINING_STATE, localStorage próprio e evento ncc:aaa:training-state."
        }),
        Object.freeze({
            id: "battledome-tracker",
            name: "Battledome Tracker & Fav",
            icon: "⚔️",
            status: "loadout auditado",
            role: "Combate visual/local",
            note: "Favoritos de oponentes, logger local e loadout visual de até 2 armas ativos. Não clica em Fight, não escolhe oponente e não envia ação."
        }),
        Object.freeze({
            id: "arena-expansion",
            name: "Expansões da Arena",
            icon: "⭐",
            status: "reservado",
            role: "Expansão",
            note: "Espaço para futuros cards de treino, arena e integração com o NCC Base."
        })
    ]);

    const TRAINING_EQUIVALENCE_AUDIT = Object.freeze({
        schema: "ncc-aaa-training-equivalence-audit-v1",
        version: VERSION,
        source: "ncc-aaa",
        sourceModule: "training-equivalence-audit",
        auditedSource: "NCC AAA v0.1.36 Course Official Actions",
        status: "passed-static-audit",
        scope: "ncc-aaa-only",
        nccBaseChanged: false,
        reviewedStages: Object.freeze([
            "arena-foundation",
            "training-state-reader",
            "training-page-panel",
            "sdb-operations",
            "course-official-actions"
        ]),
        compatibilityTargets: Object.freeze([
            "Swashbuckling Academy",
            "Mystery Island Training School",
            "Secret Ninja Training School",
            "SDB moderno",
            "SDB legado"
        ]),
        confirmedCapabilities: Object.freeze([
            "Arena separada de Módulos",
            "estado externo ncc-aaa-training-state-v1",
            "painel de academia por página",
            "itens de pagamento por pet sem escanear tabela inteira",
            "Codestones e Red Codestones via SDB com confirmação",
            "Dubloons em fluxo manual assistido",
            "pagamento, conclusão e início por clique real e confirmação",
            "ações em lote com delay seguro",
            "PIN do SDB apenas em memória de sessão"
        ]),
        safetyChecks: Object.freeze({
            automaticOfficialAction: false,
            withdrawSdbOnBoot: false,
            payCourseOnBoot: false,
            completeCourseOnBoot: false,
            startCourseOnBoot: false,
            requiresUserClickForOfficialActions: true,
            requiresConfirmationForOfficialActions: true,
            permanentPinStorage: false,
            officialActionHandlers: "panel-click-only",
            bootOfficialNetwork: false,
            nccBaseTouched: false
        }),
        staticFindings: Object.freeze([
            "Boot chama apenas leitor, painel e publicação de estado; ações oficiais permanecem nos handlers do painel.",
            "SDB moderno e legado estão presentes e isolados em funções chamadas por ações confirmadas.",
            "Course actions preservam confirmação antes de pagar, completar e iniciar cursos.",
            "A tela da Arena foi ajustada para refletir que o Training está operacional/auditado.",
            "A bridge expõe relatório de SDB, relatório de ações de curso e relatório de auditoria."
        ]),
        manualTestRequired: Object.freeze([
            "Testar cada academia em conta real antes de remover qualquer mirror antigo de outro script.",
            "Testar SDB moderno com item suficiente, item ausente e PIN exigido.",
            "Testar fallback legado apenas se o SDB moderno falhar ou não expuser _ref_ck.",
            "Testar pagar/completar/iniciar com um pet antes de usar ações em lote."
        ])
    });

    let state = null;
    let identity = null;
    let storageKey = "";
    let visualPreferencesKey = "";
    let root = null;
    let mountedTarget = null;
    let lastPublishedAt = null;
    let lastRenderError = "";
    let lastStorageSyncAt = null;
    let storageSyncListenerRegistered = false;
    let lastRenderMode = "none";
    let lastRenderReason = "boot";
    let lastFullRenderAt = null;
    let lastPartialRenderAt = null;
    let partialRenderCount = 0;
    let fullRenderCount = 0;
    let lastCellblockReport = null;
    let lastKikoPopReport = null;
    let lastNeoQuestReport = null;
    let lastNeoQuestTwoReport = null;
    let lastTyranuEvavuReport = null;
    let lastKissTheMortogReport = null;
    let lastGodoriReport = null;
    let lastTrainingReaderReport = null;
    let lastTrainingPagePanelReport = null;
    let lastTrainingSdbReport = null;
    let lastTrainingCourseActionReport = null;
    let lastBattledomeTrackerReport = null;
    let lastTrophyTrackerReport = null;
    let battledomeFightFavoriteClickBound = false;
    let battledomePrizeObserver = null;
    let battledomePrizeObserverStarted = false;
    let battledomePrizeObserverRetryCount = 0;
    let battledomeLoadoutClickBound = false;
    let battledomeLoadoutAppliedThisArena = false;
    let trainingSessionPin = "";
    let trainingSessionSdbContext = null;
    const bootErrors = [];
    const debouncedStorageWrites = new Map();

    boot();

    function boot() {
        try {
            identity = detectIdentity();
            state = loadStateForIdentity(identity);
            registerStorageSyncListener();
            writeStorageManifest("boot");
            writeBaseEntryContract("boot");
            writeBaseEntryReadiness("boot");
            exposeBridge();
            bootTrainingStateReader("boot");
            injectTrainingPagePanel("boot");
            publishExternalState("boot");
            publishTrainingExternalState("boot");
            publishBattledomeExternalState("boot");
            bootBattledomeOpponentFavorites("boot");
            bootBattledomePrizeLogger("boot");
            bootBattledomeLoadout("boot");
            bootTrophyTrackerOnPrizes("boot");
            if (isCellblockPage()) bootCellblockHelper();
            if (isKikoPopPage()) bootKikoPopFix();
            if (isNeoQuestOnePage()) bootNeoQuestGuideHelper();
            if (isNeoQuestTwoPage()) bootNeoQuestTwoGuideHelper();
            if (isTyranuEvavuPage()) bootTyranuEvavuAutoplayer("boot");
            if (isKissTheMortogPage()) bootKissTheMortogAutoplayer("boot");
            if (isGodoriAutoplayerPage()) bootGodoriAutoplayer("boot");
            if (!isMountPage()) return;
            mount();
        } catch (error) {
            bootErrors.push(errorToMessage(error));
            publishExternalState("boot-error");
            publishTrainingExternalState("boot-error");
            publishBattledomeExternalState("boot-error");
            console.warn("[NCC AAA] Falha no boot.", error);
        }
    }

    function normalizePagePath(path) {
        return String(path || "").trim().toLowerCase();
    }

    function isPrimaryMountPage(path) {
        return normalizePagePath(path || location.pathname) === normalizePagePath(MOUNT_PATH);
    }

    function isLegacyMountPage(path) {
        return normalizePagePath(path || location.pathname) === normalizePagePath(LEGACY_MOUNT_PATH);
    }

    function isMountPage(path) {
        const normalized = normalizePagePath(path || location.pathname);
        return MOUNT_PATHS.some(candidate => normalizePagePath(candidate) === normalized);
    }



    function isCellblockPage() {
        return location.pathname === CELLBLOCK_PATH;
    }

    function isKikoPopPage() {
        return location.pathname === KIKO_POP_PATH;
    }

    function isNeoQuestOnePage() {
        return location.pathname === NEOQUEST_ONE_PATH;
    }

    function isNeoQuestTwoPage() {
        return location.pathname === NEOQUEST_TWO_PATH;
    }

    function isTyranuEvavuPage() {
        return location.pathname === TYRANU_EVAVU_PATH;
    }

    function isKissTheMortogPage(path) {
        const currentPath = String(path || location.pathname || "").toLowerCase();
        return currentPath === KISS_THE_MORTOG_PLAY_PATH || currentPath === KISS_THE_MORTOG_PROCESS_PATH;
    }

    function isGodoriAutoplayerPage() {
        return normalizePagePath(location.pathname).startsWith(GODORI_PATH_PREFIX);
    }

    function bootNeoQuestGuideHelper() {
        if (!isNeoQuestOnePage()) return;
        if (window.__NCC_AAA_NEOQUEST_ONE_HELPER_BOOTED__) {
            saveNeoQuestReport("already-active", "NeoQuest I Helper já estava ativo nesta página.");
            return;
        }
        window.__NCC_AAA_NEOQUEST_ONE_HELPER_BOOTED__ = true;
        saveNeoQuestReport("booting", "Inicializando NeoQuest I Helper.");
        try {
            runEmbeddedNeoQuestGuideHelper();
            saveNeoQuestReport("active", "NeoQuest I Helper ativo nesta página.", {
                keyboardShortcuts: true,
                actionKeys: ["KeyA", "KeyF", "Space"],
                fightActionKeys: false,
                sourceHelperVersion: "20251230"
            });
        } catch (error) {
            saveNeoQuestReport("error", errorToMessage(error), {
                error: errorToMessage(error),
                sourceHelperVersion: "20251230"
            });
            console.warn("[NCC AAA] Falha no NeoQuest I Helper.", error);
        }
    }

    function makeNeoQuestGmStorageShim() {
        return {
            getValue(key, fallback) {
                const raw = readJson(NEOQUEST_ONE_GM_PREFIX + String(key || ""), undefined);
                return raw === undefined ? fallback : raw;
            },
            setValue(key, value) {
                writeJson(NEOQUEST_ONE_GM_PREFIX + String(key || ""), value);
                return value;
            }
        };
    }

    function saveNeoQuestReport(status, message, extra) {
        lastNeoQuestReport = clonePlain(Object.assign({
            schema: "ncc-aaa-neoquest-one-helper-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceHelper: "NeoQuest.Guide NeoQuest 1 Helper",
            sourceHelperVersion: "20251230",
            module: "neoquest-one",
            pageOnly: NEOQUEST_ONE_PATH,
            currentPage: location.pathname,
            status: status || "unknown",
            active: isNeoQuestOnePage(),
            manualKeyboardOnly: true,
            backgroundAction: false,
            keyboardShortcuts: true,
            programmaticClicks: "user-keypress-only-on-neoquest-map/actions",
            message: message || "",
            updatedAt: isoNow()
        }, extra || {}));
        writeJson(NEOQUEST_ONE_LATEST_KEY, lastNeoQuestReport);
        dispatchStateEvent(window, NEOQUEST_ONE_EVENT, lastNeoQuestReport);
        try {
            const publicWindow = getPublicWindow();
            if (publicWindow && publicWindow !== window) dispatchStateEvent(publicWindow, NEOQUEST_ONE_EVENT, lastNeoQuestReport);
        } catch (_) {}
        return lastNeoQuestReport;
    }

    function buildNeoQuestReport() {
        return clonePlain(lastNeoQuestReport || readJson(NEOQUEST_ONE_LATEST_KEY, null) || {
            schema: "ncc-aaa-neoquest-one-helper-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceHelper: "NeoQuest.Guide NeoQuest 1 Helper",
            sourceHelperVersion: "20251230",
            module: "neoquest-one",
            pageOnly: NEOQUEST_ONE_PATH,
            currentPage: location.pathname,
            status: isNeoQuestOnePage() ? "not-mounted-yet" : "available",
            active: isNeoQuestOnePage(),
            manualKeyboardOnly: true,
            backgroundAction: false,
            keyboardShortcuts: true,
            programmaticClicks: "user-keypress-only-on-neoquest-map/actions",
            message: isNeoQuestOnePage() ? "Aguardando montagem do NeoQuest I Helper." : "Abra NeoQuest I para o helper atuar.",
            updatedAt: null
        });
    }

    function runEmbeddedNeoQuestGuideHelper() {
        /*
         * NeoQuest.Guide's NeoQuest 1 Helper 20251230, embedded minimally in NCC AAA.
         * Original scope: https://www.neopets.com/games/neoquest/neoquest.phtml*
         * Local changes: metadata removed, storage shimmed to ncc.aaa.*, update alert suppressed,
         * and a few sloppy-mode assignments patched for the NCC AAA strict wrapper.
         */
        const nccNeoQuestStorage = makeNeoQuestGmStorageShim();
        const GM_getValue = (key, fallback) => nccNeoQuestStorage.getValue(key, fallback);
        const GM_setValue = (key, value) => nccNeoQuestStorage.setValue(key, value);
        const defaultActionKeys = ["KeyA", "KeyF", "Space"]; //to disable keyboard actions, set this value to []
        //const defaultActionKeys = [];
        //2025-12-19
        //neopets discussed making nq speed runs more competitive by resetting the times each month/some interval
        //IMO nq is the sort of game people play for the single-player trophy and avatars, so this script was, again, IMO, acceptable
        //however, to maintain the philosophy that scripts should not give a competitive advantage over vanilla gameplay,
        //defaultActionKeys no longer apply to in-fight actions
        //while the timed runs were mostly accessible to those willing to grind it out, it wasn't commonly done, due to server issues
        //perhaps some nq tweaks -- such as the proposed speedrun resets, and other changes like adding farmable prizes, like stamps --
        //would make nq more ""competitive"" again
        //while I'd not like to abandon the script (it's on the internet, anyway, it's here forever)
        //it should continue to be useful for the proc chances (% of x attack, etc) and keyboard movement
        
        if (!GM_getValue('update20251230NoticeShown', false)) {
            // Original helper shows an update alert here. NCC AAA suppresses the popup and records the notice as acknowledged.
            GM_setValue('update20251230NoticeShown', true);
        }
        
        
        const gameObjects = {
         monsters: {
        	"fire imp": {
        	 id: 400000,
        	 image: "./images/png/monsters/400000_afireimp.png",
        	 level: 1,
        	 heat: 10,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["small yellow gem",333]
        	 ],
        	 map: ["world_crop_1"]
        	},
        	"snow imp": {
        	 id: 400001,
        	 image: "./images/png/monsters/400001_asnowimp.png",
        	 level: 1,
        	 cold: 10,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["chunk of metal",333],
        		["blue thread",333]
        	 ],
        	 map: ["world_crop_1"]
        	},
        	"plains lupe": {
        	 id: 400002,
        	 image: "./images/png/monsters/400002_aplainswolf.png",
        	 level: 2,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["glowing stone",333],
        		["plains lupe pelt",333]
        	 ],
        	 map: ["world_crop_1"]
        	},
        	"plains aisha": {
        	 id: 400003,
        	 image: "./images/png/monsters/400003_aplainscougar.png",
        	 level: 2,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["glowing stone",333]
        	 ],
        	 map: ["world_crop_1"]
        	},
        	"grey lupe": {
        	 id: 400004,
        	 image: "./images/png/monsters/400004_agraywolf.png",
        	 level: 3,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["grey lupe fang",400]
        	 ],
        	 map: []
        	},
        	"black bearog": {
        	 id: 400005,
        	 image: "./images/png/monsters/400005_ablackbear.png",
        	 level: 3,
        	 damage: 1,
        	 cold: 5,
        	 powers: [],
        	 drops: [
        		["black bearog paw",400]
        	 ],
        	 map: []
        	},
        	"grizzly bearog": {
        	 id: 400006,
        	 image: "./images/png/monsters/400006_agrizzlybear.png",
        	 level: 4,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["grizzly bearog tooth",500]
        	 ],
        	 map: []
        	},
        	"dire lupe": {
        	 id: 400007,
        	 image: "./images/png/monsters/400007_adirewolf.png",
        	 level: 4,
        	 damage: 1,
        	 heat: 5,
        	 powers: [],
        	 drops: [
        		["dire lupe pelt",500]
        	 ],
        	 map: []
        	},
        	"cave imp": {
        	 id: 400008,
        	 image: "./images/png/monsters/400008_acaveimp.png",
        	 level: 2,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"cave slug": {
        	 id: 400009,
        	 image: "./images/png/monsters/400009_acaveslug.png",
        	 level: 3,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"cave lupe": {
        	 id: 400010,
        	 image: "./images/png/monsters/400010_acavewolf.png",
        	 level: 4,
        	 powers: [],
        	 drops: [
        		["cave lupe pelt",300]
        	 ],
        	 map: []
        	},
        	"cave troll": {
        	 id: 400011,
        	 image: "./images/png/monsters/400011_acavetroll.png",
        	 level: 5,
        	 stun: 10,
        	 powers: [],
        	 drops: [
        		["tiny garnet", 75],
        		["tiny amber", 75],
        		["tiny beryl", 75],
        	 ],
        	 map: []
        	},
        	"cave ghoul": {
        	 id: 400012,
        	 image: "./images/png/monsters/400012_acaveghoul.png",
        	 level: 5,
        	 powers: [],
        	 drops: [
        		["tiny lapis", 75],
        		["tiny obsidian", 100],
        		["lodestone", 100],
        	 ],
        	 map: []
        	},
        	"broken skeleton": {
        	 id: 400013,
        	 image: "./images/png/monsters/400013_abrokenskeleton.png",
        	 level: 6,
        	 powers: [],
        	 drops: [
        		["tiny garnet", 75],
        		["tiny amber", 75],
        		["tiny beryl", 75],
        		["tiny lapis", 75],
        		["tiny obsidian", 75],
        	 ],
        	 map: []
        	},
        	"burned skeleton": {
        	 id: 400014,
        	 image: "./images/png/monsters/400014_aburnedskeleton.png",
        	 level: 7,
        	 heat: 10,
        	 powers: [],
        	 drops: [
        		["tiny garnet", 100],
        		["tiny amber", 100],
        		["tiny beryl", 100],
        		["tiny lapis", 100],
        		["tiny obsidian", 150],
        	 ],
        	 map: []
        	},
        	"frozen skeleton": {
        	 id: 400015,
        	 image: "./images/png/monsters/400015_afrozenskeleton.png",
        	 level: 7,
        	 cold: 10,
        	 powers: [],
        	 drops: [
        		["tiny garnet", 100],
        		["tiny amber", 100],
        		["tiny beryl", 100],
        		["tiny lapis", 100],
        		["tiny obsidian", 150],
        	 ],
        	 map: []
        	},
        	"metal devourer": {
        	 id: 400016,
        	 image: "./images/png/monsters/400016_ametaldevourer.png",
        	 level: 8,
        	 damage: -1,
        	 heat: 10,
        	 cold: 10,
        	 stun: 10,
        	 powers: ["$MSPW_POISON|P|5|3,3|5|10"],
        	 maxHitPotential: "m+3",
        	 drops: [
        		["corroded pyrite rod", 200],
        		["corroded pewter rod", 200],
        		["corroded copper rod", 200],
        		["corroded ore rod", 200],
        		["corroded aluminum rod", 200],
        		["lodestone", 300],
        	 ],
        	 map: []
        	},
        	"cave ogre": {
        	 id: 400017,
        	 image: "./images/png/monsters/400017_acaveogre.png",
        	 level: 8,
        	 stun: 15,
        	 powers: ["$MSPW_STUN|P|10|1|0|5"],
        	 drops: [
        		["tiny garnet", 150],
        		["tiny amber", 150],
        		["tiny beryl", 150],
        		["tiny lapis", 150],
        		["tiny obsidian", 150],
        	 ],
        	 map: []
        	},
        	"skeleton guard": {
        	 id: 400018,
        	 image: "./images/png/monsters/400018_askeletonguard.png",
        	 level: 9,
        	 powers: [],
        	 drops: [
        		["piece of smooth glass",200],
        		["stretch of rotted cloth",200],
        	 ],
        	 map: []
        	},
        	"xantan the foul": {
        	 id: 400019,
        	 image: "./images/png/monsters/400019_zBNtr_XantantheFoul.png",
        	 level: 10,
        	 attack: 3,
        	 defence: 3,
        	 damage: 3,
        	 heat: 25,
        	 cold: 25,
        	 stun: 25,
        	 xp: 500,
        	 np: {normal: 500, evil: 750, insane: 1000},
        	 maxHitPotential: "m|20",
        	 powers: ["$MSPW_FIREBLAST|P|10|20|0|10", "$MSPW_ICEBLAST|P|10|20|0|10"],
        	 drops: [
        		["Xantan's Ring",1000]
        	 ],
        	 hpNormal: 80
        	},
        	"rotting skeleton": {
        	 id: 400020,
        	 image: "./images/png/monsters/400020_arottingskeleton.png",
        	 level: 5,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"shadow knight": {
        	 id: 400021,
        	 image: "./images/png/monsters/400021_ashadowknight.png",
        	 level: 6,
        	 stun: 10,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"koi ghost": {
        	 id: 400022,
        	 image: "./images/png/monsters/400022_aghost.png",
        	 level: 7,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"eerie spectre": {
        	 id: 400023,
        	 image: "./images/png/monsters/400023_aspectre.png",
        	 level: 7,
        	 attack: 1,
        	 defence: 1,
        	 damage: 1,
        	 heat: -5,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"wight": {
        	 id: 400024,
        	 image: "./images/png/monsters/400024_awight.png",
        	 level: 8,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"kacheek zombie": {
        	 id: 400025,
        	 image: "./images/png/monsters/400025_azombie.png",
        	 level: 8,
        	 attack: 2,
        	 defence: 2,
        	 damage: 2,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"jubjub ghoul": {
        	 id: 400026,
        	 image: "./images/png/monsters/400026_aghoul.png",
        	 level: 9,
        	 defence: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"poogle vampire": {
        	 id: 400027,
        	 image: "./images/png/monsters/400027_avampire.png",
        	 level: 10,
        	 attack: 1,
        	 defence: 1,
        	 heat: -10,
        	 powers: ["$MSPW_STUNBLAST|P|10|10,2|0|8", "$MSPW_LIFETAP|P|10|10|0|8"],
        	 maxHitPotential: "10,m|10,m",
        	 drops: [],
        	 map: []
        	},
        	"pygmy": {
        	 id: 400028,
        	 image: "./images/png/monsters/400028_apygmy.png",
        	 level: 5,
        	 heat: 5,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"jungle zafara": {
        	 id: 400029,
        	 image: "./images/png/monsters/400029_ajunglepanther.png",
        	 level: 6,
        	 heat: 5,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"angry buzz": {
        	 id: 400030,
        	 image: "./images/png/monsters/400030_agiantmosquito.png",
        	 level: 7,
        	 heat: 5,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"jungle beast": {
        	 id: 400031,
        	 image: "./images/png/monsters/400031_ajunglebeast.png",
        	 level: 10,
        	 attack: 1,
        	 heat: 5,
        	 powers: [],
        	 drops: [
        		["jungle beast claw",500]
        	 ],
        	 map: []
        	},
        	"shaman": {
        	 id: 400032,
        	 image: "./images/png/monsters/400032_ashaman.png",
        	 level: 11,
        	 heat: 10,
        	 powers: ["$MSPW_FIREBLAST|P|10|15|0|6", "$MSPW_HEAL|P|10|P,20|0|5"],
        	 maxHitPotential: "m|15",
        	 drops: [
        		["shamanistic totem",500],
        	 ],
        	 map: []
        	},
        	"scorpion": {
        	 id: 400033,
        	 image: "./images/png/monsters/400033_ascorpion.png",
        	 level: 11,
        	 powers: ["$MSPW_POISON|P|10|8,3|0|10"],
        	 maxHitPotential: "m+8",
        	 drops: [
        		["scorpion carapace",500]
        	 ],
        	 map: []
        	},
        	"greater shaman": {
        	 id: 400034,
        	 image: "./images/png/monsters/400034_agreatershaman.png",
        	 level: 12,
        	 powers: ["$MSPW_ICEBLAST|P|10|20|0|6", "$MSPW_HEAL|P|10|P,20|0|5"],
        	 maxHitPotential: "m|20",
        	 drops: [
        		["shamanistic totem",500],
        		["wooden shield",500]
        	 ],
        	 map: [],
        	},
        	"killer buzz": {
        	 id: 400035,
        	 image: "./images/png/monsters/400035_akillermosquito.png",
        	 level: 12,
        	 powers: [],
        	 drops: [
        		["buzz wing",500]
        	 ],
        	 map: [],
        	},
        	"armored scorpion": {
        	 id: 400036,
        	 image: "./images/png/monsters/400036_anarmoredscorpion.png",
        	 level: 13,
        	 defence: 1,
        	 stun: 20,
        	 powers: [],
        	 drops: [
        		["armored stinger",500]
        	 ],
        	 map: []
        	},
        	"huge skeith": {
        	 id: 400037,
        	 image: "./images/png/monsters/400037_ahugeiguana.png",
        	 level: 13,
        	 attack: 1,
        	 powers: [],
        	 drops: [
        		["skeith fang",500]
        	 ],
        	 map: []
        	},
        	"giant wadjet": {
        	 id: 400038,
        	 image: "./images/png/monsters/400038_agiantpython.png",
        	 level: 14,
        	 attack: 1,
        	 powers: [],
        	 drops: [
        		["wadjet skin",500]
        	 ],
        	 map: []
        	},
        	"noil": {
        	 id: 400039,
        	 image: "./images/png/monsters/400039_alion.png",
        	 level: 14,
        	 attack: 1,
        	 defence: 1,
        	 powers: [],
        	 drops: [
        		["noils tooth",500],
        		["noils mane",500]
        	 ],
        	 map: []
        	},
        	"kreai": {
        	 id: 400040,
        	 image: "./images/png/monsters/400040_rUmqD_Kreai.png",
        	 level: 15,
        	 attack: 3,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 xp: 700,
        	 np: {normal: 700, evil: 1000, insane: 1300},
        	 powers: ["$MSPW_FIREBLAST|P|10|40|0|10", "$MSPW_ICEBLAST|P|10|40|0|10", "$MSPW_STUN|P|25|2|0|5"],
        	 maxHitPotential: "m|40,m|40",
        	 drops: [
        		["rotting wooden key",1000]
        	 ],
        	 hpNormal: 130
        	},
        	"pygmy warrior": {
        	 id: 400041,
        	 image: "./images/png/monsters/400041_apygmywarrior.png",
        	 level: 15,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"jungle knight": {
        	 id: 400042,
        	 image: "./images/png/monsters/400042_ajungleknight.png",
        	 level: 15,
        	 powers: [],
        	 drops: [
        		["jungle gauntlet", 50],
        		["jungle vambrace", 50],
        		["jungle breastplate", 50],
        		["jungle helm", 50],
        		["jungle pauldrons", 50],
        	 ],
        	 map: []
        	},
        	"pygmy chief": {
        	 id: 400043,
        	 image: "./images/png/monsters/400043_apygmychief.png",
        	 level: 16,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"jungle lord": {
        	 id: 400044,
        	 image: "./images/png/monsters/400044_ajunglelord.png",
        	 level: 16,
        	 attack: 1,
        	 defence: 1,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["jungle gauntlet", 100],
        		["jungle vambrace", 100],
        		["jungle breastplate", 100],
        		["jungle helm", 100],
        		["jungle pauldrons", 100],
        	 ],
        	 map: []
        	},
        	"pygmy sage": {
        	 id: 400045,
        	 image: "./images/png/monsters/400045_apygmysage.png",
        	 level: 17,
        	 powers: ["$MSPW_FIREBLAST|P|10|15|0|5","$MSPW_ICEBLAST|P|10|15|0|5","$MSPW_HEAL|P|10|P,15|0|5"],
        	 maxHitPotential: "m|15",
        	 drops: [],
        	 map: []
        	},
        	"jungle battle lord": {
        	 id: 400046,
        	 image: "./images/png/monsters/400046_ajunglebattlelord.png",
        	 level: 17,
        	 attack: 1,
        	 defence: 1,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["jungle gauntlet", 150],
        		["jungle vambrace", 150],
        		["jungle breastplate", 150],
        		["jungle helm", 150],
        		["jungle pauldrons", 150],
        	 ],
        	 map: []
        	},
        	"pygmy elder": {
        	 id: 400047,
        	 image: "./images/png/monsters/400047_apygmyelder.png",
        	 level: 18,
        	 powers: ["$MSPW_FIRESTUN|P|10|15,1|0|5","$MSPW_ICESTUN|P|10|15,1|0|5","$MSPW_HEAL|P|10|P,15|0|5"],
        	 maxHitPotential: "15,15,m",
        	 drops: [],
        	 map: []
        	},
        	"jungle death knight": {
        	 id: 400048,
        	 image: "./images/png/monsters/400048_ajungledeathknight.png",
        	 level: 18,
        	 attack: 1,
        	 defence: 1,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["jungle gauntlet", 200],
        		["jungle vambrace", 200],
        		["jungle breastplate", 200],
        		["jungle helm", 200],
        		["jungle pauldrons", 200],
        	 ],
        	 map: []
        	},
        	"skeletal guardian": {
        	 id: 400049,
        	 image: "./images/png/monsters/400049_askeletalguardian.png",
        	 level: 19,
        	 attack: 1,
        	 defence: 1,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["jungle gauntlet", 250],
        		["jungle vambrace", 250],
        		["jungle breastplate", 250],
        		["jungle helm", 250],
        		["jungle pauldrons", 250],
        	 ],
        	 map: []
        	},
        	"skeletal shaman": {
        	 id: 400050,
        	 image: "./images/png/monsters/400050_askeletalshaman.png",
        	 level: 19,
        	 powers: ["$MSPW_FIREBLAST|P|10|20|0|8","$MSPW_ICEBLAST|P|10|20|0|8","$MSPW_STUN|P|10|2|15|8","$MSPW_HEAL|P|10|P,15|0|8"],
        	 maxHitPotential: "m|20,m|20",
        	 drops: [],
        	 map: []
        	},
        	"gors the mighty": {
        	 id: 400051,
        	 image: "./images/png/monsters/400051_pLOIa_GorstheMighty.png",
        	 level: 20,
        	 attack: 3,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 xp: 850,
        	 np: {normal: 850, evil: 1275, insane: 1700},
        	 powers: ["$MSPW_FIRESTUN|P|10|30,2|0|10","$MSPW_ICESTUN|P|10|30,2|0|10","$MSPW_POISON|P|15|15,5|0|10","$MSPW_HEAL|P|5|P,20|0|8"],
        	 maxHitPotential: "45,m+15,45,m+15,m+15",
        	 drops: [
        		["Staff of Ni-tas", 1000],
        		["silvered horn key", 1000]
        	 ],
        	 hpNormal: 150
        	},
        	"fire lizard": {
        	 id: 400052,
        	 image: "./images/png/monsters/400052_afirelizard.png",
        	 level: 20,
        	 heat: 20,
        	 cold: -20,
        	 powers: ["$MSPW_FIREBLAST|P|20|20|0|7"],
        	 maxHitPotential: "m|20",
        	 drops: [],
        	 map: []
        	},
        	"ice lizard": {
        	 id: 400053,
        	 image: "./images/png/monsters/400053_anicelizard.png",
        	 level: 20,
        	 heat: -20,
        	 cold: 20,
        	 powers: ["$MSPW_ICEBLAST|P|20|20|0|7"],
        	 maxHitPotential: "m|20",
        	 drops: [],
        	 map: []
        	},
        	"shock lizard": {
        	 id: 400054,
        	 image: "./images/png/monsters/400054_ashocklizard.png",
        	 level: 20,
        	 stun: 20,
        	 def: -1,
        	 powers: ["$MSPW_STUN|P|20|1|0|7"],
        	 drops: [],
        	 map: []
        	},
        	"flame lizard": {
        	 id: 400055,
        	 image: "./images/png/monsters/400055_aflamelizard.png",
        	 level: 21,
        	 heat: 20,
        	 cold: -10,
        	 powers: ["$MSPW_FIREBLAST|P|25|20|0|6"],
        	 maxHitPotential: "m|20",
        	 drops: [],
        	 map: []
        	},
        	"frost lizard": {
        	 id: 400056,
        	 image: "./images/png/monsters/400056_afrostlizard.png",
        	 level: 21,
        	 heat: -10,
        	 cold: 20,
        	 powers: ["$MSPW_ICEBLAST|P|25|20|0|6"],
        	 maxHitPotential: "m|20",
        	 drops: [],
        	 map: []
        	},
        	"lightning lizard": {
        	 id: 400057,
        	 image: "./images/png/monsters/400057_alightninglizard.png",
        	 level: 21,
        	 stun: 20,
        	 def: -1,
        	 powers: ["$MSPW_STUN|P|25|1|0|6"],
        	 drops: [],
        	 map: []
        	},
        	"inferno lizard": {
        	 id: 400058,
        	 image: "./images/png/monsters/400058_aninfernolizard.png",
        	 level: 22,
        	 heat: 20,
        	 cold: -10,
        	 powers: ["$MSPW_FIREBLAST|P|25|30|0|6"],
        	 maxHitPotential: "m|30",
        	 drops: [],
        	 map: []
        	},
        	"blizzard lizard": {
        	 id: 400059,
        	 image: "./images/png/monsters/400059_ablizzardlizard.png",
        	 level: 22,
        	 heat: -10,
        	 cold: 20,
        	 powers: ["$MSPW_ICEBLAST|P|25|30|0|6"],
        	 maxHitPotential: "m|30",
        	 drops: [],
        	 map: []
        	},
        	"thunder lizard": {
        	 id: 400060,
        	 image: "./images/png/monsters/400060_athunderlizard.png",
        	 level: 22,
        	 stun: 20,
        	 def: -1,
        	 powers: ["$MSPW_STUN|P|25|2|0|6"],
        	 maxHitPotential: "m,m",
        	 drops: [],
        	 map: []
        	},
        	"fiery lizard": {
        	 id: 400061,
        	 image: "./images/png/monsters/400061_afierylizard.png",
        	 level: 23,
        	 heat: 20,
        	 cold: -10,
        	 powers: ["$MSPW_FIREBLAST|P|25|30|0|5"],
        	 maxHitPotential: "m|30",
        	 drops: [],
        	 map: []
        	},
        	"frozen lizard": {
        	 id: 400062,
        	 image: "./images/png/monsters/400062_afrozenlizard.png",
        	 level: 23,
        	 heat: -10,
        	 cold: 20,
        	 powers: ["$MSPW_ICEBLAST|P|25|30|0|5"],
        	 maxHitPotential: "m|30",
        	 drops: [],
        	 map: []
        	},
        	"electro lizard": {
        	 id: 400063,
        	 image: "./images/png/monsters/400063_anelectrolizard.png",
        	 level: 23,
        	 stun: 20,
        	 def: -1,
        	 powers: ["$MSPW_STUN|P|25|2|0|5"],
        	 maxHitPotential: "m,m",
        	 drops: [],
        	 map: []
        	},
        	"fire lizard guardian": {
        	 id: 400064,
        	 image: "./images/png/monsters/400064_afirelizardguardian.png",
        	 level: 24,
        	 heat: 30,
        	 powers: ["$MSPW_FIRESTUN|P|15|20,2|0|7"],
        	 maxHitPotential: "20,m,m",
        	 drops: [],
        	 map: []
        	},
        	"ice lizard guardian": {
        	 id: 400065,
        	 image: "./images/png/monsters/400065_anicelizardguardian.png",
        	 level: 24,
        	 cold: 30,
        	 powers: ["$MSPW_ICESTUN|P|15|20,2|0|7"],
        	 maxHitPotential: "20,m,m",
        	 drops: [],
        	 map: []
        	},
        	"shock lizard guardian": {
        	 id: 400066,
        	 image: "./images/png/monsters/400066_ashocklizardguardian.png",
        	 level: 24,
        	 stun: 30,
        	 powers: ["$MSPW_STUN|P|15|3|0|10"],
        	 maxHitPotential: "m,m,m",
        	 drops: [],
        	 map: []
        	},
        	"rollay scaleback": {
        	 id: 400067,
        	 image: "./images/png/monsters/400067_rbbZZ_RollayScaleback.png",
        	 level: 25,
        	 attack: 3,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 xp: 1000,
        	 np: {normal: 1000, evil: 1500, insane: 2000},
        	 powers: ["$MSPW_STUN|P|15|3|0|10","$MSPW_HEAL|P|15|P,25|0|8"],
        	 maxHitPotential: "m,m,m",
        	 drops: [
        		["Rusty Medallion",1000],
        		["Jeweled Crystal Key",1000]
        	 ],
        	 hpNormal: 180
        	},
        	"swamp lupe": {
        	 id: 400068,
        	 image: "./images/png/monsters/400068_aswampwolf.png",
        	 level: 11,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"krawk": {
        	 id: 400069,
        	 image: "./images/png/monsters/400069_analligator.png",
        	 level: 11,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"brown wadjet": {
        	 id: 400070,
        	 image: "./images/png/monsters/400070_abrownpython.png",
        	 level: 12,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"swamp krawk": {
        	 id: 400071,
        	 image: "./images/png/monsters/400071_aswampalligator.png",
        	 level: 12,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"black wadjet": {
        	 id: 400072,
        	 image: "./images/png/monsters/400072_ablackpython.png",
        	 level: 13,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"vicious vine": {
        	 id: 400073,
        	 image: "./images/png/monsters/400073_aviciousvine.png",
        	 level: 13,
        	 powers: ["$MSPW_POISON|P|10|10,3|0|10"],
        	 maxHitPotential: "m+10",
        	 drops: [],
        	 map: []
        	},
        	"giant krawk": {
        	 id: 400074,
        	 image: "./images/png/monsters/400074_agiantalligator.png",
        	 level: 14,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"crushing vine": {
        	 id: 400075,
        	 image: "./images/png/monsters/400075_acrushingvine.png",
        	 level: 14,
        	 powers: ["$MSPW_POISON|P|10|12,3|0|10"],
        	 maxHitPotential: "m+12",
        	 drops: [],
        	 map: []
        	},
        	"swamp dire lupe": {
        	 id: 400076,
        	 image: "./images/png/monsters/400076_aswampdirewolf.png",
        	 level: 15,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"deadly vine": {
        	 id: 400077,
        	 image: "./images/png/monsters/400077_adeadlyvine.png",
        	 level: 15,
        	 powers: ["$MSPW_POISON|P|10|14,3|0|10"],
        	 maxHitPotential: "m+14",
        	 drops: [],
        	 map: []
        	},
        	"plains ogre": {
        	 id: 400078,
        	 image: "./images/png/monsters/400078_aplainsogre.png",
        	 level: 16,
        	 damage: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"lupe warrior": {
        	 id: 400079,
        	 image: "./images/png/monsters/400079_awolfman.png",
        	 level: 16,
        	 attack: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"ghastly meerca": {
        	 id: 400080,
        	 image: "./images/png/monsters/400080_aghast.png",
        	 level: 17,
        	 powers: ["$MSPW_LIFETAP|P|10|20|0|10"],
        	 maxHitPotential: "m|20",
        	 drops: [],
        	 map: []
        	},
        	"lupe mage": {
        	 id: 400081,
        	 image: "./images/png/monsters/400081_awolfmage.png",
        	 level: 17,
        	 powers: ["$MSPW_FIREBLAST|P|10|20|0|6","$MSPW_ICEBLAST|P|10|20|0|6"],
        	 maxHitPotential: "m|20",
        	 drops: [],
        	 map: []
        	},
        	"giant sand spyder": {
        	 id: 400082,
        	 image: "./images/png/monsters/400082_agiantsandspider.png",
        	 level: 18,
        	 powers: [],
        	 drops: [
        		["giant spider leg",500],
        		["drop of giant spider blood",500]
        	 ],
        	 map: []
        	},
        	"desert cobrall": {
        	 id: 400083,
        	 image: "./images/png/monsters/400083_adesertcobra.png",
        	 level: 18,
        	 powers: ["$MSPW_POISON|P|15|20,2|0|6"],
        	 maxHitPotential: "m+20",
        	 drops: [
        		["drop of desert cobra venom",500],
        		["desert cobra fang",500]
        	 ],
        	 map: []
        	},
        	"sand skeith": {
        	 id: 400084,
        	 image: "./images/png/monsters/400084_asandiguana.png",
        	 level: 19,
        	 powers: [],
        	 drops: [
        		["sand iguana eye",500],
        		["glob of dried iguana spit",500]
        	 ],
        	 map: []
        	},
        	"dust mummy": {
        	 id: 400085,
        	 image: "./images/png/monsters/400085_adustmummy.png",
        	 level: 19,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"giant dust spyder": {
        	 id: 400086,
        	 image: "./images/png/monsters/400086_agiantdustspider.png",
        	 level: 20,
        	 powers: ["$MSPW_STUN|P|15|2|10|10"],
        	 maxHitPotential: "m,m",
        	 drops: [
        		["dust spider pincer",500]
        	 ],
        	 map: []
        	},
        	"desert zombie": {
        	 id: 400087,
        	 image: "./images/png/monsters/400087_adesertzombie.png",
        	 level: 20,
        	 powers: ["$MSPW_LIFETAP|P|10|25|0|8"],
        	 maxHitPotential: "m|25",
        	 drops: [
        		["pinch of crystallized sand",500]
        	 ],
        	 map: []
        	},
        	"young hill giant": {
        	 id: 400088,
        	 image: "./images/png/monsters/400088_ayounghillgiant.png",
        	 level: 19,
        	 defence: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"dirt golem": {
        	 id: 400089,
        	 image: "./images/png/monsters/400089_adirtgolem.png",
        	 level: 19,
        	 defence: 1,
        	 heat: 3,
        	 cold: 3,
        	 stun: 3,
        	 powers: [],
        	 drops: [
        		["glowing stone",500]
        	 ],
        	 map: []
        	},
        	"hill giant": {
        	 id: 400090,
        	 image: "./images/png/monsters/400090_ahillgiant.png",
        	 level: 20,
        	 damage: 2,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"rock golem": {
        	 id: 400091,
        	 image: "./images/png/monsters/400091_arockgolem.png",
        	 level: 20,
        	 defence: 1,
        	 heat: 6,
        	 cold: 6,
        	 stun: 6,
        	 powers: [],
        	 drops: [
        		["glowing stone",500]
        	 ],
        	 map: []
        	},
        	"young plains grarrl": {
        	 id: 400092,
        	 image: "./images/png/monsters/400092_ayoungplainsgiant.png",
        	 level: 20,
        	 damage: 1,
        	 powers: [],
        	 drops: [
        		["carved oak staff",500]
        	 ],
        	 map: []
        	},
        	"stone golem": {
        	 id: 400093,
        	 image: "./images/png/monsters/400093_astonegolem.png",
        	 level: 21,
        	 defence: 2,
        	 heat: 9,
        	 cold: 9,
        	 stun: 9,
        	 powers: [],
        	 drops: [
        		["glowing stone",500]
        	 ],
        	 map: []
        	},
        	"plains grarrl": {
        	 id: 400094,
        	 image: "./images/png/monsters/400094_aplainsgiant.png",
        	 level: 21,
        	 damage: 2,
        	 powers: [],
        	 drops: [
        		["carved oak staff",500]
        	 ],
        	 map: []
        	},
        	"iron golem": {
        	 id: 400095,
        	 image: "./images/png/monsters/400095_anirongolem.png",
        	 level: 22,
        	 defence: 2,
        	 heat: 12,
        	 cold: 12,
        	 stun: 12,
        	 powers: [],
        	 drops: [
        		["glowing stone",500]
        	 ],
        	 map: []
        	},
        	"elder plains grarrl": {
        	 id: 400096,
        	 image: "./images/png/monsters/400096_anelderplainsgiant.png",
        	 level: 22,
        	 damage: 2,
        	 attack: 1,
        	 powers: [],
        	 drops: [
        		["carved oak staff",500]
        	 ],
        	 map: []
        	},
        	"steel golem": {
        	 id: 400097,
        	 image: "./images/png/monsters/400097_asteelgolem.png",
        	 level: 23,
        	 defence: 2,
        	 heat: 15,
        	 cold: 15,
        	 stun: 15,
        	 powers: [],
        	 drops: [
        		["glowing stone",500]
        	 ],
        	 map: []
        	},
        	"ancient plains grarrl": {
        	 id: 400098,
        	 image: "./images/png/monsters/400098_anancientplainsgiant.png",
        	 level: 23,
        	 damage: 2,
        	 attack: 2,
        	 powers: [],
        	 drops: [
        		["carved oak staff",500]
        	 ],
        	 map: []
        	},
        	"cave scorpion": {
        	 id: 400099,
        	 image: "./images/png/monsters/400099_acavescorpion.png",
        	 level: 20,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"giant cave lizard": {
        	 id: 400100,
        	 image: "./images/png/monsters/400100_agiantcavelizard.png",
        	 level: 21,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"desert khonsu": {
        	 id: 400101,
        	 image: "./images/png/monsters/400101_adesertmummy.png",
        	 level: 22,
        	 powers: ["$MSPW_LIFETAP|P|15|25|0|10"],
        	 maxHitPotential: "m|25",
        	 drops: [],
        	 map: []
        	},
        	"temple guardian": {
        	 id: 400102,
        	 image: "./images/png/monsters/400102_atempleguardian.png",
        	 level: 23,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"sand golem": {
        	 id: 400103,
        	 image: "./images/png/monsters/400103_asandgolem.png",
        	 level: 24,
        	 defence: 2,
        	 heat: 25,
        	 cold: 5,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"greater temple guardian": {
        	 id: 400104,
        	 image: "./images/png/monsters/400104_agreatertempleguardian.png",
        	 level: 24,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"glass golem": {
        	 id: 400105,
        	 image: "./images/png/monsters/400105_aglassgolem.png",
        	 level: 25,
        	 defence: 2,
        	 heat: 5,
        	 cold: 25,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"temple watchman": {
        	 id: 400106,
        	 image: "./images/png/monsters/400106_atemplewatchman.png",
        	 level: 25,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"crystal golem": {
        	 id: 400107,
        	 image: "./images/png/monsters/400107_acrystalgolem.png",
        	 level: 26,
        	 defence: 3,
        	 heat: 25,
        	 cold: 25,
        	 powers: [],
        	 drops: [
        		["piece of living crystal",500]
        	 ],
        	 map: []
        	},
        	"temple sentinel": {
        	 id: 400108,
        	 image: "./images/png/monsters/400108_atemplesentinel.png",
        	 level: 26,
        	 attack: 2,
        	 damage: 2,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"ghastly guard": {
        	 id: 400109,
        	 image: "./images/png/monsters/400109_aghastlyguard.png",
        	 level: 27,
        	 powers: ["$MSPW_LIFETAP|P|15|30|0|10"],
        	 maxHitPotential: "m|30",
        	 drops: [
        		["emerald", 100],
        		["onyx", 100],
        		["ruby", 100],
        		["sapphire", 100],
        		["topaz", 100],
        	 ],
        	 map: []
        	},
        	"ghastly initiate": {
        	 id: 400110,
        	 image: "./images/png/monsters/400110_aghastlyinitiate.png",
        	 level: 28,
        	 powers: ["$MSPW_LIFETAP|P|15|35|0|8"],
        	 maxHitPotential: "m|35",
        	 drops: [
        		["emerald", 200],
        		["onyx", 200],
        		["ruby", 200],
        		["sapphire", 200],
        		["topaz", 200],
        		["copper-plated key", 1000]
        	 ],
        	 map: []
        	},
        	"ghastly guardian": {
        	 id: 400111,
        	 image: "./images/png/monsters/400111_aghastlyguardian.png",
        	 level: 27,
        	 powers: ["$MSPW_LIFETAP|P|15|30|0|9"],
        	 maxHitPotential: "m|30",
        	 drops: [
        		["emerald", 100],
        		["onyx", 100],
        		["ruby", 100],
        		["sapphire", 100],
        		["topaz", 100],
        	 ],
        	 map: []
        	},
        	"ghastly sentry": {
        	 id: 400112,
        	 image: "./images/png/monsters/400112_aghastlysentry.png",
        	 level: 28,
        	 powers: ["$MSPW_LIFETAP|P|15|35|0|8"],
        	 maxHitPotential: "m|35",
        	 drops: [
        		["emerald", 100],
        		["onyx", 100],
        		["ruby", 100],
        		["sapphire", 100],
        		["topaz", 100],
        	 ],
        	 map: []
        	},
        	"ghastly adept": {
        	 id: 400113,
        	 image: "./images/png/monsters/400113_aghastlyadept.png",
        	 level: 29,
        	 powers: ["$MSPW_LIFETAP|P|15|40|0|7"],
        	 maxHitPotential: "m|40",
        	 drops: [
        		["emerald", 200],
        		["onyx", 200],
        		["ruby", 200],
        		["sapphire", 200],
        		["topaz", 200],
        		["bronze-plated key", 1000]
        	 ],
        	 map: []
        	},
        	"ghastly priest": {
        	 id: 400114,
        	 image: "./images/png/monsters/400114_aghastlypriest.png",
        	 level: 29,
        	 powers: ["$MSPW_LIFETAP|P|15|40|0|7"],
        	 maxHitPotential: "m|40",
        	 drops: [
        		["emerald", 200],
        		["onyx", 200],
        		["ruby", 200],
        		["sapphire", 200],
        		["topaz", 200],
        		["silver-plated key", 1000]
        	 ],
        	 map: []
        	},
        	"ghastly master": {
        	 id: 400115,
        	 image: "./images/png/monsters/400115_aghastlymaster.png",
        	 level: 29,
        	 powers: ["$MSPW_LIFETAP|P|15|40|0|7"],
        	 maxHitPotential: "m|40",
        	 drops: [
        		["emerald", 200],
        		["onyx", 200],
        		["ruby", 200],
        		["sapphire", 200],
        		["topaz", 200],
        		["gold-plated key", 1000]
        	 ],
        	 map: []
        	},
        	"ghastly protector": {
        	 id: 400116,
        	 image: "./images/png/monsters/400116_aghastlyprotector.png",
        	 level: 28,
        	 powers: ["$MSPW_LIFETAP|P|15|35|0|8"],
        	 maxHitPotential: "m|35",
        	 drops: [
        		["emerald", 100],
        		["onyx", 100],
        		["ruby", 100],
        		["sapphire", 100],
        		["topaz", 100],
        	 ],
        	 map: []
        	},
        	"ghastly sentinel": {
        	 id: 400117,
        	 image: "./images/png/monsters/400117_aghastlysentinel.png",
        	 level: 29,
        	 powers: ["$MSPW_LIFETAP|P|15|40|0|7"],
        	 maxHitPotential: "m|40",
        	 drops: [
        		["emerald", 100],
        		["onyx", 100],
        		["ruby", 100],
        		["sapphire", 100],
        		["topaz", 100],
        	 ],
        	 map: []
        	},
        	"ghastly archon": {
        	 id: 400118,
        	 image: "./images/png/monsters/400118_aghastlyarchon.png",
        	 level: 30,
        	 powers: ["$MSPW_LIFETAP|P|15|45|0|6"],
        	 maxHitPotential: "m|45",
        	 drops: [
        		["emerald", 200],
        		["onyx", 200],
        		["ruby", 200],
        		["sapphire", 200],
        		["topaz", 200],
        		["platinum-plated key", 1000]
        	 ],
        	 map: []
        	},
        	"ghastly templar": {
        	 id: 400119,
        	 image: "./images/png/monsters/400119_aghastlytemplar.png",
        	 level: 30,
        	 powers: ["$MSPW_LIFETAP|P|15|45|0|6"],
        	 maxHitPotential: "m|45",
        	 drops: [
        		["emerald", 200],
        		["onyx", 200],
        		["ruby", 200],
        		["sapphire", 200],
        		["topaz", 200],
        		["crystalline key", 1000]
        	 ],
        	 map: []
        	},
        	"ghastly defender": {
        	 id: 400120,
        	 image: "./images/png/monsters/400120_aghastlydefender.png",
        	 level: 29,
        	 powers: ["$MSPW_LIFETAP|P|15|40|0|7"],
        	 maxHitPotential: "m|40",
        	 drops: [
        		["emerald", 100],
        		["onyx", 100],
        		["ruby", 100],
        		["sapphire", 100],
        		["topaz", 100],
        	 ],
        	 map: []
        	},
        	"ghastly champion": {
        	 id: 400121,
        	 image: "./images/png/monsters/400121_aghastlychampion.png",
        	 level: 29,
        	 powers: ["$MSPW_LIFETAP|P|15|40|0|7"],
        	 maxHitPotential: "m|40",
        	 drops: [
        		["emerald", 100],
        		["onyx", 100],
        		["ruby", 100],
        		["sapphire", 100],
        		["topaz", 100],
        	 ],
        	 map: []
        	},
        	"ghastly warder": {
        	 id: 400122,
        	 image: "./images/png/monsters/400122_aghastlywarder.png",
        	 level: 30,
        	 powers: ["$MSPW_LIFETAP|P|20|45|0|6"],
        	 maxHitPotential: "m|45",
        	 drops: [
        		["emerald", 100],
        		["onyx", 100],
        		["ruby", 100],
        		["sapphire", 100],
        		["topaz", 100],
        	 ],
        	 map: []
        	},
        	"the archmagus of roo": {
        	 id: 400123,
        	 image: "./images/png/monsters/400123_GLkAS_theArchmagusofRoo.png",
        	 level: 31,
        	 attack: 3,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 xp: 1200,
        	 np: {normal: 1200, evil: 1800, insane: 2400},
        	 powers: ["$MSPW_HEAL|P|20|P,40|20|10","$MSPW_FIREBLAST|P|20|50|0|5","$MSPW_ICEBLAST|P|20|50|0|5","$MSPW_FIRESTUN|P|20|40,3|20|15","$MSPW_ICESTUN|P|20|40,3|20|15","$MSPW_LIFETAP|P|15|50|0|7"],
        	 maxHitPotential: "40,m|50,m|50,40,m|50,m,m",
        	 drops: [
        		["Clouded Gem",1000]
        	 ],
        	 hpNormal: 200
        	},
        	"hatchling drakonid": {
        	 id: 400124,
        	 image: "./images/png/monsters/400124_ahatchlingdrakonid.png",
        	 level: 27,
        	 powers: ["$MSPW_FIREBLAST|P|5|30|0|15"],
        	 maxHitPotential: "m|30",
        	 drops: [
        		["drakonid eye", 250],
        	 ],
        	 map: []
        	},
        	"agate dervish": {
        	 id: 400125,
        	 image: "./images/png/monsters/400125_anagatedervish.png",
        	 level: 27,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|5|2|20|10"],
        	 maxHitPotential: "m,m",
        	 drops: [
        		["piece of agate",250]
        	 ],
        	 map: []
        	},
        	"juvenile drakonid": {
        	 id: 400126,
        	 image: "./images/png/monsters/400126_ajuveniledrakonid.png",
        	 level: 28,
        	 powers: ["$MSPW_FIREBLAST|P|5|35|0|12"],
        	 maxHitPotential: "m|35",
        	 drops: [
        		["drakonid eye", 250],
        		["drakonid hide", 250],
        	 ],
        	 map: []
        	},
        	"greater agate dervish": {
        	 id: 400127,
        	 image: "./images/png/monsters/400127_agreateragatedervish.png",
        	 level: 28,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|5|2|17|9"],
        	 maxHitPotential: "m,m",
        	 drops: [
        		["piece of agate",250]
        	 ],
        	 map: []
        	},
        	"rock scorchio": {
        	 id: 400128,
        	 image: "./images/png/monsters/400128_arockdragon.png",
        	 level: 29,
        	 damage: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"chrysolite dervish": {
        	 id: 400129,
        	 image: "./images/png/monsters/400129_achrysolitedervish.png",
        	 level: 29,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|8|2|14|9"],
        	 maxHitPotential: "m,m",
        	 drops: [
        		["piece of chrysolite",250]
        	 ],
        	 map: []
        	},
        	"adult drakonid": {
        	 id: 400130,
        	 image: "./images/png/monsters/400130_anadultdrakonid.png",
        	 level: 30,
        	 powers: ["$MSPW_FIREBLAST|P|10|35|0|10"],
        	 maxHitPotential: "m|35",
        	 drops: [
        		["drakonid heart", 250],
        		["drakonid hide", 250],
        	 ],
        	 map: []
        	},
        	"tempus rock scorchio": {
        	 id: 400131,
        	 image: "./images/png/monsters/400131_atempusrockdragon.png",
        	 level: 30,
        	 damage: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"morgus rock scorchio": {
        	 id: 400132,
        	 image: "./images/png/monsters/400132_amorgusrockdragon.png",
        	 level: 31,
        	 damage: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"greater chrysolite dervish": {
        	 id: 400133,
        	 image: "./images/png/monsters/400133_agreaterchrysolitedervish.png",
        	 level: 31,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|11|2|14|8"],
        	 maxHitPotential: "m,m",
        	 drops: [
        		["piece of chrysolite",250]
        	 ],
        	 map: []
        	},
        	"mature drakonid": {
        	 id: 400134,
        	 image: "./images/png/monsters/400134_amaturedrakonid.png",
        	 level: 32,
        	 powers: ["$MSPW_FIREBLAST|P|10|45|-20|8"],
        	 maxHitPotential: "m|45",
        	 drops: [
        		["drakonid eye", 250],
        		["drakonid heart", 250],
        		["drakonid hide", 250],
        	 ],
        	 map: []
        	},
        	"serpentine dervish": {
        	 id: 400135,
        	 image: "./images/png/monsters/400135_aserpentinedervish.png",
        	 level: 32,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|14|2|14|8"],
        	 maxHitPotential: "m,m",
        	 drops: [
        		["piece of serpentine",250]
        	 ],
        	 map: []
        	},
        	"fumit rock scorchio": {
        	 id: 400136,
        	 image: "./images/png/monsters/400136_afumitrockdragon.png",
        	 level: 33,
        	 damage: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"greater serpentine dervish": {
        	 id: 400137,
        	 image: "./images/png/monsters/400137_agreaterserpentinedervish.png",
        	 level: 33,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|14|2|10|8"],
        	 maxHitPotential: "m,m",
        	 drops: [
        		["piece of serpentine",250]
        	 ],
        	 map: []
        	},
        	"elder drakonid": {
        	 id: 400138,
        	 image: "./images/png/monsters/400138_anelderdrakonid.png",
        	 level: 34,
        	 powers: ["$MSPW_FIREBLAST|P|15|50|-30|8"],
        	 maxHitPotential: "m|50",
        	 drops: [
        		["drakonid eye", 250],
        		["drakonid heart", 250],
        		["drakonid hide", 250],
        	 ],
        	 map: []
        	},
        	"lazulite dervish": {
        	 id: 400139,
        	 image: "./images/png/monsters/400139_alazulitedervish.png",
        	 level: 34,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|15|2|7|8"],
        	 maxHitPotential: "m,m",
        	 drops: [],
        	 map: []
        	},
        	"tolbas rock scorchio": {
        	 id: 400140,
        	 image: "./images/png/monsters/400140_atolbasrockdragon.png",
        	 level: 35,
        	 damage: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"melatite dervish": {
        	 id: 400141,
        	 image: "./images/png/monsters/400141_amelatitedervish.png",
        	 level: 35,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|15|2|4|7"],
        	 maxHitPotential: "m,m",
        	 drops: [],
        	 map: []
        	},
        	"praetus rock scorchio": {
        	 id: 400142,
        	 image: "./images/png/monsters/400142_apraetusrockdragon.png",
        	 level: 36,
        	 damage: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"greater melatite dervish": {
        	 id: 400143,
        	 image: "./images/png/monsters/400143_agreatermelatitedervish.png",
        	 level: 36,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|15|2|0|7"],
        	 maxHitPotential: "m,m",
        	 drops: [],
        	 map: []
        	},
        	"giant drakonid": {
        	 id: 400144,
        	 image: "./images/png/monsters/400144_agiantdrakonid.png",
        	 level: 37,
        	 powers: ["$MSPW_FIREBLAST|P|15|50|-30|6"],
        	 maxHitPotential: "m|50",
        	 drops: [
        		["drakonid eye", 250],
        		["drakonid heart", 250],
        		["drakonid hide", 250],
        	 ],
        	 map: []
        	},
        	"granite dervish": {
        	 id: 400145,
        	 image: "./images/png/monsters/400145_agranitedervish.png",
        	 level: 37,
        	 defence: 1,
        	 powers: ["$MSPW_STUN|P|15|2|0|7"],
        	 maxHitPotential: "m,m",
        	 drops: [],
        	 map: []
        	},
        	"mountain guardian": {
        	 id: 400146,
        	 image: "./images/png/monsters/400146_amountainguardian.png",
        	 level: 38,
        	 attack: 1,
        	 defence: 2,
        	 powers: ["$MSPW_HEAL|P|15|P,20|0|7"],
        	 drops: [
        		["Inferno Robe", 50],
        		["Evening Sun Energy Shield", 50]
        	 ],
        	 map: []
        	},
        	"mountain protector": {
        	 id: 400147,
        	 image: "./images/png/monsters/400147_amountainprotector.png",
        	 level: 38,
        	 attack: 1,
        	 defence: 1,
        	 damage: 1,
        	 powers: ["$MSPW_HEAL|P|15|P,20|0|7"],
        	 drops: [
        		["Inferno Robe", 50],
        		["Evening Sun Energy Shield", 50]
        	 ],
        	 map: []
        	},
        	"greater spirit": {
        	 id: 400148,
        	 image: "./images/png/monsters/400148_agreaterspirit.png",
        	 level: 38,
        	 attack: 2,
        	 defence: 2,
        	 powers: [],
        	 drops: [
        		["Inferno Robe", 50],
        		["Evening Sun Energy Shield", 50]
        	 ],
        	 map: []
        	},
        	"shock elemental": {
        	 id: 400149,
        	 image: "./images/png/monsters/400149_ashockelemental.png",
        	 level: 39,
        	 attack: 2,
        	 defence: 2,
        	 stun: 30,
        	 powers: ["$MSPW_STUN|P|20|3|0|6"],
        	 maxHitPotential: "m,m,m",
        	 drops: [
        		["Evening Sun Energy Shield", 100]
        	 ],
        	 map: []
        	},
        	"spectral elemental": {
        	 id: 400150,
        	 image: "./images/png/monsters/400150_aspectralelemental.png",
        	 level: 39,
        	 attack: 2,
        	 defence: 4,
        	 heat: 10,
        	 cold: 10,
        	 stun: 10,
        	 powers: ["$MSPW_DISABLE|P|20|10,4|0|10"],
        	 drops: [
        		["Evening Sun Energy Shield", 100]
        	 ],
        	 map: []
        	},
        	"fire elemental": {
        	 id: 400151,
        	 image: "./images/png/monsters/400151_afireelemental.png",
        	 level: 39,
        	 attack: 2,
        	 defence: 2,
        	 heat: 30,
        	 powers: ["$MSPW_FIREBLAST|P|20|100|0|6"],
        	 maxHitPotential: "m|100",
        	 drops: [
        		["Inferno Robe", 100]
        	 ],
        	 map: []
        	},
        	"life elemental": {
        	 id: 400152,
        	 image: "./images/png/monsters/400152_alifeelemental.png",
        	 level: 39,
        	 attack: 2,
        	 defence: 2,
        	 hpNormal: 300,
        	 powers: ["$MSPW_HEAL|P|20|P,20|0|7"],
        	 drops: [
        		["Inferno Robe", 50],
        		["Evening Sun Energy Shield", 50]
        	 ],
        	 map: []
        	},
        	"ice elemental": {
        	 id: 400153,
        	 image: "./images/png/monsters/400153_aniceelemental.png",
        	 level: 39,
        	 attack: 2,
        	 defence: 2,
        	 cold: 30,
        	 powers: ["$MSPW_ICEBLAST|P|20|80|0|5"],
        	 maxHitPotential: "m|80",
        	 drops: [
        		["Inferno Robe", 100]
        	 ],
        	 map: []
        	},
        	"the guardian of shock magic": {
        	 id: 400154,
        	 image: "./images/png/monsters/400154_VnaeF_theGuardianofShockMagic.png",
        	 level: 40,
        	 attack: 3,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 200,
        	 xp: 500,
        	 np: {normal: 500, evil: 1000, insane: 1500},
        	 powers: ["$MSPW_STUN|P|40|4|10|15", "$MSPW_LIFETAP|P|15|70|0|7"],
        	 maxHitPotential: "m|70,m,m,m",
        	 drops: [
        		["Thunderstar Staff",1000]
        	 ],
        	 hpNormal: 320
        	},
        	"the guardian of spectral magic": {
        	 id: 400155,
        	 image: "./images/png/monsters/400155_AasWK_theGuardianofSpectralMagic.png",
        	 level: 40,
        	 attack: 3,
        	 defence: 6,
        	 damage: 3,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 xp: 500,
        	 np: {normal: 500, evil: 1000, insane: 1500},
        	 powers: ["$MSPW_DISABLE|P|40|30,5|10|12", "$MSPW_LIFETAP|P|15|70|0|7"],
        	 maxHitPotential: "m|70",
        	 drops: [
        		["Shadowgem Staff",1000]
        	 ],
        	 hpNormal: 320
        	},
        	"the guardian of fire magic": {
        	 id: 400156,
        	 image: "./images/png/monsters/400156_NQRQW_theGuardianofFireMagic.png",
        	 level: 40,
        	 attack: 3,
        	 defence: 3,
        	 damage: 3,
        	 heat: 200,
        	 cold: 30,
        	 stun: 30,
        	 xp: 500,
        	 np: {normal: 500, evil: 1000, insane: 1500},
        	 powers: ["$MSPW_FIREBLAST|P|40|90|0|5", "$MSPW_LIFETAP|P|15|70|0|7"],
        	 maxHitPotential: "m|90",
        	 drops: [
        		["Firedrop Staff",1000]
        	 ],
        	 hpNormal: 320
        	},
        	"the guardian of life magic": {
        	 id: 400157,
        	 image: "./images/png/monsters/400157_qlfCy_theGuardianofLifeMagic.png",
        	 level: 40,
        	 attack: 3,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 xp: 500,
        	 np: {normal: 500, evil: 1000, insane: 1500},
        	 powers: ["$MSPW_HEAL|P|40|A,75|0|8", "$MSPW_LIFETAP|P|15|70|0|7"],
        	 maxHitPotential: "m|70",
        	 drops: [
        		["Moonstone Staff",1000]
        	 ],
        	 hpNormal: 450
        	},
        	"the guardian of ice magic": {
        	 id: 400158,
        	 image: "./images/png/monsters/400158_iYhMr_theGuardianofIceMagic.png",
        	 level: 40,
        	 attack: 3,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 200,
        	 stun: 30,
        	 xp: 500,
        	 np: {normal: 500, evil: 1000, insane: 1500},
        	 powers: ["$MSPW_ICEBLAST|P|40|70|0|4", "$MSPW_LIFETAP|P|15|70|0|7"],
        	 maxHitPotential: "m|70",
        	 drops: [
        		["Iceheart Staff",1000]
        	 ],
        	 hpNormal: 320
        	},
        	"storm chia giant": {
        	 id: 400159,
        	 image: "./images/png/monsters/400159_astormgiant.png",
        	 level: 35,
        	 defence: 1,
        	 damage: 1,
        	 powers: ["$MSPW_STUN|P|20|1|0|8"],
        	 drops: [],
        	 map: []
        	},
        	"shadow kougra": {
        	 id: 400160,
        	 image: "./images/png/monsters/400160_ashadowbeast.png",
        	 level: 35,
        	 attack: 2,
        	 powers: ["$MSPW_LIFETAP|P|20|35|0|7"],
        	 maxHitPotential: "m|35",
        	 drops: [],
        	 map: []
        	},
        	"thunder chia giant": {
        	 id: 400161,
        	 image: "./images/png/monsters/400161_athundergiant.png",
        	 level: 36,
        	 defence: 1,
        	 damage: 1,
        	 powers: ["$MSPW_STUNBLAST|P|10|40,1|0|8"],
        	 maxHitPotential: "40,m",
        	 drops: [],
        	 map: []
        	},
        	"vapour kougra": {
        	 id: 400162,
        	 image: "./images/png/monsters/400162_avaporbeast.png",
        	 level: 36,
        	 attack: 2,
        	 powers: ["$MSPW_LIFETAP|P|20|40|0|7"],
        	 maxHitPotential: "m|40",
        	 drops: [],
        	 map: []
        	},
        	"lightning chia giant": {
        	 id: 400163,
        	 image: "./images/png/monsters/400163_alightninggiant.png",
        	 level: 37,
        	 defence: 1,
        	 damage: 1,
        	 powers: ["$MSPW_STUNBLAST|P|15|45,1|0|7"],
        	 maxHitPotential: "45,m",
        	 drops: [],
        	 map: []
        	},
        	"mist kougra": {
        	 id: 400164,
        	 image: "./images/png/monsters/400164_amistbeast.png",
        	 level: 37,
        	 attack: 2,
        	 powers: ["$MSPW_LIFETAP|P|20|45|0|6"],
        	 maxHitPotential: "m|45",
        	 drops: [],
        	 map: []
        	},
        	"frost chia giant": {
        	 id: 400165,
        	 image: "./images/png/monsters/400165_afrostgiant.png",
        	 level: 38,
        	 defence: 1,
        	 damage: 1,
        	 powers: ["$MSPW_ICESTUN|P|15|45,1|-20|7"],
        	 maxHitPotential: "45,m",
        	 drops: [],
        	 map: []
        	},
        	"blizzard kougra": {
        	 id: 400166,
        	 image: "./images/png/monsters/400166_ablizzardbeast.png",
        	 level: 38,
        	 attack: 2,
        	 powers: ["$MSPD_ICEBLAST|P|20|60|-10|7"],
        	 maxHitPotential: "m|60",
        	 drops: [],
        	 map: []
        	},
        	"reaver chia giant": {
        	 id: 400167,
        	 image: "./images/png/monsters/400167_areavergiant.png",
        	 level: 38,
        	 defence: 1,
        	 damage: 1,
        	 powers: ["$MSPW_LIFETAP|P|15|50|0|8"],
        	 maxHitPotential: "m|50",
        	 drops: [],
        	 map: []
        	},
        	"gloom kougra": {
        	 id: 400168,
        	 image: "./images/png/monsters/400168_agloombeast.png",
        	 level: 38,
        	 attack: 2,
        	 powers: ["$MSPW_DISABLE|P|20|20,4|0|8", "$MSPW_LIFETAP|P|10|30|0|8"],
        	 maxHitPotential: "m|30",
        	 drops: [],
        	 map: []
        	},
        	"chaos giant": {
        	 id: 400169,
        	 image: "./images/png/monsters/400169_achaosgiant.png",
        	 level: 39,
        	 defence: 1,
        	 damage: 1,
        	 powers: ["$MSPW_LIFETAP|P|15|50|0|7", "$MSPW_STUNBLAST|P|15|60,1|0|6"],
        	 maxHitPotential: "60,m|50",
        	 drops: [],
        	 map: []
        	},
        	"dusk kougra": {
        	 id: 400170,
        	 image: "./images/png/monsters/400170_aduskbeast.png",
        	 level: 39,
        	 attack: 2,
        	 powers: ["$MSPW_DISABLE|P|20|20,5|0|8", "$MSPW_LIFETAP|P|10|40|0|7"],
        	 maxHitPotential: "m|40",
        	 drops: [],
        	 map: []
        	},
        	"undead moehog": {
        	 id: 400171,
        	 image: "./images/png/monsters/400171_anundeadfarmer.png",
        	 level: 39,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 powers: ["$MSPW_LIFETAP|P|15|50|0|7"],
        	 maxHitPotential: "m|50",
        	 drops: [],
        	 map: []
        	},
        	"undead patrolman": {
        	 id: 400172,
        	 image: "./images/png/monsters/400172_anundeadpatrolman.png",
        	 level: 39,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 powers: ["$MSPW_LIFETAP|P|15|50|-10|7"],
        	 maxHitPotential: "m|50",
        	 drops: [],
        	 map: []
        	},
        	"undead cybunny": {
        	 id: 400173,
        	 image: "./images/png/monsters/400173_anundeadcitizen.png",
        	 level: 40,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 powers: ["$MSPW_LIFETAP|P|15|55|-10|7"],
        	 maxHitPotential: "m|55",
        	 drops: [],
        	 map: []
        	},
        	"undead city guard": {
        	 id: 400174,
        	 image: "./images/png/monsters/400174_anundeadcityguard.png",
        	 level: 40,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 powers: ["$MSPW_LIFETAP|P|15|55|-20|7"],
        	 maxHitPotential: "m|55",
        	 drops: [],
        	 map: []
        	},
        	"undead grundo shopkeeper": {
        	 id: 400175,
        	 image: "./images/png/monsters/400175_anundeadshopkeeper.png",
        	 level: 41,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 powers: ["$MSPW_LIFETAP|P|20|60|-20|7"],
        	 maxHitPotential: "m|60",
        	 drops: [],
        	 map: []
        	},
        	"undead guard captain": {
        	 id: 400176,
        	 image: "./images/png/monsters/400176_anundeadguardcaptain.png",
        	 level: 41,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 powers: ["$MSPW_LIFETAP|P|20|60|-30|7"],
        	 maxHitPotential: "m|60",
        	 drops: [],
        	 map: []
        	},
        	"undead council member": {
        	 id: 400177,
        	 image: "./images/png/monsters/400177_anundeadcouncilmember.png",
        	 level: 42,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 powers: ["$MSPW_LIFETAP|P|15|70|-30|6"],
        	 maxHitPotential: "m|70",
        	 drops: [],
        	 map: []
        	},
        	"undead guard commander": {
        	 id: 400178,
        	 image: "./images/png/monsters/400178_anundeadguardcommander.png",
        	 level: 42,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 powers: ["$MSPW_LIFETAP|P|15|70|-40|6"],
        	 maxHitPotential: "m|70",
        	 drops: [],
        	 map: []
        	},
        	"two rings knight": {
        	 id: 400180,
        	 image: "./images/png/monsters/400180_atworingsknight.png",
        	 level: 41,
        	 attack: 1,
        	 defence: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"two rings wizard": {
        	 id: 400181,
        	 image: "./images/png/monsters/400181_atworingswizard.png",
        	 level: 41,
        	 powers: ["$MSPW_FIREBLAST|P|20|100|20|8"],
        	 maxHitPotential: "m|100",
        	 drops: [],
        	 map: []
        	},
        	"two rings cavalier": {
        	 id: 400182,
        	 image: "./images/png/monsters/400182_atworingscavalier.png",
        	 level: 42,
        	 attack: 1,
        	 defence: 2,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"two rings sorcerer": {
        	 id: 400183,
        	 image: "./images/png/monsters/400183_atworingssorcerer.png",
        	 level: 42,
        	 powers: ["$MSPW_ICEBLAST|P|20|100|20|8"],
        	 maxHitPotential: "m|100",
        	 drops: [],
        	 map: []
        	},
        	"two rings paladin": {
        	 id: 400184,
        	 image: "./images/png/monsters/400184_atworingspaladin.png",
        	 level: 43,
        	 attack: 2,
        	 defence: 2,
        	 damage: 1,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"two rings warlock": {
        	 id: 400185,
        	 image: "./images/png/monsters/400185_atworingswarlock.png",
        	 level: 43,
        	 powers: ["$MSPW_FIRESTUN|P|20|80,1|20|8"],
        	 maxHitPotential: "80,m",
        	 drops: [],
        	 map: []
        	},
        	"two rings crusader": {
        	 id: 400186,
        	 image: "./images/png/monsters/400186_atworingscrusader.png",
        	 level: 44,
        	 attack: 2,
        	 defence: 2,
        	 damage: 2,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"two rings archmagus": {
        	 id: 400187,
        	 image: "./images/png/monsters/400187_atworingsarchmagus.png",
        	 level: 44,
        	 powers: ["$MSPW_ICESTUN|P|20|80,1|20|8"],
        	 maxHitPotential: "80,m",
        	 drops: [],
        	 map: []
        	},
        	"palace guard": {
        	 id: 400188,
        	 image: "./images/png/monsters/400188_apalaceguard.png",
        	 level: 45,
        	 attack: 10,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"palace servant": {
        	 id: 400189,
        	 image: "./images/png/monsters/400189_apalaceservant.png",
        	 level: 45,
        	 attack: 5,
        	 defence: 2,
        	 damage: 1,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: ["$MSPW_LIFETAP|P|20|100|0|10"],
        	 maxHitPotential: "m|100",
        	 drops: [],
        	 map: []
        	},
        	"palace guardian": {
        	 id: 400190,
        	 image: "./images/png/monsters/400190_apalaceguardian.png",
        	 level: 46,
        	 attack: 10,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: ["$MSPW_LIFETAP|P|20|100|0|10"],
        	 maxHitPotential: "m|100",
        	 drops: [],
        	 map: []
        	},
        	"palace gardener": {
        	 id: 400191,
        	 image: "./images/png/monsters/400191_apalacegardener.png",
        	 level: 46,
        	 attack: 5,
        	 defence: 2,
        	 damage: 1,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: ["$MSPW_LIFETAP|P|20|100|0|10"],
        	 maxHitPotential: "m|100",
        	 drops: [],
        	 map: []
        	},
        	"palace sentry": {
        	 id: 400192,
        	 image: "./images/png/monsters/400192_apalacesentry.png",
        	 level: 47,
        	 attack: 10,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"palace groundskeeper": {
        	 id: 400193,
        	 image: "./images/png/monsters/400193_apalacegroundskeeper.png",
        	 level: 47,
        	 attack: 5,
        	 defence: 2,
        	 damage: 1,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: ["$MSPW_LIFETAP|P|20|100|0|10"],
        	 maxHitPotential: "m|100",
        	 drops: [],
        	 map: []
        	},
        	"palace captain": {
        	 id: 400194,
        	 image: "./images/png/monsters/400194_apalacecaptain.png",
        	 level: 48,
        	 attack: 10,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"palace clerk": {
        	 id: 400195,
        	 image: "./images/png/monsters/400195_apalaceclerk.png",
        	 level: 48,
        	 attack: 5,
        	 defence: 2,
        	 damage: 1,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: ["$MSPW_LIFETAP|P|20|100|0|10"],
        	 maxHitPotential: "m|100",
        	 drops: [],
        	 map: []
        	},
        	"palace commander": {
        	 id: 400196,
        	 image: "./images/png/monsters/400196_apalacecommander.png",
        	 level: 49,
        	 attack: 10,
        	 defence: 3,
        	 damage: 3,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: [],
        	 drops: [],
        	 map: []
        	},
        	"palace major domo": {
        	 id: 400197,
        	 image: "./images/png/monsters/400197_apalacemajordomo.png",
        	 level: 49,
        	 attack: 5,
        	 defence: 2,
        	 damage: 2,
        	 heat: 30,
        	 cold: 30,
        	 stun: 30,
        	 powers: ["$MSPW_LIFETAP|P|20|100|0|10"],
        	 maxHitPotential: "m|100",
        	 drops: [],
        	 map: []
        	},
        	"mummy": {
        	 id: 4000821,
        	 image: "./images/png/monsters/4000821_amummy.png",
        	 level: 18,
        	 powers: ["$MSPW_LIFETAP|P|10|20|0|8"],
        	 maxHitPotential: "m|20",
        	 drops: [],
        	 map: []
        	},
        	"faleinn": {
        	 id: 400179,
        	 image: "./images/png/monsters/400179_DpvXh_Faleinn.png",
        	 level: 45,
        	 attack: 5,
        	 defence: 5,
        	 damage: 3,
        	 heat: 60,
        	 cold: 60,
        	 stun: 60,
        	 xp: 1500,
        	 np: {normal: 1500, evil: 1500, insane: 1500},
        	 powers: ["$MSPW_STUNBLAST|P|40|70,3|0|5","$MSPW_FIREBLAST|P|40|70|0|5","$MSPW_ICEBLAST|P|40|70|0|5","$MSPW_LIFETAP|P|40|90|0|5"],
        	 maxHitPotential: "70,m|70,m|70,m|90",
        	 drops: [
        		["The Key to the Two Rings", 1000]
        	 ],
        	 hpNormal: 500,
        	},
        	"jahbal": {
        	 id: 400198,
        	 image: "./images/png/monsters/400198_VRhlK_Jahbal.png",
        	 level: 50,
        	 attack: 10,
        	 defence: 5,
        	 damage: 5,
        	 heat: 50,
        	 cold: 50,
        	 stun: 50,
        	 xp: 5000,
        	 np: {normal: 5000, evil: 5000, insane: 5000},
        	 powers: ["$MSPW_POISON|P|6|50,5|0|8","$MSPW_STUNBLAST|P|6|70,3|0|8","$MSPW_FIREBLAST|P|6|90|0|8","$MSPW_ICEBLAST|P|6|90|0|8","$MSPW_HEAL|P|6|A,150|0|10","$MSPW_LIFETAP|P|6|100|0|8","$MSPW_DISABLE|P|6|30,5|0|8"],
        	 maxHitPotential: "270,m|90,m|100,m|90",
        	 drops: [],
        	 hpNormal: 700,
        	},
        	"mastermind": {
        	 id: 400199,
        	 image: "./images/png/monsters/400199_GWcxJ_Mastermind.png",
        	 level: 50,
        	 attack: 10,
        	 defence: 5,
        	 damage: 10,
        	 heat: 60,
        	 cold: 60,
        	 stun: 60,
        	 xp: 7500,
        	 np: {normal: 7500, evil: 7500, insane: 7500},
        	 powers: ["$MSPW_LIFEDRAIN|P|20|50,3|0|20","$MSPW_FIREBLAST|P|5|120|0|15","$MSPW_ICEBLAST|P|5|120|0|15"],
        	 maxHitPotential: "170",
        	 drops: [],
        	 hpNormal: 0,
        	 hpEvil: 1000
        	},
        	"xantan reborn": {
        	 id: 400200,
        	 image: "./images/png/monsters/400200_aeluC_XantanReborn.png",
        	 level: 50,
        	 attack: 10000,
        	 defence: -10000,
        	 fixeddamage: 50,
        	 maxHitPotential: "50",
        	 heat: 5,
        	 cold: 5,
        	 stun: 5,
        	 xp: 10000,
        	 np: {normal: 10000, evil: 10000, insane: 10000},
        	 powers: [],
        	 drops: [],
        	 hpNormal: 0,
        	 hpEvil: 1000
        	}
         },
         //https://images.neopets .com/nq/n/
         npcs: {
        	"Eleus Batrin": {
        	 image: "./images/png/npcs/npc_eleus.png",
        	 mapImage: "",
        	 mapName: "world_crop_1",
        	 canGive: ["Gold Wand", "Steel Wand", "Bronze Wand", "Iron Wand", "Silver Wand", ],
        	},
        	"Boraxis the Healer": {
        	 image: "./images/png/npcs/npc_boraxis.png",
        	 mapImage: "",
        	 canGive: [],
        	},
        	"Lummock Sendent": {
        	 image: "./images/png/npcs/npc_lummock.png",
        	 mapImage: "",
        	 canGive: ["Red Wand", "Blue Wand", "Yellow Wand", "Black Wand", "White Wand", ],
        	},
        	"Morax Dorangis": {
        	 image: "./images/png/npcs/npc_morax.png",
        	 mapImage: "",
        	 canGive: ["Energy Shield", "Cloth Robe"],
        	},
        	"Choras Tillie": {
        	 image: "./images/png/npcs/npc_choras.png",
        	 mapImage: "",
        	 canGive: ["Mirrored Force Field", "Magic Robe"],
        	},
        	"Rikti": {
        	 image: "./images/png/npcs/npc_rikti.png",
        	 mapImage: "./images/png/tiles/npc_rikti.png",
        	 canGive: [],
        	},
        	"Margoreth": {
        	 image: "./images/png/npcs/npc_margoreth.png",
        	 mapImage: "./images/png/tiles/npc_margoreth.png",
        	 canGive: [],
        	},
        	"Tylix": {
        	 image: "./images/png/npcs/npc_tylix.png",
        	 mapImage: "./images/png/tiles/npc_tylix.png",
        	 canGive: [],
        	},
        	"Pomanna": {
        	 image: "./images/png/npcs/npc_pomanna.png",
        	 mapImage: "./images/png/tiles/npc_pomanna.png",
        	 canGive: [],
        	},
        	"Denethrir": {
        	 image: "./images/png/npcs/npc_denethrir.png",
        	 mapImage: "./images/png/tiles/npc_denethrir.png",
        	 canGive: ["Volcano Wand", "Glacier Wand", "Storm Wand", "Mountain Wand", "Nature Wand", ],
        	},
        	"Korabric": {
        	 image: "./images/png/npcs/npc_korabric.png",
        	 mapImage: "./images/png/tiles/npc_korabric.png",
        	 canGive: [],
        	},
        	"Mokti": {
        	 image: "./images/png/npcs/npc_mokti.png",
        	 mapImage: "",
        	 canGive: ["Dawnshine Generator Shield"],
        	},
        	"Leirobas": {
        	 image: "./images/png/npcs/npc_leirobas.png",
        	 mapImage: "",
        	 canGive: ["Sorcerous Robe", "Blazing Jewel", "Chilling Jewel", "Stunning Jewel", "Radiant Jewel", "Growing Jewel", ],
        	},
        	"Erick": {
        	 image: "./images/png/npcs/npc_erick.png",
        	 mapImage: "./images/png/tiles/npc_erick.png",
        	 canGive: ["Coruscating Gem", "Fire Staff", "Ice Staff", "Shock Staff", "Spectral Staff", "Life Staff", ],
        	},
        	"A Note": {
        	 image: "./images/png/npcs/npc_note.png",
        	 mapImage: "",
        	 canGive: [],
        	},
        	"Mr Irgo": {
        	 image: "./images/png/npcs/npc_irgo.png",
        	 mapImage: "./images/png/tiles/npc_irgo.png",
        	 canGive: ["Energy Absorber", "Robe of Protection"],
        	},
        	"Gali Yoj": {
        	 image: "./images/png/npcs/npc_galiyoj.png",
        	 mapImage: "",
        	 canGive: ["Keladrian Medallion"],
        	},
        	"Faleinn": {
        	 image: "./images/png/npcs/npc_faleinn.png",
        	 mapImage: "",
        	 canGive: ["The Key to the Two Rings"],
        	},
        	"The Gatekeeper": {
        	 image: "./images/png/npcs/npc_gatekeeper.png",
        	 mapImage: "./images/png/tiles/npc_gatekeeper.png",
        	 canGive: [],
        	},
         },
         items: {
        	"glowing stone": {
        	 maxqty: 20,
        	 usedfor: "energy shield, cloth robe, mirrored force field, magic robe, gold wand, steel wand, bronze wand, iron wand, silver wand, volcano wand, glacier wand, storm wand, mountain wand, nature wand, blazing jewel, chilling jewel, stunning jewel, radiant jewel, growing jewel",
        	},
        	"chunk of metal": {
        	 maxqty: 1,
        	 usedfor: "energy shield",
        	},
        	"small yellow gem": {
        	 maxqty: 1,
        	 usedfor: "energy shield",
        	},
        	"plains lupe pelt": {
        	 maxqty: 1,
        	 usedfor: "cloth robe",
        	},
        	"blue thread": {
        	 maxqty: 1,
        	 usedfor: "magic robe",
        	},
        	"cave lupe pelt": {
        	 maxqty: 1,
        	 usedfor: "magic robe",
        	},
        	"tiny amber": {
        	 maxqty: 5,
        	 usedfor: "bronze wand, stunning jewel",
        	},
        	"tiny obsidian": {
        	 maxqty: 5,
        	 usedfor: "iron wand, radiant jewel",
        	},
        	"tiny beryl": {
        	 maxqty: 5,
        	 usedfor: "silver wand, growing jewel",
        	},
        	"corroded pyrite rod": {
        	 maxqty: 1,
        	 usedfor: "gold wand",
        	},
        	"corroded pewter rod": {
        	 maxqty: 1,
        	 usedfor: "steel wand",
        	},
        	"corroded copper rod": {
        	 maxqty: 1,
        	 usedfor: "bronze wand",
        	},
        	"corroded ore rod": {
        	 maxqty: 1,
        	 usedfor: "iron wand",
        	},
        	"corroded aluminum rod": {
        	 maxqty: 1,
        	 usedfor: "silver wand",
        	},
        	"grey lupe fang": {
        	 maxqty: 5,
        	 usedfor: "red wand, blue wand",
        	},
        	"black bearog paw": {
        	 maxqty: 5,
        	 usedfor: "red wand, black wand, white wand",
        	},
        	"grizzly bearog tooth": {
        	 maxqty: 5,
        	 usedfor: "red wand, yellow wand",
        	},
        	"dire lupe pelt": {
        	 maxqty: 5,
        	 usedfor: "blue wand, black wand",
        	},
        	"piece of smooth glass": {
        	 maxqty: 1,
        	 usedfor: "mirrored force field",
        	},
        	"lodestone": {
        	 maxqty: 1,
        	 usedfor: "mirrored force field",
        	},
        	"stretch of rotted cloth": {
        	 maxqty: 1,
        	 usedfor: "magic robe",
        	},
        	"armored stinger": {
        	 maxqty: 1,
        	 usedfor: "volcano wand",
        	},
        	"noils mane": {
        	 maxqty: 1,
        	 usedfor: "volcano wand",
        	},
        	"shamanistic totem": {
        	 maxqty: 1,
        	 usedfor: "glacier wand",
        	},
        	"skeith fang": {
        	 maxqty: 1,
        	 usedfor: "glacier wand",
        	},
        	"buzz wing": {
        	 maxqty: 1,
        	 usedfor: "storm wand",
        	},
        	"wadjet skin": {
        	 maxqty: 1,
        	 usedfor: "storm wand",
        	},
        	"scorpion carapace": {
        	 maxqty: 1,
        	 usedfor: "mountain wand",
        	},
        	"wooden shield": {
        	 maxqty: 1,
        	 usedfor: "mountain wand",
        	},
        	"jungle beast claw": {
        	 maxqty: 1,
        	 usedfor: "nature wand",
        	},
        	"noils tooth": {
        	 maxqty: 1,
        	 usedfor: "nature wand",
        	},
        	"jungle gauntlet": {
        	 maxqty: 1,
        	 usedfor: "volcano wand",
        	},
        	"jungle vambrace": {
        	 maxqty: 1,
        	 usedfor: "glacier wand",
        	},
        	"jungle breastplate": {
        	 maxqty: 1,
        	 usedfor: "storm wand",
        	},
        	"jungle helm": {
        	 maxqty: 1,
        	 usedfor: "mountain wand",
        	},
        	"jungle pauldrons": {
        	 maxqty: 1,
        	 usedfor: "nature wand",
        	},
        	"giant spider leg": {
        	 maxqty: 1,
        	 usedfor: "dawnshine generator shield",
        	},
        	"desert cobra fang": {
        	 maxqty: 1,
        	 usedfor: "dawnshine generator shield",
        	},
        	"sand iguana eye": {
        	 maxqty: 1,
        	 usedfor: "dawnshine generator shield",
        	},
        	"dust spider pincer": {
        	 maxqty: 1,
        	 usedfor: "dawnshine generator shield",
        	},
        	"drop of giant spider blood": {
        	 maxqty: 1,
        	 usedfor: "sorcerous robe",
        	},
        	"drop of desert cobra venom": {
        	 maxqty: 1,
        	 usedfor: "sorcerous robe",
        	},
        	"glob of dried iguana spit": {
        	 maxqty: 1,
        	 usedfor: "sorcerous robe",
        	},
        	"pinch of crystallized sand": {
        	 maxqty: 1,
        	 usedfor: "sorcerous robe",
        	},
        	"copper-plated key": {
        	 maxqty: 1,
        	 usedfor: "opens the first locked door inside Temple of Roo",
        	},
        	"bronze-plated key": {
        	 maxqty: 1,
        	 usedfor: "opens the second locked door inside Temple of Roo",
        	},
        	"silver-plated key": {
        	 maxqty: 1,
        	 usedfor: "opens the third locked door inside Temple of Roo",
        	},
        	"gold-plated key": {
        	 maxqty: 1,
        	 usedfor: "opens the fourth locked door inside Temple of Roo",
        	},
        	"platinum-plated key": {
        	 maxqty: 1,
        	 usedfor: "opens the fifth locked door inside Temple of Roo",
        	},
        	"crystalline key": {
        	 maxqty: 1,
        	 usedfor: "opens the sixth locked door inside Temple of Roo",
        	},
        	"ruby": {
        	 maxqty: 5,
        	 usedfor: "blazing jewel",
        	},
        	"sapphire": {
        	 maxqty: 5,
        	 usedfor: "chilling jewel",
        	},
        	"topaz": {
        	 maxqty: 5,
        	 usedfor: "stunning jewel",
        	},
        	"onyx": {
        	 maxqty: 5,
        	 usedfor: "radiant jewel",
        	},
        	"emerald": {
        	 maxqty: 5,
        	 usedfor: "growing jewel",
        	},
        	"carved oak staff": {
        	 maxqty: 5,
        	 usedfor: "fire staff, ice staff, shock staff, spectral staff, life staff",
        	},
        	"piece of living crystal": {
        	 maxqty: 1,
        	 usedfor: "Leirobas",
        	},
        	"piece of agate": {
        	 maxqty: 1,
        	 usedfor: "energy absorber",
        	},
        	"piece of chrysolite": {
        	 maxqty: 1,
        	 usedfor: "energy absorber",
        	},
        	"piece of serpentine": {
        	 maxqty: 1,
        	 usedfor: "energy absorber",
        	},
        	"drakonid eye": {
        	 maxqty: 1,
        	 usedfor: "robe of protection",
        	},
        	"drakonid hide": {
        	 maxqty: 1,
        	 usedfor: "robe of protection",
        	},
        	"drakonid heart": {
        	 maxqty: 1,
        	 usedfor: "robe of protection",
        	},
        	// "growing jewel": {
        	//  maxqty: 1,
        	//  usedfor: "",
        	// },
        	"energy shield": {
        	 itemtype: "armour",
        	 armour: "energy",
        	 points: 3,
        	 special: "none",
        	 materials: [],
        	},
        	"mirrored force field": {
        	 itemtype: "armour",
        	 armour: "energy",
        	 points: 6,
        	 special: "none",
        	},
        	"dawnshine generator shield": {
        	 itemtype: "armour",
        	 armour: "energy",
        	 points: 10,
        	 special: "2% chance to reflect a regular enemy attack",
        	},
        	"energy absorber": {
        	 itemtype: "armour",
        	 armour: "energy",
        	 points: 14,
        	 special: "5% chance for a regular enemy attack to heal you",
        	},
        	"evening sun energy shield": {
        	 itemtype: "armour",
        	 armour: "energy",
        	 points: 18,
        	 special: "15% chance to inflict 20 damage when a regular enemy attack damages you",
        	},
        	"cloth robe": {
        	 itemtype: "armour",
        	 armour: "magical",
        	 points: 3,
        	 special: "none",
        	},
        	"magic robe": {
        	 itemtype: "armour",
        	 armour: "magical",
        	 points: 6,
        	 special: "none",
        	},
        	"sorcerous robe": {
        	 itemtype: "armour",
        	 armour: "magical",
        	 points: 10,
        	 special: "increases your attack stat by 5% (which increases your chance to hit)",
        	},
        	"robe of protection": {
        	 itemtype: "armour",
        	 armour: "magical",
        	 points: 14,
        	 special: "10% chance for a regular enemy attack to do 0 damage",
        	},
        	"inferno robe": {
        	 itemtype: "armour",
        	 armour: "magical",
        	 points: 18,
        	 special: "adds 1-10 damage to your regular attack",
        	},
        	//weapons
        	"white wand": {
        	 itemtype: "weapon",
        	 weapon: "life",
        	 points: 3,
        	 special: "",
        	},
        	"silver wand": {
        	 itemtype: "weapon",
        	 weapon: "life",
        	 points: 6,
        	 special: "",
        	},
        	"nature wand": {
        	 itemtype: "weapon",
        	 weapon: "life",
        	 points: 10,
        	 special: "6% chance each turn to heal 25 health",
        	},
        	"life staff": {
        	 itemtype: "weapon",
        	 weapon: "life",
        	 points: 15,
        	 special: "can cast Spirit of Growth: heals 100 health; 20 turn cooldown",
        	},
        	"moonstone staff": {
        	 itemtype: "weapon",
        	 weapon: "life",
        	 points: 20,
        	 special: "heals 4 health each turn and 10% chance each turn to heal 30 health",
        	},
        	"yellow wand": {
        	 itemtype: "weapon",
        	 weapon: "shock",
        	 points: 3,
        	 special: "",
        	},
        	"bronze wand": {
        	 itemtype: "weapon",
        	 weapon: "shock",
        	 points: 6,
        	 special: "",
        	},
        	"storm wand": {
        	 itemtype: "weapon",
        	 weapon: "shock",
        	 points: 10,
        	 special: "3% chance to stun an enemy for 1 turn",
        	},
        	"shock staff": {
        	 itemtype: "weapon",
        	 weapon: "shock",
        	 points: 15,
        	 special: "can cast Weakness: lowers an enemy's defence by 5 for 3 turns; 8 turn cooldown",
        	},
        	"thunderstar staff": {
        	 itemtype: "weapon",
        	 weapon: "shock",
        	 points: 20,
        	 special: "8% chance to stun an enemy for 2 turns",
        	},
        	"red wand": {
        	 itemtype: "weapon",
        	 weapon: "fire",
        	 points: 3,
        	 special: "",
        	},
        	"gold wand": {
        	 itemtype: "weapon",
        	 weapon: "fire",
        	 points: 6,
        	 special: "",
        	},
        	"volcano wand": {
        	 itemtype: "weapon",
        	 weapon: "fire",
        	 points: 10,
        	 special: "3% chance to add 20 fire damage to your normal attack",
        	},
        	"fire staff": {
        	 itemtype: "weapon",
        	 weapon: "fire",
        	 points: 15,
        	 special: "can cast Magma Blast: inflicts 40 fire damage; 10 turn cooldown",
        	},
        	"firedrop staff": {
        	 itemtype: "weapon",
        	 weapon: "fire",
        	 points: 20,
        	 heat: 10,
        	 special: "8% chance to inflict 30 fire damage for 2 turns when using a normal attack",
        	},
        	"blue wand": {
        	 itemtype: "weapon",
        	 weapon: "ice",
        	 points: 3,
        	 special: "",
        	},
        	"steel wand": {
        	 itemtype: "weapon",
        	 weapon: "ice",
        	 points: 6,
        	 special: "",
        	},
        	"glacier wand": {
        	 itemtype: "weapon",
        	 weapon: "ice",
        	 points: 10,
        	 special: "3% chance to add 20 ice damage to your normal attack",
        	},
        	"ice staff": {
        	 itemtype: "weapon",
        	 weapon: "ice",
        	 points: 15,
        	 special: "can cast Ice Shield: an enemy takes 10 damage when you are hit with a normal attack; 12 turn cooldown",
        	},
        	"iceheart staff": {
        	 itemtype: "weapon",
        	 weapon: "ice",
        	 points: 20,
        	 cold: 10,
        	 special: "can cast Ice Wind: on each turn, for the next 5 turns, 40% chance an enemy cannot move; 10 turn cooldown",
        	},
        	"black wand": {
        	 itemtype: "weapon",
        	 weapon: "spectral",
        	 points: 3,
        	 special: "",
        	},
        	"iron wand": {
        	 itemtype: "weapon",
        	 weapon: "spectral",
        	 points: 6,
        	 special: "",
        	},
        	"mountain wand": {
        	 itemtype: "weapon",
        	 weapon: "spectral",
        	 points: 10,
        	 special: "when you use a normal attack, 4% chance to activate Absorbing Stone: you can absorb up to 15 damage from an enemy's normal attack, when 15 damage is absorbed, the effect ends and may activate again",
        	},
        	"spectral staff": {
        	 itemtype: "weapon",
        	 weapon: "spectral",
        	 points: 15,
        	 special: "can cast Rage of Light: for 5 turns, you gain 3 attack and 3 defence; 10 turn cooldown",
        	},
        	"shadowgem staff": {
        	 itemtype: "weapon",
        	 weapon: "spectral",
        	 points: 20,
        	 special: "+3 defence bonus; can cast Elemental Resistance: for 4 turns, you gain 12 in these stats: heat, cold, and stun; 9 turn cooldown",
        	},
        	"weak healing potion": {
        	 maxqty: 30,
        	 heals: 10,
        	 dropLevelRange: [-2, 15]
        	},
        	"standard healing potion": {
        	 maxqty: 20,
        	 heals: 30,
        	 dropLevelRange: [5, 21]
        	},
        	"strong healing potion": {
        	 maxqty: 20,
        	 heals: 60,
        	 dropLevelRange: [16, 30]
        	},
        	"greater healing potion": {
        	 maxqty: 15,
        	 heals: 90,
        	 dropLevelRange: [22, 38]
        	},
        	"superior healing potion": {
        	 maxqty: 15,
        	 heals: 120,
        	 dropLevelRange: [31, 50]
        	},
        	"spirit healing potion": {
        	 maxqty: 10,
        	 heals: 150,
        	 dropLevelRange: [45, 50]
        	},
         },
         skills: {
        	s1001: {
        	 type: "fire",
        	 name: "Fire Weapons",
        	 desc: "May add fire damage to your attack. When not using a fire-type weapon, this damage is halved, rounded up.",
        	 prob: [3, 5, 7, 10, 14, 18, 23, 28, 33, 40],
        	 dmg: [2, 3, 3, 4, 5, 6, 6, 7, 8, 9],
        	 reduced: [1, 2, 2, 2, 3, 3, 3, 4, 4, 5]
        	},
        	s1002: {
        	 type: "fire",
        	 name: "Firepower",
        	 desc: "Increases fire damage for Fire Weapons and Fire Ball by a percentage. This skill has no effect on Wall of Flame.",
        	 dmg: [4, 8, 12, 16, 20, 24, 28, 32, 36, 40]
        	},
        	s1003: {
        	 type: "fire",
        	 name: "Fire Ball",
        	 desc: "Cast to inflict fire damage.",
        	 dmg: [4, 6, 8, 10, 13, 16, 20, 24, 28, 32],
        	 cd: [8, 7, 6, 6, 5, 5, 4, 4, 4, 4]
        	},
        	s1004: {
        	 type: "fire",
        	 name: "Wall of Flame",
        	 desc: "Enemies take fire damage when attacking.",
        	 dmg: [1, 2, 3, 4, 6, 8, 11, 14, 18, 22]
        	},
        	s2001: {
        	 type: "ice",
        	 name: "Ice Weapons",
        	 desc: "May add ice damage to your normal attack. When not using an ice-type weapon, this damage is halved, rounded up.",
        	 prob: [3, 5, 7, 10, 14, 18, 23, 28, 33, 40],
        	 dmg: [4, 5, 6, 7, 8, 9, 11, 12, 13, 14],
        	 reduced: [2, 3, 3, 4, 4, 5, 6, 6, 7, 7]
        	},
        	s2002: {
        	 type: "ice",
        	 name: "Heart of Ice",
        	 desc: "Your normal attack may freeze an enemy for 1 turn. The enemy cannot use its normal attack, but may use its abilities. When not using an ice-type weapon, this skill does nothing!",
        	 prob: [7, 10, 13, 15, 18, 20, 23, 25, 28, 30]
        	},
        	s2003: {
        	 type: "ice",
        	 name: "Snowball",
        	 desc: "Cast to inflict ice damage.",
        	 dmg: [5, 7, 10, 12, 16, 20, 26, 32, 38, 45],
        	 cd: [9, 8, 7, 6, 5, 5, 4, 4, 3, 3]
        	},
        	s2004: {
        	 type: "ice",
        	 name: "Glacier Strike",
        	 desc: "Cast to begin charging an ice attack for 3 turns. On the 3rd turn, you have a 60% at every level to inflict 3.7x your normal damage. If the attack fails, you do no damage. This skill is cancelled if you use your normal attack or become stunned. You may still use other skills or potions. A higher level in this skill does nothing!"
        	},
        	s3001: {
        	 type: "shock",
        	 name: "Shock Weapons",
        	 desc: "Your regular attack has a chance to stun an enemy for 1 turn. The enemy may resist. If not using a shock-type weapon, this probability is halved, rounded up.",
        	 prob: [5, 8, 9, 11, 12, 13, 14, 16, 17, 19],
        	 reduced: [3, 4, 5, 6, 6, 7, 7, 8, 9, 10]
        	},
        	s3002: {
        	 type: "shock",
        	 name: "Disable",
        	 desc: "Your regular attack has a chance lower an enemy's defence by a set amount. The defence reduction lasts for your next two attacks. The monster's defence cannot be lower than 0.",
        	 prob: [8, 15, 16, 18, 19, 21, 22, 24, 25, 27],
        	 def: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
        	 rounds: [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
        	},
        	s3003: {
        	 type: "shock",
        	 name: "Fortitude",
        	 desc: "Increases resistance to enemy special attacks (fire, ice, poison, stun, lifetap).",
        	 resist: [2, 3, 4, 6, 7, 9, 10, 12, 13, 15]
        	},
        	s3004: {
        	 type: "shock",
        	 name: "Shockwave",
        	 desc: "Your regular attack has a chance to add extra damage or may stun for 1 turn or both.",
        	 prob: [7, 12, 15, 17, 20, 22, 25, 27, 28, 30],
        	 dmg: [6, 9, 11, 13, 14, 16, 18, 19, 20, 22],
        	 rounds: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
        	},
        	s4001: {
        	 type: "spectral",
        	 name: "Spectral Weapons",
        	 desc: "Increases your defence, which decreases an enemy's chance to hit you with its normal attack. When not using a Spectral weapon, this defence bonus is reduced to 40% of its normal value, rounded up.",
        	 def: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        	 reduced: [1, 1, 2, 2, 2, 3, 3, 4, 4, 4]
        	},
        	s4002: {
        	 type: "spectral",
        	 name: "Evasion",
        	 desc: "May evade an enemy's normal attack.",
        	 prob: [8, 10, 13, 15, 18, 20, 23, 25, 28, 30]
        	},
        	s4003: {
        	 type: "spectral",
        	 name: "Absorption",
        	 desc: "Cast to attempt to absorb an enemy's next normal attack, restoring some health.",
        	 absorb: [20, 23, 26, 29, 31, 33, 35, 37, 39, 40],
        	 cd: [8, 8, 8, 8, 7, 7, 7, 7, 6, 6]
        	},
        	s4004: {
        	 type: "spectral",
        	 name: "Reflex",
        	 desc: "May cause an enemy's normal attack to damage itself for the amount you would have been damaged for. You will take no damage from its normal attack when this activates.",
        	 prob: [3, 5, 6, 8, 9, 10, 11, 12, 13, 14]
        	},
        	s5001: {
        	 type: "life",
        	 name: "Life Weapons",
        	 desc: "Potions restore 1 more health for each point in this skill. If you do not level up this skill at all, there is a 5% chance to restore 1 HP each time you move 1 tile. Higher levels in this skill increase these values. If you level up this skill, when not using a life-type weapon, this probability is reduced to 2/3 its normal value, rounded up, and the healing value is halved, rounded up.",
        	 prob: [8, 10, 13, 15, 18, 20, 22, 23, 24, 25],
        	 heal: [1, 1, 1, 1, 2, 2, 2, 2, 3, 3],
        	 potionBonus: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        	 reducedProb: [6, 7, 9, 10, 12, 14, 15, 16, 16, 17],
        	 reducedHeal: [1, 1, 1, 1, 1, 1, 1, 1, 2, 2]
        	},
        	s5002: {
        	 type: "life",
        	 name: "Field Medic",
        	 desc: "May restore health in battle (\"You regenerate ... health.\")",
        	 heal: [1, 1, 2, 2, 2, 3, 3, 4, 4, 4],
        	 prob: [10, 13, 15, 18, 20, 23, 25, 28, 30, 33]
        	},
        	s5003: {
        	 type: "life",
        	 name: "Lifesteal",
        	 desc: "65% chance at every level to restore between 1 and [the skill's level] HP when you damage an enemy with a normal attack (\"You lifesteal ... health.\") Lifesteal does not inflict additional damage.",
        	 maxHeal: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        	 prob: [65, 65, 65, 65, 65, 65, 65, 65, 65, 65]
        	},
        	s5004: {
        	 type: "life",
        	 name: "Resurrection",
        	 desc: "If you are defeated, you may be restored to life to 50% health.",
        	 prob: [20, 40, 45, 50, 55, 60, 65, 70, 75, 80]
        	}
         }
        };
        
        Object.entries(gameObjects.items).filter(item => item[1].hasOwnProperty("maxqty")).forEach(item=>{
         gameObjects.items[item[0]].droppedby = [];
         const whoDropsThisItem = Object.entries(gameObjects.monsters).filter(monster => monster[1].drops.map(drop=>drop[0]).includes(item[0]));
         whoDropsThisItem.forEach(monster => {
        	const dropChance = monster[1].drops.find(drop=>drop[0] === item[0])[1];
        	gameObjects.items[item[0]].droppedby.push([monster[0], dropChance]);
         });
        });
        
        const random = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
        const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
        const mainGameWnd = document.querySelector("div.contentModule.phpGamesPortalView") ?? document.querySelector("div.contentModule.phpGamesNonPortalView");
        
        const gameFunc = {
         mobHp: function(oMob) {
        	let mobHp = {
        	 normal: {},
        	 evil: {}
        	};
        	if(!oMob.hp) { //if not a boss
        	 const monsterHPBase = oMob.level * 5 + 5;
        	 mobHp.normal.min = monsterHPBase + Math.round(Math.ceil(monsterHPBase / 10) * -1);
        	 mobHp.normal.max = monsterHPBase + Math.round(Math.ceil(monsterHPBase / 10) * 1);
        	 mobHp.evil.min = Math.floor(mobHp.normal.min * 1.5);
        	 mobHp.evil.max = Math.floor(mobHp.normal.max * 1.5);
        	}
        	else {
        	 mobHp.normal.min = oMob.hp;
        	 mobHp.normal.max = oMob.hp;
        	 mobHp.evil.min = Math.floor(oMob.hp * 1.2);
        	 mobHp.evil.max = Math.floor(oMob.hp * 1.2);
        	}
        	return mobHp;
         },
         mobMeleeDmg: function(oMob) {
        	let dmg = {
        	 normal: {},
        	 evil: {}
        	};
        	dmg.normal.min = clamp(Math.ceil(0.2 * oMob.damage), 0, 100000);
        	dmg.normal.max = clamp(Math.ceil(1.2 * oMob.damage), 0, 100000);
        	dmg.evil.min = clamp(Math.ceil(0.3 * oMob.damage), 0, 100000);
        	dmg.evil.max = clamp(Math.ceil(1.3 * oMob.damage), 0, 100000);
        	if(oMob.fixeddamage){
        	 dmg.normal.min = oMob.fixeddamage;
        	 dmg.normal.max = oMob.fixeddamage;
        	 dmg.evil.min = oMob.fixeddamage;
        	 dmg.evil.max = oMob.fixeddamage;
        	}
        	return dmg;
         },
         mobMeleeAcc: function(oMob, oPet) {
        	const baseHitChance = 45;
        	let modHitChance = 0;
        	const levelDiff = oPet.level - oMob.level;
        	if(levelDiff > 0) {
        	 modHitChance -= Math.abs(levelDiff * 2);
        	}
        	else if(levelDiff < 0) {
        	 modHitChance += Math.abs(levelDiff * 5) + 5;
        	}
        	const strikeDiff = oMob.attack - oPet.defence;
        	if(strikeDiff > 0) {
        	 modHitChance += Math.abs(strikeDiff * 5);
        	}
        	else if(strikeDiff < 0) {
        	 modHitChance -= Math.abs(strikeDiff * 1);
        	}
        	const finalAccuracy = clamp(baseHitChance + modHitChance, 15, 95);
        	return finalAccuracy;
         },
         petMeleeAcc: function(oPet, oMob) {
        	const baseHitChance = 60;
        	let modHitChance = 0;
        	const levelDiff = oPet.level - oMob.level;
        	if(levelDiff > 0) {
        	 modHitChance += Math.abs(levelDiff * 2);
        	}
        	else if(levelDiff < 0) {
        	 modHitChance -= Math.abs(levelDiff * 10) + 5;
        	}
        	const strikeDiff = oPet.attack - oMob.defence;
        	if(strikeDiff > 0) {
        	 modHitChance += Math.abs(strikeDiff * 1);
        	}
        	else if(strikeDiff < 0) {
        	 modHitChance -= Math.abs(strikeDiff * 5);
        	}
        	// NCC AAA v0.1.63: debug log removed from production build.
        	const finalAccuracy = clamp(baseHitChance + modHitChance, 5, 95);
        	return finalAccuracy;
         },
         petMeleeDmg: function(oPet) {
        	let dmg = {};
        	const damageBase = oPet.damage + Math.round((oPet.level - 1) / 3);
        	dmg.min = clamp(Math.ceil(0.6 * damageBase), 0, 100000);
        	dmg.max = clamp(Math.ceil(1.6 * damageBase), 0, 100000);
        	return dmg;
         },
         potionDropChance: function(monsterLevel, potionMinLevel, potionMaxLevel, potionName, baseChance, hasItemQty) {
        	let dropChance = {
        	 base: 0,
        	 reduced: 0
        	};
        	if(monsterLevel >= potionMinLevel && monsterLevel <= potionMaxLevel) {
        	 dropChance.base = clamp((((monsterLevel - (potionMinLevel - 1)) * 80) + baseChance), 0, 700);
        	 const maxItemQty = gameObjects.items[potionName.toLowerCase() + " healing potion"].maxqty;
        	 if(hasItemQty / maxItemQty === 1) {
        		dropChance.reduced = 0;
        		dropChance.useReduced = true;
        	 }
        	 else if(hasItemQty / maxItemQty > 0.5) {
        		dropChance.reduced = Math.max(dropChance.base - (Math.ceil(((hasItemQty / maxItemQty) - 0.5) * 100) * 12), 0);
        		dropChance.useReduced = true;
        	 }
        	}
        	return dropChance;
         },
         firepowerMod: function(){
        	return 1 + (gameSkills.fire[1][oPetSkills.fire[1]]/100);
         }
        };
        
        function addBlurb(textContent, color){
         if(textContent !== ""){
        	const txt = document.createElement("span");
        	const txt1 = document.createElement("span");
        	const txt2 = document.createElement("span");
        	txt1.textContent = textContent.split(":: ")[0]+(textContent.split(":: ").length > 1 ? ": " : "");
        	txt1.style.color = color ?? "";
        	txt2.textContent = textContent.split(":: ")[1];
        	txt.append(txt1);
        	txt.append(txt2);
        	nqInfo.append(txt);
         }
         nqInfo.append(document.createElement("br"));
        }
        
        let oPetSkills = {};
        if(document.URL.includes("action=skill")){
         const levelUpSkills = [
        	5001,5002,5003,3001,3002,3003,3004,4001,4002,4003,
        	4004,3001,3002,3003,3004,4001,4002,4003,4004,3001,
        	3002,3003,3004,4001,4002,4003,4004,3001,3002,3003,
        	3004,4001,4002,4003,4004,3001,3002,3003,3004,3001,
        	3002,3003,3004,3001,3002,3003,3004,3001,3002,3003,
        	3004,3001,3002,3003,3004,3001,
         ];
         const currentLevel = parseInt(mainGameWnd.querySelectorAll("b")[2].textContent);
         const levelUpSelection = mainGameWnd.querySelector("a[href*='neoquest.phtml?skill_choice="+levelUpSkills[currentLevel+5]+"&action=skill']");
         const levelUpConfirm = mainGameWnd.querySelector("a[href*='neoquest.phtml?action=skill&skill_choice="+levelUpSkills[currentLevel+5]+"&confirm=1']");
         if(levelUpSelection) levelUpSelection.style.backgroundColor = "yellow";
         
         document.querySelectorAll("a[onclick*='nq_skills.phtml#']").forEach(el => {
        	const skillId = parseInt(el.onclick.toString().split("nq_skills.phtml#")[1].split("'")[0]);
        	if(skillId > 5){
        	 const skillLevel = parseInt(el.parentElement.nextElementSibling.textContent);
        	 oPetSkills["s"+skillId] = (isNaN(skillLevel) ? 0 : skillLevel);
        	}
         });
         // NCC AAA v0.1.63: debug log removed from production build.
         GM_setValue("oPetSkills", JSON.stringify(oPetSkills));
        }
        oPetSkills = JSON.parse(GM_getValue("oPetSkills", "{}"));
        function getPetSkillAttr(skillId, strAttr){
         const skillLevel = oPetSkills["s"+skillId];
         if(!strAttr){
        	return skillLevel;
         } else {
        	return gameObjects.skills["s"+skillId][strAttr][skillLevel-1];
         }
        }
        function getMonsterLoot(name){
         gameObjects.monsters[name].drops.forEach(drop=>{
        	addBlurb(drop[0] + ":: "+drop[1]/10 +"%", "darkgoldenrod");
         });
        }
        function objectifyMobPowers(aMobPowerStr){
         let aMobPowers = [];
         aMobPowerStr.forEach(powerStr => {
        	const powerParams = powerStr.split("|");
        	const oPower = {};
        	oPower.id = powerParams[0]; //the id of the power in the code
        	oPower.type = powerParams[1]; //P = useable power
        	oPower.prob = parseInt(powerParams[2]); //the probability the mob uses the power
        	//if the id is lifetap or heal, the prob is 0 if the mob is higher than 80% hp and doubled if lower than 20% hp
        	oPower.attr = powerParams[3].split(","); //attributes of the power
        	oPower.resist = parseInt(powerParams[4]); //a lower number means the skill is less likely to be resisted
        	//resist is added to the PET's resistance. usually it's 0 or negative (to lower the pet's resistance)
        	oPower.cooldown = parseInt(powerParams[5]); //how many turns the mob has to wait to use the power again
        	oPower.attr = oPower.attr.map(s=>isNaN(parseInt(s)) ? s : parseInt(s));
        	oPower.txt = "("+oPower.prob+"%) ";
        	if(oPower.id === "$MSPW_POISON"){
        	 oPower.name = "Poison";
        	 oPower.poisonDamage = oPower.attr[0];
        	 oPower.poisonDuration = oPower.attr[1];
        	 oPower.txt += oPower.name+": inflicts " + oPower.poisonDamage + " damage for " + oPower.poisonDuration + (oPower.poisonDuration > 1 ? " turns" : " turn");
        	}
        	else if(oPower.id === "$MSPW_STUN"){
        	 oPower.name = "Stun";
        	 oPower.stunDuration = oPower.attr[0];
        	 oPower.txt += oPower.name+": stuns for " + oPower.stunDuration + (oPower.stunDuration > 1 ? " turns" : " turn");
        	}
        	else if(oPower.id === "$MSPW_STUNBLAST"){
        	 oPower.name = "Stunblast";
        	 oPower.damage = oPower.attr[0];
        	 oPower.stunDuration = oPower.attr[1];
        	 oPower.txt += oPower.name+": stuns for " + oPower.stunDuration + (oPower.stunDuration > 1 ? " turns" : " turn") + " and inflicts "+oPower.damage+" damage";
        	}
        	else if(oPower.id === "$MSPW_FIREBLAST"){
        	 oPower.name = "Fireblast";
        	 oPower.damage = oPower.attr[0];
        	 oPower.txt += oPower.name+": inflicts " +oPower.damage+" fire damage";
        	}
        	else if(oPower.id === "$MSPW_ICEBLAST"){
        	 oPower.name = "Iceblast";
        	 oPower.damage = oPower.attr[0];
        	 oPower.txt += oPower.name+": inflicts " +oPower.damage+" ice damage";
        	}
        	else if(oPower.id === "$MSPW_HEAL"){
        	 oPower.name = "Heal";
        	 oPower.healtype = oPower.attr[0]; //P for %, A for absolute, however, mobs only use P
        	 oPower.heal = oPower.attr[1];
        	 oPower.txt += oPower.name+": heals for "+ oPower.heal +(oPower.healtype === "P" ? "% of max health" : "");
        	}
        	else if(oPower.id === "$MSPW_FIRESTUN"){
        	 oPower.name = "Firestun";
        	 oPower.damage = oPower.attr[0];
        	 oPower.stunDuration = oPower.attr[1];
        	 oPower.txt += oPower.name+": stuns for " + oPower.stunDuration + (oPower.stunDuration > 1 ? " turns" : " turn") + " and inflicts "+oPower.damage+" fire damage";
        	}
        	else if(oPower.id === "$MSPW_ICESTUN"){
        	 oPower.name = "Icestun";
        	 oPower.damage = oPower.attr[0];
        	 oPower.stunDuration = oPower.attr[1];
        	 oPower.txt += oPower.name+": stuns for " + oPower.stunDuration + (oPower.stunDuration > 1 ? " turns" : " turn") + " and inflicts "+oPower.damage+" ice damage";
        	}
        	else if(oPower.id === "$MSPW_LIFETAP"){
        	 //lifetap damage is resisted with pet's stun stat
        	 oPower.name = "Lifetap";
        	 oPower.damage = oPower.attr[0];
        	 oPower.txt += oPower.name+": inflicts " +oPower.damage + " damage and heals the user for the same amount";
        	}
        	else if(oPower.id === "$MSPW_LIFEDRAIN"){
        	 //like lifetap but no resist and repeats over x turns
        	 //only mastermind uses this
        	 oPower.name = "Lifedrain";
        	 oPower.damage = oPower.attr[0];
        	 oPower.drainDuration = oPower.attr[1];
        	 oPower.txt += oPower.name+": for "+oPower.duration + (oPower.duration > 1 ? " turns" : " turn") +", inflicts " +oPower.damage + " damage and heals the user for the same amount";
        	}
        	else if(oPower.id === "$MSPW_DISABLE"){
        	 oPower.name = "Disable";
        	 oPower.reduction = oPower.attr[0];
        	 oPower.disableDuration = oPower.attr[1];
        	 oPower.txt += oPower.name+": reduces defence by for " + oPower.reduction +" for "+ oPower.duration + (oPower.duration > 1 ? " turns" : " turn")
        	}
        	oPower.txt += " (" + oPower.cooldown + " turn cooldown)" + "\n";
        	aMobPowers.push(oPower);
         });
         return aMobPowers;
        }
        function textualizeMobPowers(aMobPowers){
         // NCC AAA v0.1.63: debug log removed from production build.
         let mobPowersToText = "\n";
         aMobPowers.forEach(power => {
        	mobPowersToText += power.txt;
         });
         return (mobPowersToText === "\n" ? "none" : mobPowersToText);
        }
        
        function calcMaxPotentialDmg(maxHitPotentialStr, maxMeleeHit){
         let arr = maxHitPotentialStr.split(",");
         let total = 0;
         arr.forEach(str=>{
        	if(str.includes("m|")){total += Math.max(parseInt(str.split("|")[1]), maxMeleeHit);}
        	else if(str.includes("m+")){total += maxMeleeHit + parseInt(str.split("+")[1]);}
        	else if(str === "m"){total += maxMeleeHit;}
        	else if(!str.includes("m")){total += parseInt(str);}
         });
         return total;
         // aMobPowers, maxMeleeHit
         // aMobPowers = aMobPowers.filter(oPower => oPower.hasOwnProperty("damage") || oPower.hasOwnProperty("stunDuration"));
         // let totalDamagePotential = maxMeleeHit;
         // let maxPowerDamage = 0;
         // maxPowerDamage = aMobPowers.reduce((a,b)=>Math.max(a.damage, b.damage));
         // let maxSingleTurnDmg = Math.max(maxPowerDamage, maxMeleeHit);
         //   aMobPowers.map(power => {
         // 	if(power.stunDuration && !power.damage){atkStr = maxSingleTurnDmg+","+maxSingleTurnDmg;}
         // 	else if(power.stunDuration && power.stunDuration===1 && power.damage){atkStr}
         //   });
         // NCC AAA v0.1.63: debug log removed from production build.
         //   aMobPowers.forEach(power => {
         // if(power.stunDuration) totalDamagePotential += power.damage;
         // aMobPowers.filter(oPower => oPower.hasOwnProperty("poisonDamage")).forEach(oPower=>totalDamagePotential+=oPower.poisonDamage);
         //   return totalDamagePotential;
        }
        
        function makeColoredChildren(parent, color){
         for (const child of parent.children){
        	child.style.backgroundColor = color;
         }
        }
        
        let defaultActionKeysStr = " (Press ";
        defaultActionKeys.forEach(str => defaultActionKeysStr += str.replace("Key", "") + " | ");
        defaultActionKeysStr = (defaultActionKeysStr + ")").replace(" | )", ")");
        let defaultActionElement;
        if(!mainGameWnd && !document.querySelector("p.loader__items-paragraph")) location.href = "https://www.neopets.com/games/neoquest/neoquest.phtml";
        const nqInfo = document.createElement("td");
        nqInfo.style.whiteSpace = "pre-wrap";
        nqInfo.style.textAlign = "left";
        nqInfo.setAttribute("colspan", 2);
        let gameMode = mainGameWnd.querySelectorAll("b")[5].textContent.toLowerCase();
        if(gameMode === "evil!") gameMode = "evil";
        let isABossFight = false;
        
        //'restart music' button was added sometime in 2025 and broke it a bit
        //this 'quick fix' seems to be enough
        let minAElementsLength = 0;
        if(mainGameWnd.querySelectorAll("a").length > 0 && mainGameWnd.querySelector("a").textContent === "Restart music"){
          minAElementsLength = 1;
        }
        
        if(mainGameWnd.querySelectorAll("a").length !== minAElementsLength){
         //we are in a fight or the map or talking to an npc
         let gameLinks = mainGameWnd.querySelectorAll("table")[0].querySelectorAll("a");
         if(gameLinks.length === 0){ //talking to npc
        	const urlParams = new URLSearchParams(window.location.search);
        	if(urlParams.has('give')){
        	 const giveItemTable = mainGameWnd.querySelector("tbody");
        	 const npcId = parseInt(urlParams.get('target'));
        	 const colors = ["steelblue", "green"];
        	 let itemsToGive = {};
        	 const div = document.createElement("div");
        	 div.style.whiteSpace = "pre-wrap";
        	 mainGameWnd.querySelector("table").parentNode.insertBefore(div, mainGameWnd.querySelector("table"));
        	 
        	 if(npcId === 90010003){ //Morax Dorangis
        		itemsToGive["energy shield"] = ["glowing stone", "chunk of metal", "small yellow gem"];
        		itemsToGive["cloth robe"] = ["glowing stone", "blue thread", "plains lupe pelt"];
        	 }
        	 else if(npcId === 90010005){ //Choras Tillie
        		itemsToGive["mirrored force field"] = ["glowing stone", "tiny obsidian", "lodestone", "piece of smooth glass"];
        		itemsToGive["magic robe"] = ["glowing stone", "tiny obsidian", "cave lupe pelt", "stretch of rotted cloth"];
        	 }
        	 else if(npcId === 90030001){ //Denethrir
        		itemsToGive["nature wand"] = ["glowing stone", "jungle beast claw", "noil's tooth", "jungle pauldrons", "the Staff of Ni-tas"];
        	 }
        	 else if(npcId === 90010007){ //Leirobas
        		itemsToGive["sorcerous robe"] = ["drop of giant spider blood", "drop of desert cobra venom", "glob of dried iguana spit", "pinch of crystallized sand"];
        		itemsToGive["growing jewel"] = ["glowing stone", "tiny beryl", "emerald"];
        	 }
        	 else if(npcId === 90010006){ //Mokti
        		itemsToGive["dawnshine generator shield"] = ["giant spider leg", "dust spider pincer", "desert cobra fang", "sand iguana eye"];
        	 }
        	 else if(npcId === 90050001){ //Erick
        		itemsToGive["coruscating gem"] = ["clouded gem"];
        		itemsToGive["life staff"] = ["the Coruscating Gem", "the Growing Jewel", "carved oak staff"];
        	 }
        	 else if(npcId === 90070001){ //Mr. Irgo
        		itemsToGive["energy absorber"] = ["piece of agate", "piece of chrysolite", "piece of serpentine"];
        		itemsToGive["robe of protection"] = ["drakonid eye", "drakonid heart", "drakonid hide"];
        	 }
        	 else if(npcId === 90010008){ //Gali Yoj
        		itemsToGive["Keladrian Medallion"] = ["rusty medallion"];
        	 }
        	 
        	 Object.entries(itemsToGive).forEach((itemList, index) => {
        		// NCC AAA v0.1.63: debug log removed from production build.
        		div.textContent += itemList[0] + " items are in " + colors[index] + ": "+itemList[1].join(", ")+"\n\n";
        		giveItemTable.querySelectorAll("tr").forEach(row => {
        		 if(itemList[1].includes(row.children[0].textContent)){
        			makeColoredChildren(row, colors[index]);
        			if(Object.entries(itemsToGive).length === 1){
        			 row.children[2].querySelector("input").checked = true;
        			}
        		 }
        		});
        	 });
        	}
        	else{ //initiate give
        	 
        	}
         }
         else if(gameLinks[0].textContent === "Options"){
        	//we are in the overworld/map
        	
        	const nqNav = mainGameWnd.querySelectorAll("table")[2].querySelector("tbody");
        	const currentMapName = nqNav.querySelectorAll("td")[1].textContent.split("You are in ")[1].split(".")[0];
        	// const mapTiles = mainGameWnd.querySelector('table td').querySelectorAll('img:not([src*="brd_"])');
        	
        	const objectives = [
        	 {level: 1, next: "Train to level 2 east of city"},
        	 {level: 2, next: "Train to level 3 east of city"},
        	 {level: 3, next: "Train to level 4 east of city"},
        	 {level: 4, next: "Train to level 5 west of city"},
        	 {level: 4, next: "Collect 5 glowing stones"},
        	 {level: 5, next: "Train to level 6 hills northwest of city"},
        	 {level: 5, next: "Collect 5 glowing stones"},
        	 {level: 6, next: "Do you have 5 glowing stones?"},
        	 {level: 6, next: "Create armor @ southwest city tile"},
        	 {level: 7, next: "Enter cave northwest of city"},
        	 {level: 7, next: "Navigate to cave level 2"},
        	 {level: 7, next: "In cave level 2, train to level 8 by ramp to level 1"},
        	 {level: 8, next: "Train to level 9 at Dank Cave 2 near stairs to Dank Cave 3"},
        	 {level: 9, next: "Train to level 10 at Dank Cave 3 near the vertical hallway with the pillars"},
        	 {level: 10, next: "Train to level 11 at Dank Cave 4 near the start of the maze"},
        	 {level: 11, next: "Train to level 12 at Dank Cave 4 near Xantan"},
        	 {level: 12, next: "Train to level 13 at Dank Cave 4 near Xantan"},
        	 {level: 13, next: "Defeat Xantan the Foul"},
        	 {level: 13, next: "Create and equip the Iron Wand at Eleus Batrin (center city tile)"},
        	 {level: 13, next: "Create and equip one of the armour items at Choras Tillie (southeast city tile)"},
        	 {level: 13, next: "Go to the Jungle Ruins (south of city). You are on the base level. Take the northeastern stairs down"},
        	 {level: 13, next: "Train to level 14 at Jungle Ruins dungeon 1 near stairs"},
        	 {level: 14, next: "Train to level 15 at Jungle Ruins dungeon 1 horizontal hallway before beds"},
        	 {level: 15, next: "Train to level 16 at Jungle Ruins dungeon 1 vertical hallway near beds"},
        	 {level: 16, next: "Train to level 17 at Jungle Ruins dungeon 1 horizontal hallway after beds"},
        	 {level: 17, next: "Train to level 18 at Jungle Ruins dungeon 1 water and grass area before Kreai"},
        	 {level: 17, next: "Check to be sure you have a jungle beast claw, noil's tooth, and jungle pauldrons. If not refer to the Guide"},
        	 {level: 18, next: "Defeat Kreai, exit through the portal, continue downstairs."},
        	 {level: 18, next: "Train to level 19 at Jungle Ruins dungeon 2 hallway near top of map"},
        	 {level: 19, next: "Train to level 20 at Jungle Ruins dungeon 2 in front of portal maze"},
        	 {level: 19, next: "Go through the portal maze to reach Jungle Ruins Gors' Garden"},
        	 {level: 20, next: "Train to level 21 at Jungle Ruins Gors' Garden horizontal hallway before grassy area"},
        	 {level: 21, next: "Train to level 22 at Jungle Ruins Gors' Garden grassy area"},
        	 {level: 22, next: "Train to level 23 at Jungle Ruins Gors' Garden grassy area"},
        	 {level: 23, next: "Defeat Gors the Mighty, exit through the portal."},
        	 {level: 23, next: "Go up the stairs 2 tiles north. Go up the stairs 3 tiles west. Go up again to tower level 1."},
        	 {level: 23, next: "Create and equip the Nature wand at Denethrir in Jungle Ruins tower 1"},
        	 {level: 23, next: "Leave the Jungle ruins by returning to the base level; there is a patch of grass that acts as an exit. Go west of Neopia City. Pass through the swamp."},
        	 {level: 23, next: "Go south to the desert. Collect materials for an armour item. You need to move around the desert. Craft the armour in Swamp Edge City (southwestern area of swamp)."},
        	 {level: 23, next: "Find a carved oak staff at Southern Plains of Roo, the grasslands southeast of the desert and hills, from the various plains grarrls."},
        	 {level: 23, next: "Return to the Jungle Ruins base level, go down to dungeon 1 then 2 then 3"},
        	 {level: 23, next: "Train to level 24 in Jungle Ruins dungeon 3, NW area of map"},
        	 {level: 24, next: "You may have leveled up. Refer to Guide for previous steps."},
        	 {level: 24, next: "Train to level 25 at Jungle Ruins dungeon 3 northeastern area of map"},
        	 {level: 25, next: "Train to level 26 at Jungle Ruins dungeon 3 south of lake"},
        	 {level: 26, next: "Train to level 27 at Jungle Ruins dungeon 3 around Pomanna"},
        	 {level: 27, next: "Train to level 28 at Jungle Ruins dungeon 3 north of lake"},
        	 {level: 28, next: "Defeat Rollay Scaleback. Exit through the portal"},
        	 {level: 28, next: "Return to the Desert, enter the Temple of Roo through the cave, sneak through the trees"},
        	 {level: 28, next: "Train to level 29 at Temple of Roo western area of map after trees"},
        	 {level: 29, next: "Train to level 30 at Temple of Roo western area of map after trees"},
        	 {level: 30, next: "Take the ramp to the next level. On this level you must find the keys in order. Refer to the Guide's maps."},
        	 {level: 30, next: "Find an emerald (all monsters drop this)"},
        	 {level: 30, next: "Find the copper-plated key in Temple of Roo, room 1"},
        	 {level: 30, next: "Find the bronze-plated key in Temple of Roo, room 2"},
        	 {level: 30, next: "Find the silver-plated key in Temple of Roo, room 3"},
        	 {level: 30, next: "Train to level 31 at Temple of Roo, room 3"},
        	 {level: 31, next: "Train to level 32 at Temple of Roo, room 3"},
        	 {level: 32, next: "Find the gold-plated key in Temple of Roo, room 4"},
        	 {level: 32, next: "Train to level 33 at Temple of Roo, room 5"},
        	 {level: 33, next: "Find the platinum-plated key in Temple of Roo, room 5"},
        	 {level: 33, next: "Find the crystalline key in Temple of Roo, room 6"},
        	 {level: 33, next: "Train to level 34 at Temple of Roo, room 7"},
        	 {level: 34, next: "Defeat Archmagus of Roo. Enter the portal"},
        	 {level: 34, next: "Return to Swamp Edge City then give Leirobas the glowing stone, tiny beryl, emerald for the Growing jewel"},
        	 {level: 34, next: "Return to Temple of Roo, speak to Erick near entrance, give the clouded gem for the Coruscating gem, then create the Life staff with the coruscating gem, growing jewel, carved oak staff"},
        	 {level: 34, next: "Find armour materials and give them to Mr. Irgo, refer to the Guide"},
        	 {level: 34, next: "Train to level 35"},
        	 {level: 35, next: "Train to level 36 at cave 3 around the sand near the entrance"},
        	 {level: 36, next: "Train to level 37 at cave 3 around the sand near the entrance"},
        	 {level: 37, next: "Exit cave 3 to Sunny Town. In Sunny Town, give the rusty medallion to Gali Yoj to receive the Keladrian Medallion"},
        	 {level: 37, next: "Train to level 38 at cave 3 around the exit to Sunny Town"},
        	 {level: 38, next: "Train to level 39 at cave 5, gateway to the Mountain Fortress (the grey tiles)"},
        	 {level: 39, next: "Find and equip an Inferno Robe in the Mountain Fortress"},
        	 {level: 39, next: "Train to level 40 at Mountain Fortress in the hallway"},
        	 {level: 40, next: "Train to level 41 at Mountain Fortress in the life guardian's area"},
        	 {level: 41, next: "Defeat the Guardian of Life Magic and equip the Moonstone staff"},
        	 {level: 41, next: "Train to level 42 at Mountain Fortress in the life guardian's area"},
        	 {level: 42, next: "Defeat each guardian"},
        	 {level: 42, next: "Train to level 43 at Mountain Fortress in the life guardian's area"},
        	 {level: 43, next: "Make your way to Faleinn's room in Kal Panning"},
        	 {level: 43, next: "Train to level 44 at Kal Panning in Faleinn's room"},
        	 {level: 44, next: "Train to level 45 at Kal Panning in Faleinn's room"},
        	 {level: 45, next: "Defeat Faleinn with the \"Show the Keladrian Medallion to Faleinn\" option during the fight"},
        	 {level: 45, next: "Exit Kal Panning, go back to the cave south of the mountains, and pass through the doors and exit the cave"},
        	 {level: 45, next: "Train to level 46 in the Two Rings Hills outside the palace"},
        	 {level: 46, next: "Train to level 47 at Palace of the Two Rings, Level 1 near the entrance"},
        	 {level: 47, next: "Train to level 48 at Palace of the Two Rings, Level 1 near the entrance"},
        	 {level: 48, next: "Train to level 49 at Palace of the Two Rings, Level 1 near the stairs to level 2"},
        	 {level: 49, next: "Train to level 50 at Palace of the Two Rings, Level 2"},
        	 {level: 50, next: "Defeat the final boss(es)"},
        	];
        	const currentPlayerLevel = parseInt(mainGameWnd.querySelectorAll("b")[2].textContent);
        	const currentObjectives = objectives.filter(obj=>obj.level===currentPlayerLevel);
        	const objectiveContainer = document.createElement("td");
        	const objectiveList = document.createElement("ol");
        	objectiveList.style.paddingLeft = "10px";
        	objectiveContainer.style.textAlign = "center";
        	nqNav.append(objectiveContainer);
        	objectiveContainer.append(document.createTextNode("Objectives:"));
        	objectiveContainer.append(objectiveList);
        	currentObjectives.forEach(objective => {
        	 const e = document.createElement("li");
        	 e.textContent = objective.next;
        	 objectiveList.append(e);
        	});
        	
        	defaultActionElement = "move";
        	document.addEventListener("keydown", (event) => {
        	 switch (event.code) {
        		case "KeyF":
        		case "Space":
        		 event.preventDefault();
        		 //move without moving (you stay on the same tile) so you can hunt mobs/train
        		 location.href = "https://www.neopets.com/games/neoquest/neoquest.phtml?action=move&movedir=";
        		 break;
        		case "KeyS":
        		case "ArrowDown":
        		 event.preventDefault();
        		 document.querySelector("area[alt='South']").click();
        		 break;
        		case "KeyW":
        		case "ArrowUp":
        		 event.preventDefault();
        		 document.querySelector("area[alt='North']").click();
        		 break;
        		case "KeyA":
        		case "ArrowLeft":
        		 event.preventDefault();
        		 document.querySelector("area[alt='West']").click();
        		 break;
        		case "KeyD":
        		case "ArrowRight":
        		 event.preventDefault();
        		 document.querySelector("area[alt='East']").click();
        		 break;
        		case "KeyQ":
        		 document.querySelector("area[alt='Northwest']").click();
        		 break;
        		case "KeyE":
        		 document.querySelector("area[alt='Northeast']").click();
        		 break;
        		case "KeyZ":
        		 document.querySelector("area[alt='Southwest']").click();
        		 break;
        		case "KeyC":
        		 document.querySelector("area[alt='Southeast']").click();
        		 break;
        	 }
        	});
         }
         else if(gameLinks[0].textContent === "Attack" || gameLinks[0].textContent === "Click here to see what you found!" || gameLinks[0].textContent === "Click here to see what happened..."){
        	
        	// const mobImage = mainGameWnd.querySelector("img[src^='//images.neopets.com/nq/m/']");
        	// if(mobImage.height > 120) mobImage.height = 120;
        	
        	//we are in a fight
        	mainGameWnd.querySelector(".frame table tbody").append(nqInfo);
        	//on insane, queryselectorall(b) past 5 needs to be 2 higher because insane text has 2 letters in bold
        	let bSelMod = 0;
        	if(gameMode !== "normal" && gameMode !== "evil"){
        	 bSelMod = 2;
        	}
        	
        	const oPetInfo = {
        	 hpNow: parseInt(mainGameWnd.querySelectorAll("b")[3].textContent),
        	 hpMax: parseInt(mainGameWnd.querySelectorAll("b")[3].nextSibling.textContent.split("/")[1]),
        	 level: parseInt(mainGameWnd.querySelectorAll("b")[2].textContent),
        	 weapon: mainGameWnd.querySelectorAll("b")[9+bSelMod].textContent.toLowerCase(),
        	 armour: mainGameWnd.querySelectorAll("b")[10+bSelMod].textContent.toLowerCase()
        	};
        	oPetInfo.attack = gameObjects.items[oPetInfo.weapon].points + oPetInfo.level; //NOT SURE IF LEVEL IS ADDED but it is for mobs
        	oPetInfo.damage = gameObjects.items[oPetInfo.weapon].points; //this is correct
        	let defBonus = getPetSkillAttr(4001,"def");
        	if(gameObjects.items[oPetInfo.weapon].type !== "spectral"){defBonus = Math.ceil(defBonus*0.4);}
        	oPetInfo.defence = (oPetInfo.armour === "none" ? 0 : gameObjects.items[oPetInfo.armour].points) + oPetInfo.level + defBonus; //NOT SURE IF LEVEL IS ADDED but it is for mobs
        	oPetInfo.attack = clamp(oPetInfo.attack, 0, oPetInfo.level*2);
        	oPetInfo.defence = clamp(oPetInfo.defence, 0, oPetInfo.level*2);
        	const oMobInfo = {
        	 name:  mainGameWnd.querySelectorAll("b")[7+bSelMod].textContent.toLowerCase().replace(/^an? /,""),
        	 hpNow: parseInt(mainGameWnd.querySelectorAll("b")[11+bSelMod].textContent),
        	 hpMax: parseInt(mainGameWnd.querySelectorAll("b")[11+bSelMod].nextSibling.textContent.split("/")[1]),
        	 level: parseInt(mainGameWnd.querySelectorAll("b")[12+bSelMod].textContent)
        	};
        	if (oMobInfo.name == "jahbal") {
        	 oMobInfo.attack = oPetInfo.defence;
        	 oMobInfo.defence = oPetInfo.attack;
        	} else if (oMobInfo.name == "mastermind") {
        	 oMobInfo.attack = oPetInfo.defence + 2;
        	 oMobInfo.defence = oPetInfo.attack + 2;
        	} else {
        	 oMobInfo.attack = oMobInfo.level + (gameObjects.monsters[oMobInfo.name].attack ?? 0);
        	 oMobInfo.defence = oMobInfo.level + (gameObjects.monsters[oMobInfo.name].defence ?? 0);
        	}
        	oMobInfo.damage = oMobInfo.level + (gameObjects.monsters[oMobInfo.name].damage ?? 0);
        	isABossFight = gameObjects.monsters[oMobInfo.name].xp ? true : false;
        	// NCC AAA v0.1.63: debug log removed from production build.
        	
        	const petMeleeDmg = gameFunc.petMeleeDmg(oPetInfo);
        	addBlurb("Pet Melee Damage:: " + petMeleeDmg.min + " - " + petMeleeDmg.max, "steelblue");
        	
        	const petMeleeAcc = gameFunc.petMeleeAcc(oPetInfo, oMobInfo);
        	addBlurb("Pet Melee Accuracy:: " + petMeleeAcc +"%", "steelblue");
        	
        	// addBlurb("Expected Pet Damage Per Turn:: " + (((petMeleeDmg.min+petMeleeDmg.max)/2) * petMeleeAcc/100), "steelblue");
        	
        	const passiveBattleEffectSkillIds = [1001,2001,2002,3001,3002,3004,4002,4004,5002,5003];
        	passiveBattleEffectSkillIds.forEach(skillId => {
        	 const skillLvl = oPetSkills["s"+skillId];
        	 if(skillLvl > 0){
        		let skillProb = getPetSkillAttr(skillId,"prob");
        		if(skillId === 3001 && gameObjects.items[oPetInfo.weapon].type !== "shock"){
        		 skillProb = Math.ceil(skillProb/2);
        		}
        		addBlurb(gameObjects.skills["s"+skillId].name + " chance:: " + skillProb + "%");
        	 }
        	});
        	
        	addBlurb("");
        	// addBlurb(oMobInfo.name);
        	
        	if(gameMode !== "evil" && gameMode !== "normal") gameMode = "evil"; //insane has same mods as evil
        	const mobMeleeDmg = gameFunc.mobMeleeDmg(oMobInfo);
        	addBlurb("Monster Melee Damage:: " + mobMeleeDmg[gameMode].min + " - " + mobMeleeDmg[gameMode].max, "#DA0D0D");
        	
        	const mobMeleeAcc = gameFunc.mobMeleeAcc(oMobInfo, {level: oPetInfo.level, defence: oPetInfo.defence});
        	addBlurb("Monster Melee Accuracy:: " + mobMeleeAcc +"%", "#DA0D0D");
        	
        	const mobPowers = objectifyMobPowers(gameObjects.monsters[oMobInfo.name].powers);
        	addBlurb("Monster Powers:: " + textualizeMobPowers(mobPowers), "#DA0D0D");
        	
        	let mobMaxPotentialDmg = mobMeleeDmg[gameMode].max;
        	if(gameObjects.monsters[oMobInfo.name].maxHitPotential){
        	 mobMaxPotentialDmg = calcMaxPotentialDmg(gameObjects.monsters[oMobInfo.name].maxHitPotential, mobMeleeDmg[gameMode].max)
        	}
        	// note: no mob in the game has a stun power with a cooldown less than it can stun for, but a mob may have more than one stun power
        	addBlurb("The number below is the absolute max damage that could occur before you are able to heal when accounting for stuns, poison, and damaging attacks. However, for some monsters, this could be extremely rare: a monster would have to use all its stuns after one another and roll max damage on every attack. In some cases, e.g., Gors, you cannot be 100% safe.");
        	
        	addBlurb("Monster Max Potential Damage:: " + mobMaxPotentialDmg, "#DA0D0D");
        	
        	addBlurb("");
        	addBlurb("Drops");
        	getMonsterLoot(oMobInfo.name);
        	
        	const potionLinks = mainGameWnd.querySelectorAll("table")[0].querySelectorAll("a[onclick*='item']");
        	const potionNames = ["weak", "standard", "strong", "greater", "superior", "spirit"];
        	const potionIds = [220000, 220001, 220002, 220003, 220004, 220005];
        	let potionDropChances = [];
        	let highestPotionDropped;
        	let hasPotionQty = {};
        	potionLinks.forEach(potion => {
        	 const potionName = potion.textContent.split("Use a ")[1].split(" Healing")[0].toLowerCase();
        	 hasPotionQty[potionName] = parseInt(potion.textContent.split(") (")[1]);
        	});
        	//this is actually calculated when the battle begins (so using potions in battle doesnt actually increase drop chance) but its not particularly noticeable
        	potionNames.forEach(potionName => {
        	 const potionLevels = gameObjects.items[potionName + " healing potion"].dropLevelRange;
        	 const potionChance = gameFunc.potionDropChance(oMobInfo.level, potionLevels[0], potionLevels[1], potionName, (potionName === "strong" ? 20 : 0), hasPotionQty[potionName]);
        	 if(oMobInfo.level >= potionLevels[0] && oMobInfo.level <= potionLevels[1]){
        		addBlurb(potionName+" healing potion:: "+ (potionChance.useReduced ? potionChance.reduced : potionChance.base)/10 + "%", "purple");
        	 }
        	 // potionDropChances.push(potionChance.useReduced ? potionChance.reduced : potionChance.base);
        	 // if(potionChance.base > 0 && potionName !== "spirit"){highestPotionDropped = potionName}
        	});
         }
         if(gameLinks[0].textContent === "Click here to see what you found!"){
        	//we are stunned/finished and there is literally nothing else we can do
        	//these take pref over anything else...
        	defaultActionElement = gameLinks[0];
        	defaultActionElement.textContent += defaultActionKeysStr;
         }
        } else {
         //we are in the fight begin/end page
         defaultActionElement = mainGameWnd.querySelector("input[type='submit']");
         defaultActionElement.value += defaultActionKeysStr;
         const foundItems = JSON.parse(GM_getValue("foundItems", "[]"));
         const lookedForItems = [
        	"jungle beast claw",
        	"noil's tooth",
        	"jungle pauldrons",
        	"carved oak staff",
        	// "piece of living crystal", // not actually necessary
        	"emerald",
        	"copper-plated key",
        	"bronze-plated key",
        	"silver-plated key",
        	"gold-plated key",
        	"platinum-plated key",
        	"crystalline key",
        	"inferno robe",
        	"evening sun energy shield",
        	"drakonid eye",
        	"drakonid heart",
        	"drakonid hide",
        	"piece of agate",
        	"piece of chrysolite",
        	"piece of serpentine",
         ];
         const lootedItems = [...mainGameWnd.querySelectorAll("center b")].filter(e=>!e.textContent.includes("experience"));
         lootedItems.forEach(e => {
        	const itemName = e.textContent.toLowerCase().replace(/^an? /,"");
        	if(lookedForItems.includes(itemName) && !foundItems.includes(itemName)){
        	 foundItems.push(itemName);
        	 GM_setValue("foundItems", JSON.stringify(foundItems));
        	 alert("Found necessary item: " +itemName);
        	}
         });
         
        }
        
        if(defaultActionElement){
         if(defaultActionElement !== "move"){
        	document.addEventListener("keydown", (event) => {
        	 if(defaultActionKeys.includes(event.code)) {
        		event.preventDefault();
        		try{
        		 defaultActionElement.click();
        		} catch(err){
        		 alert(defaultActionElement + "\n" + err)
        		}
        	 }
        	});
         }
         
        }
    }


    function bootNeoQuestTwoGuideHelper() {
        if (!isNeoQuestTwoPage()) return;
        if (window.__NCC_AAA_NEOQUEST_TWO_HELPER_BOOTED__) {
            saveNeoQuestTwoReport("already-active", "NeoQuest II Helper já estava ativo nesta página.");
            return;
        }
        window.__NCC_AAA_NEOQUEST_TWO_HELPER_BOOTED__ = true;
        saveNeoQuestTwoReport("booting", "Inicializando NeoQuest II Helper.");
        try {
            runEmbeddedNeoQuestTwoGuideHelper();
            saveNeoQuestTwoReport("active", "NeoQuest II Helper ativo nesta página.", {
                keyboardShortcuts: true,
                actionKeys: ["KeyA", "KeyF", "Space"],
                mapKeys: ["W", "A", "S", "D", "Q", "E", "Z", "C", "R", "G", "H", "1", "2"],
                sourceHelperVersion: "20241019.3"
            });
        } catch (error) {
            saveNeoQuestTwoReport("error", errorToMessage(error), {
                error: errorToMessage(error),
                sourceHelperVersion: "20241019.3"
            });
            console.warn("[NCC AAA] Falha no NeoQuest II Helper.", error);
        }
    }

    function makeNeoQuestTwoGmStorageShim() {
        return {
            getValue(key, fallback) {
                const raw = readJson(NEOQUEST_TWO_GM_PREFIX + String(key || ""), undefined);
                return raw === undefined ? fallback : raw;
            },
            setValue(key, value) {
                writeJson(NEOQUEST_TWO_GM_PREFIX + String(key || ""), value);
                return value;
            }
        };
    }

    function saveNeoQuestTwoReport(status, message, extra) {
        lastNeoQuestTwoReport = clonePlain(Object.assign({
            schema: "ncc-aaa-neoquest-two-helper-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceHelper: "NeoQuest.Guide NeoQuest 2 Helper",
            sourceHelperVersion: "20241019.3",
            module: "neoquest-two",
            pageOnly: NEOQUEST_TWO_PATH,
            currentPage: location.pathname,
            status: status || "unknown",
            active: isNeoQuestTwoPage(),
            manualKeyboardOnly: true,
            backgroundAction: false,
            keyboardShortcuts: true,
            programmaticClicks: "user-keypress-only-on-neoquest2-map/actions",
            networkAccess: "KeyH inventory helper uses XMLHttpRequest only after user keypress; potion buttons use XHR only after user click",
            message: message || "",
            updatedAt: isoNow()
        }, extra || {}));
        writeJson(NEOQUEST_TWO_LATEST_KEY, lastNeoQuestTwoReport);
        dispatchStateEvent(window, NEOQUEST_TWO_EVENT, lastNeoQuestTwoReport);
        try {
            const publicWindow = getPublicWindow();
            if (publicWindow && publicWindow !== window) dispatchStateEvent(publicWindow, NEOQUEST_TWO_EVENT, lastNeoQuestTwoReport);
        } catch (_) {}
        return lastNeoQuestTwoReport;
    }

    function buildNeoQuestTwoReport() {
        return clonePlain(lastNeoQuestTwoReport || readJson(NEOQUEST_TWO_LATEST_KEY, null) || {
            schema: "ncc-aaa-neoquest-two-helper-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceHelper: "NeoQuest.Guide NeoQuest 2 Helper",
            sourceHelperVersion: "20241019.3",
            module: "neoquest-two",
            pageOnly: NEOQUEST_TWO_PATH,
            currentPage: location.pathname,
            status: isNeoQuestTwoPage() ? "not-mounted-yet" : "available",
            active: isNeoQuestTwoPage(),
            manualKeyboardOnly: true,
            backgroundAction: false,
            keyboardShortcuts: true,
            programmaticClicks: "user-keypress-only-on-neoquest2-map/actions",
            networkAccess: "KeyH inventory helper uses XMLHttpRequest only after user keypress; potion buttons use XHR only after user click",
            message: isNeoQuestTwoPage() ? "Aguardando montagem do NeoQuest II Helper." : "Abra NeoQuest II para o helper atuar.",
            updatedAt: null
        });
    }

    function runEmbeddedNeoQuestTwoGuideHelper() {
        /*
         * NeoQuest.Guide's NeoQuest 2 Helper 20241019.3, embedded minimally in NCC AAA.
         * Original scope: https://www.neopets.com/games/nq2/nq2.phtml*
         * Local changes: metadata removed, storage shimmed to ncc.aaa.*, null game-window guard added.
         */
        const nccNeoQuestTwoStorage = makeNeoQuestTwoGmStorageShim();
        const GM_getValue = (key, fallback) => nccNeoQuestTwoStorage.getValue(key, fallback);
        const GM_setValue = (key, value) => nccNeoQuestTwoStorage.setValue(key, value);
        //these 3 options aim to improve mobile play
        const onlyShowGame = false;
        const moveNavUnder = false;
        const navScaling = 1.5; //only applies when moveNavUnder is true

        // if(GM_getValue("onlyShowGame", "unset") === "unset") GM_setValue("onlyShowGame", onlyShowGame);
        // if(GM_getValue("moveNavUnder", "unset") === "unset") GM_setValue("moveNavUnder", moveNavUnder);
        // if(GM_getValue("navScaling", "unset") === "unset") GM_setValue("navScaling", navScaling);

        const defaultActionKeys = ["KeyA", "KeyF", "Space"]; //to disable keyboard actions, set this value to []
        // //const defaultActionKeys = [];
        const cssStyleForTarget = {
          "backgroundColor": "yellow",
          "border": "dashed 5px red",
        };
        const cssStyleForAction = {
          "backgroundColor": "yellow",
          "border": "dashed 3px red",
        };

        if(document.body.textContent.includes("https://errors.edgesuite.net/")){
          location.href = location.href;
          return;
        }

        const mobInfo = {
          "ramtor": {
        	id: 0,
        	image: "",
        	level: 0,
        	powers: [],
        	drops: [
        	  []
        	],
        	map: [""]
          },
        };
        const bossMobs = ["zombom", "ramtor", "leximp", "kolvars", "scuzzy", "siliclast", "gebarn ii", "the revenant", "coltzan's ghost", "anubits", "meuka", "spider grundo", "a fire faerie", "a dark faerie", "a water faerie", "an earth faerie", "hubrid nox", "the esophagor", "the fallen angel", "devilpuss", "the faerie thief", "a pant devil", "king terask"];
        const fleeMultiMobsWhenSolo = true;
        const rohaneHpCheck = [0.3, 0.5, 0.6];
        let actionElement;
        let actionType;
        let actionTarget;
        let party = [];
        let mobParty = [];

        const expTable = [1000,2100,3300,4600,6000,7500,9100,10800,12600,14500,16500,18600,20800,23100,25500,28000,30600,33300,36100,39000,42000,45100,48300,51600,55000,58500,62100,65800,69600,73500,77500,81600,85800,90100,94500,99000,103600,108300,113100,118000,123000,128100,133300,138600,144000,149500,155100,160800,166600,172500,178500,184600,190800,197100,203500,210000,216600,223300,230100];

        const mainGameWnd = document.querySelector("div.contentModule.phpGamesPortalView") ?? document.querySelector("div.contentModule.phpGamesNonPortalView");
        if (!mainGameWnd) {
          console.warn("[NCC AAA] NeoQuest II Helper: janela do jogo não encontrada.");
          return;
        }

        if(onlyShowGame){
          document.body.append(mainGameWnd);
          mainGameWnd.style.width = "auto";
          mainGameWnd.querySelector(".frame").style.border = "none";
          document.querySelector("#main").style.display = "none";
          document.querySelector("#superfooter").style.display = "none";
        }

        if(document.URL.includes("nq2.phtml?act=merch")){
          //hide fathers sword from selling
          document.querySelector("a[href$='mact=sell&targ_item=10010&quant=1']").remove();
        }

        if(document.querySelectorAll("map[name='navmap']").length > 0){
          if(moveNavUnder){
        	const nav = mainGameWnd.querySelector(".frame td[width='180']");
        	const newNavContainer = document.createElement("table");
        	mainGameWnd.querySelector(".frame").append(newNavContainer);
        	newNavContainer.append(nav);
        	newNavContainer.style.marginLeft = "auto";
        	newNavContainer.style.marginRight = "auto";
        	if(navScaling !== 1){
        	  nav.style.scale = navScaling;
        	  newNavContainer.style.marginTop = (120*navScaling-120) + "px";
        	}
          }
          document.addEventListener("keydown", (event) => {
        	switch (event.code) {
        	  case "Digit1":
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=travel&mode=1";
        		break;
        	  case "Digit2":
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=travel&mode=2";
        		break;
        	  case "KeyS":
        	  case "KeyX":
        	  case "ArrowDown":
        		// document.querySelector("area[alt='South']").click();
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=2";
        		break;
        	  case "KeyW":
        	  case "ArrowUp":
        		// document.querySelector("area[alt='North']").click();
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=1";
        		break;
        	  case "KeyA":
        	  case "ArrowLeft":
        		// document.querySelector("area[alt='West']").click();
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=3";
        		break;
        	  case "KeyD":
        	  case "ArrowRight":
        		// document.querySelector("area[alt='East']").click();
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=4";
        		break;
        	  case "KeyQ":
        		// document.querySelector("area[alt='Northwest']").click();
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=5";
        		break;
        	  case "KeyE":
        		// document.querySelector("area[alt='Northeast']").click();
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=7";
        		break;
        	  case "KeyZ":
        		// document.querySelector("area[alt='Southwest']").click();
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=6";
        		break;
        	  case "KeyC":
        		// document.querySelector("area[alt='Southeast']").click();
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=8";
        		break;
        	  case "KeyF":
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=4";
        		setTimeout(()=>{location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=move&dir=3";}, 200);
        		break;
        	  case "KeyR":
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml";
        		break;
        	  case "KeyG":
        		//G for Gear
        		location.href = "https://www.neopets.com/games/nq2/nq2.phtml?act=inv";
        		break;
        	  case "KeyH":
        		let xhr = new XMLHttpRequest();
        		xhr.open('POST', "https://www.neopets.com/games/nq2/nq2.phtml?act=inv", true);
        		xhr.timeout = 4000;
        		xhr.responseType = "document";
        		xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        		xhr.onreadystatechange = function() {
        		  if(xhr.readyState === 4 && xhr.status === 200) {
        			const invPage = xhr.response;
        			const potionTRElements = [...invPage.querySelectorAll(".contentModule table[width='560']")[1].querySelectorAll("tr")].filter(row => row.textContent.includes("Healing Potion"));
        			const potionListTable = document.createElement("table");

        			potionTRElements.forEach(tr => {
        			  const links = tr.querySelectorAll("a");
        			  links.forEach(a => {
        				const btn = document.createElement("button");
        				btn.textContent = a.textContent;
        				btn.addEventListener("click", ()=>{
        				  let xhr = new XMLHttpRequest();
        				  xhr.open('POST', a.href, true);
        				  xhr.timeout = 4000;
        				  // xhr.responseType = "document";
        				  xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        				  xhr.onreadystatechange = function() {
        					if(xhr.readyState === 4 && xhr.status === 200) {
        					  // NCC AAA v0.1.63: debug log removed from production build.
        					  btn.textContent += " ✅";
        					}
        					else if(xhr.readyState === 4 && xhr.status !== 200){
        					  // NCC AAA v0.1.63: debug log removed from production build.
        					  btn.textContent += " ❌";
        					}
        				  }
        				  xhr.send();
        				});
        				a.remove();
        				tr.append(btn);
        			  });
        			  potionListTable.append(tr);
        			});

        			mainGameWnd.append(potionListTable);

        		  }
        		}
        		xhr.send();
        		break;
        	}
          });
          actionType = "move";

          //% for xp bar
          [...mainGameWnd.querySelector("table").querySelectorAll("table tr:not(:first-child)")].forEach(partyMember => {
        	const currentLevel = parseInt(partyMember.children[1].textContent);
        	const currentExp = parseInt(partyMember.children[6].textContent.replace(",",""));
        	const nextLevelExp = expTable[currentLevel-1];
        	const prevLevelExp = expTable[currentLevel-2];
        	const percent = (currentExp-prevLevelExp) / (nextLevelExp-prevLevelExp) * 100;
        	// NCC AAA v0.1.63: debug log removed from production build.
        	partyMember.children[6].textContent = "(" + percent.toFixed(0) + "%) " + partyMember.children[6].textContent;
          });
        }
        else {
          //in/starting/ending a fight
          //some actions have 'noAlt' no alternative and we must do them
          if(mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_next.gif']")){
        	actionElement = mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_next.gif']");
        	actionType = "noAlt";
          }
          else if(mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_end.gif']")){
        	actionElement = mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_end.gif']");
        	actionType = "noAlt";
          }
          else if(mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_begin.gif']")){
        	actionElement = mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_begin.gif']");
        	actionType = "noAlt";
          }
          else if(mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/tomap.gif']")){
        	//
        	if(mainGameWnd.textContent.match(/You have fled from the fight!/) || (mainGameWnd.textContent.match(/You found .* gold piece/) && !mainGameWnd.textContent.match(/You gain .* neopoints/))){
        	  actionElement = mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/tomap.gif']");
        	  actionType = "noAlt";
        	}

          }
          else if(mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_atk.gif']")){

        	//get party info
        	mainGameWnd.querySelectorAll("img[src^='//images.neopets.com/nq2/c/']").forEach((img, index)=>{
        	  const partyContainer = img.parentElement.parentElement.parentElement.parentElement.parentElement;
        	  const member = {};
        	  member.position = index;
        	  member.name = partyContainer.querySelectorAll("tr")[0].querySelectorAll("td")[[0,5,10,15][index]].childNodes[0].textContent;
        	  const hpText = partyContainer.querySelectorAll("tr")[0].querySelectorAll("td")[[0,5,10,15][index]].childNodes[2].textContent;
        	  member.hpNow = parseInt(hpText.split("/")[0]);
        	  member.hpMax = parseInt(hpText.split("/")[1]);
        	  member.hpPer = member.hpNow / member.hpMax;
        	  const mods = partyContainer.children[3].querySelectorAll("td")[[0,2,4,6][index]];
        	  member.mods = {def: 0, mdef: 0, haste: 0, slow: 0, shield: 0, focused: false, mesmerized: false};
        	  [...mods.querySelectorAll("font")].forEach(modStr => {
        		const str = modStr.textContent;
        		if(str.match(/^MR /)){member.mods.mdef += parseInt(str.replace(/^MR /, ""));}
        		if(str.match(/^Def /)){member.mods.def += parseInt(str.replace(/^Def /, ""));}
        		if(str.match(/^Slowed /)){member.mods.slow += parseInt(str.replace(/^Slowed /, "").replace("%", ""));}
        		if(str.match(/^Hasted /)){member.mods.haste += parseInt(str.replace(/^Hasted /, "").replace("%", ""));}
        		if(str.match(/^Focused/)){member.mods.focused = true;}
        		if(str.match(/^Mesmerized/)){member.mods.mesmerized = true;}
        	  });
        	  member.turn = parseFloat(mods.textContent.split("Next turn: ")[1].replace(/sec.*/, ""));
        	  if(isNaN(member.turn)) member.turn = -1;
        	  party.push(member);
        	});
        	const currentMember = party.find(member => member.turn === -1);
        	// NCC AAA v0.1.63: debug log removed from production build.

        	//get mob info
        	mainGameWnd.querySelectorAll("img[src^='//images.neopets.com/nq2/m/']").forEach((img, index)=>{
        	  const mobPartyContainer = img.parentElement.parentElement.parentElement.parentElement.parentElement;
        	  const mob = {};
        	  // mob.position = index+5; //sometimes this glitches server side (e.g., mob that should be 5 is 6)
        	  if(img.parentElement.getAttribute("onclick")){
        		mob.position = parseInt(img.parentElement.getAttribute("onclick").toString().match(/(\d)/)[0]);
        	  } else {
        		mob.position = 0;
        	  }
        	  mob.img = img;
        	  mob.name = mobPartyContainer.querySelectorAll("tr")[2].querySelectorAll("td")[[0,2,4,6][index]].textContent.toLowerCase();
        	  const mobText = mobPartyContainer.querySelectorAll("tr")[4].querySelectorAll("td")[[0,5,10,15][index]].textContent.split("Next turn: ");

        	  const mods = mobPartyContainer.querySelectorAll("tr")[4].querySelectorAll("td")[[0,5,10,15][index]];
        	  mob.mods = {def: 0, mdef: 0, haste: 0, slow: 0, shield: 0, focused: false, mesmerized: false};
        	  [...mods.querySelectorAll("font")].forEach(modStr => {
        		const str = modStr.textContent;
        		if(str.match(/^MR /)){mob.mods.mdef += parseInt(str.replace(/^MR /, ""));}
        		if(str.match(/^Def /)){mob.mods.def += parseInt(str.replace(/^Def /, ""));}
        		if(str.match(/^Slowed /)){mob.mods.slow += parseInt(str.replace(/^Slowed /, "").replace("%", ""));}
        		if(str.match(/^Hasted /)){mob.mods.haste += parseInt(str.replace(/^Hasted /, "").replace("%", ""));}
        		if(str.match(/^Focused/)){mob.mods.focused = true;}
        	  });


        	  mob.hpNow = parseInt(mobText[0].split("/")[0]);
        	  mob.hpMax = parseInt(mobText[0].split("/")[1]);
        	  mob.hpPer = mob.hpNow / mob.hpMax;
        	  mob.turn = mobText[1] ? parseFloat(mobText[1].replace("sec", "")) : -1;
        	  mobParty.push(mob);
        	});

        	//determine threat
        	//priorityMobs array 0th element has more priority than 1st element, etc
        	const priorityMobs = ["the revenant", "a cursed ixi", "a hand of nox", "an eye of nox", "a nox guardian", "a heart of nox", "a corrupted fire faerie", "a bionic cybunny", "a dark overlord"];
        	mobParty = mobParty.filter(mob => mob.hpNow !== 0);
        	if(mobParty.length > 1) {
        	  //lowest hp mob
        	  mobParty.sort((a, b) => a.hpNow - b.hpNow);
        	  //priority mobs
        	  mobParty.sort((a, b) => {
        		if(priorityMobs.includes(b.name) && priorityMobs.includes(a.name) && a.name === b.name){
        		  return a.hpNow - b.hpNow;
        		}
        		else if(priorityMobs.includes(b.name) && priorityMobs.includes(a.name)){
        		  return priorityMobs.indexOf(a.name) - priorityMobs.indexOf(b.name);
        		}
        		else {
        		  return priorityMobs.includes(b.name);
        		}
        	  });
        	  //non-cb focused mobs
        	  // NCC AAA v0.1.63: debug log removed from production build.
        	  mobParty.sort((a, b) => {
        		if(a.mods.focused && b.mods.focused){
        		  return a.hpNow - b.hpNow;
        		}
        		else {
        		  if(currentMember.name === "Mipsy"){
        			return b.mods.focused;
        		  }
        		  return a.mods.focused;
        		}
        	  });
        	}
        	// NCC AAA v0.1.63: debug log removed from production build.

        	//decide action
        	if(false || (fleeMultiMobsWhenSolo && party.length === 1 && mobParty.length > 1)){
        	  actionType = "canAttack";
        	  actionElement = mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_flee.gif']").parentElement;
        	}
        	else {
        	  if(party.length === 1) {
        		if(party[0].hpPer < rohaneHpCheck[mobParty.length-1]){
        		  mainGameWnd.style.backgroundColor = "darkred";
        		  actionElement = mainGameWnd.querySelector("a[onclick*='setitem(30011)']");
        		}
        	  }
        	  if(mobParty.length > 1){
        		actionTarget = mobParty[0].position;
        		// NCC AAA v0.1.63: debug log removed from production build.
        	  }

        	  actionType = "canAttack";
        	  let priortizeHeal = false;

        	  if(currentMember.name === "Rohane"){
        		if(currentMember.hpPer < .25) priortizeHeal = true;
        		actionElement = mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_atk.gif']");
        	  }
        	  else if(currentMember.name === "Mipsy"){
        		if(currentMember.hpPer < .4) priortizeHeal = true;
        		// if(mainGameWnd.querySelector("a[onclick*='setaction(9201)']")){
        		//   actionElement = mainGameWnd.querySelector("a[onclick*='setaction(9201)']");
        		// }
        		//dd 9201
        		//group d = 9202
        		//haste 9203
        		//slow 9204

        		if((mobParty.length > 2 || mobParty.some(mob => bossMobs.includes(mob.name))) && party.some(member => member.mods.haste === 0) && mainGameWnd.querySelector("a[onclick*='setaction(9203)']")){
        		  actionElement = mainGameWnd.querySelector("a[onclick*='setaction(9203)']");
        		}
        		else if(mainGameWnd.querySelector("a[onclick*='setaction(9201)']")){
        		  actionElement = mainGameWnd.querySelector("a[onclick*='setaction(9201)']");
        		}
        	  }
        	  else if(currentMember.name === "Talinia"){
        		if(currentMember.hpPer < .33) priortizeHeal = true;
        		if(mobParty.length > 1 && mainGameWnd.querySelector("a[onclick*='setaction(9302)']")){
        		  actionElement = mainGameWnd.querySelector("a[onclick*='setaction(9302)']");
        		}
        		else {
        		  actionElement = mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_atk.gif']");
        		}
        	  }
        	  else if(currentMember.name === "Velm"){
        		// if(currentMember.hpPer < .5) priortizeHeal = true;
        		if(party.some(member => member.hpNow < member.hpMax && member.mods.mesmerized)){
        		  actionElement = mainGameWnd.querySelector("a[onclick*='setaction(9402)']");
        		}
        		if(party.some(member => member.hpPer < .7)){
        		  actionElement = mainGameWnd.querySelector("a[onclick*='setaction(9402)']");
        		}
        		else if((mobParty.length > 1 || mobParty.some(mob => bossMobs.includes(mob.name))) && party.some(member => member.mods.def === 0)){ //group protection
        		  actionElement = mainGameWnd.querySelector("a[onclick*='setaction(9403)']");
        		}
        		else if(party.some(member => member.hpPer < .85)){
        		  actionElement = mainGameWnd.querySelector("a[onclick*='setaction(9402)']");
        		}
        		else{//nothing important to do
        		  if(mobParty.length > 3 || mobParty.some(mob => bossMobs.includes(mob.name))) {
        			//do nothing to wait for heal src="//images.neopets.com/nq2/x/1s.gif"
        			actionElement = mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/1s.gif']");
        		  }
        		  else if(mainGameWnd.querySelector("a[onclick*='setaction(9405)']")){//hammer
        			actionElement = mainGameWnd.querySelector("a[onclick*='setaction(9405)']");
        		  }
        		  else{
        			actionElement = mainGameWnd.querySelector("img[src='//images.neopets.com/nq2/x/com_atk.gif']");
        		  }
        		}
        	  }
        	  if(priortizeHeal) {
        		const potions = {
        		  30013: 35,
        		  30021: 60,
        		  30022: 70,
        		  30023: 80,
        		  30031: 90,
        		  30032: 100,
        		  30033: 110,
        		  30041: 120,
        		  30042: 130,
        		  30043: 140,
        		}
        		actionElement = mainGameWnd.querySelector("a[onclick*='setitem(30042)']");
        	  }
        	}
          }
        }

        if(actionTarget !== undefined){
          const targetImg = mobParty.find(mob=>mob.position===actionTarget).img.parentElement.parentElement;
          Object.keys(cssStyleForTarget).forEach(key=>{
        	targetImg.style[key] = cssStyleForTarget[key];
          });
        }

        if(actionElement !== undefined){
          Object.keys(cssStyleForAction).forEach(key=>{
        	actionElement.style[key] = cssStyleForAction[key];
          });
          document.addEventListener("keydown", (event) => {
        	if(defaultActionKeys.includes(event.code)) {
        	  event.preventDefault();
        	  try{
        		if(actionTarget !== undefined){
        		  setTarget(actionTarget);
        		  setTimeout(()=>{
        			actionElement.click();
        		  }, 200);
        		}
        		else{
        		  actionElement.click();
        		}
        	  } catch(err){
        		alert(actionElement + "\n" + err)
        	  }
        	}
        	else if(event.code === "KeyR"){
        	  location.href = "https://www.neopets.com/games/nq2/nq2.phtml";
        	}
          });
        }

        function setTarget(i){
          const currentTarget = [...mainGameWnd.querySelectorAll("img[src='//images.neopets.com/nq2/x/ch_red.gif']")].find(e => e.style.visibility !== 'hidden');
          if(currentTarget === undefined || currentTarget.name !== "ch"+i){
        	// NCC AAA v0.1.63: debug log removed from production build.
        	//rohane 1
        	//first mob 5
        	settarget(i);
        	if(i === 1) setch(ch1);
        	else if(i === 2) setch(ch2);
        	else if(i === 3) setch(ch3);
        	else if(i === 4) setch(ch4);
        	else if(i === 5 && ch5) setch(ch5);
        	else if(i === 6 && ch6) setch(ch6);
        	else if(i === 7 && ch7) setch(ch7);
        	else if(i === 8 && ch8) setch(ch8);
        	else {
        	  //nq2 server error, missing mob or something
        	  // setch();
        	}
          }
          else {
        	// NCC AAA v0.1.63: debug log removed from production build.
          }

        }
    }

    function buildTyranuEvavuFullDeck() {
        const suits = ["hearts", "clubs", "diamonds", "spades"];
        const cards = [];
        for (let value = 2; value <= 14; value += 1) {
            suits.forEach(suit => cards.push(`${value}_${suit}`));
        }
        return cards;
    }

    const TYRANU_EVAVU_FULL_DECK = Object.freeze(buildTyranuEvavuFullDeck());
    const TYRANU_EVAVU_FULL_DECK_SET = new Set(TYRANU_EVAVU_FULL_DECK);

    function getTyranuEvavuEnabled() {
        try {
            return sessionStorage.getItem(TYRANU_EVAVU_SESSION_ENABLED_KEY) === "1";
        } catch (_) {
            return false;
        }
    }

    function setTyranuEvavuEnabled(value) {
        try {
            if (value) sessionStorage.setItem(TYRANU_EVAVU_SESSION_ENABLED_KEY, "1");
            else sessionStorage.removeItem(TYRANU_EVAVU_SESSION_ENABLED_KEY);
        } catch (_) {}
        // Legacy persistent ON state is intentionally cleared so the autoplayer never remains armed across browser sessions.
        try { localStorage.removeItem(TYRANU_EVAVU_STORAGE_ENABLED_KEY); } catch (_) {}
        saveTyranuEvavuReport(value ? "enabled-session" : "disabled-session", value ? "Tyranu Evavu ligado nesta sessão pelo botão." : "Tyranu Evavu desligado nesta sessão pelo botão.");
    }

    function tyranuEvavuDelay() {
        return Math.floor(Math.random() * 701) + 300;
    }

    function getTyranuEvavuCardList() {
        const cards = readJson(TYRANU_EVAVU_STORAGE_CARDS_KEY, []);
        if (!Array.isArray(cards)) return [];
        return cards.filter(card => TYRANU_EVAVU_FULL_DECK_SET.has(card));
    }

    function setTyranuEvavuCardList(cards) {
        writeJson(TYRANU_EVAVU_STORAGE_CARDS_KEY, Array.isArray(cards) ? cards.filter(card => TYRANU_EVAVU_FULL_DECK_SET.has(card)) : []);
    }

    function resetTyranuEvavuDeck() {
        setTyranuEvavuCardList([...TYRANU_EVAVU_FULL_DECK]);
    }

    function clearTyranuEvavuDeck() {
        try { localStorage.removeItem(TYRANU_EVAVU_STORAGE_CARDS_KEY); } catch (_) {}
    }

    function parseTyranuEvavuCard(cardName) {
        const match = String(cardName || "").match(/^(\d+)_(hearts|clubs|diamonds|spades)$/);
        if (!match) return null;
        const value = Number(match[1]);
        if (!Number.isInteger(value) || value < 2 || value > 14) return null;
        return { value, suit: match[2] };
    }

    function extractTyranuEvavuCurrentCard() {
        const cardImg = Array.from(document.images || []).find(img => {
            const src = img.getAttribute("src") || img.src || "";
            return /\/games\/cards\/[^\/?#]+\.gif/i.test(src);
        });
        if (!cardImg) return null;
        const src = cardImg.getAttribute("src") || cardImg.src || "";
        const match = src.match(/\/games\/cards\/([^\/?#]+)\.gif/i);
        return match ? match[1] : null;
    }

    function chooseTyranuEvavuAction(currentCard, remainingDeck) {
        const parsedCurrent = parseTyranuEvavuCard(currentCard);
        if (!parsedCurrent) return null;
        let higherCount = 0;
        let lowerCount = 0;
        remainingDeck.forEach(card => {
            const parsed = parseTyranuEvavuCard(card);
            if (!parsed) return;
            if (parsed.value > parsedCurrent.value) higherCount += 1;
            if (parsed.value < parsedCurrent.value) lowerCount += 1;
        });
        if (higherCount > lowerCount) return "higher";
        if (lowerCount > higherCount) return "lower";
        return Math.random() < 0.5 ? "higher" : "lower";
    }

    function findTyranuEvavuActionLink(action) {
        if (action !== "higher" && action !== "lower") return null;
        const acceptedActions = action === "higher" ? ["higher", "tyranu"] : ["lower", "evavu"];
        return Array.from(document.links || []).find(link => {
            let url;
            try { url = new URL(link.href || "", location.href); } catch (_) { return false; }
            if (url.hostname && !/^(www\.)?neopets\.com$/i.test(url.hostname)) return false;
            if (url.pathname !== TYRANU_EVAVU_PATH) return false;
            const urlAction = String(url.searchParams.get("action") || "").toLowerCase();
            return acceptedActions.includes(urlAction);
        }) || null;
    }

    function clickTyranuEvavuAction(action) {
        const link = findTyranuEvavuActionLink(action);
        if (!link) return false;
        saveTyranuEvavuReport("action-click", `Escolha automática: ${action === "higher" ? "Tyranu/maior" : "Evavu/menor"}.`, { action, href: link.href || "" });
        link.click();
        return true;
    }

    function getTyranuEvavuSubmitButtonByValue(value) {
        return Array.from(document.querySelectorAll('input[type="submit"], input:not([type])')).find(input => (input.value || "").trim() === value) || null;
    }

    function submitTyranuEvavuButton(button, actionLabel) {
        const form = button?.closest?.("form");
        if (!form) return false;
        saveTyranuEvavuReport("form-submit", `${actionLabel || "Ação"} enviado automaticamente porque o módulo está ligado.`, { action: actionLabel || "submit" });
        if (typeof form.requestSubmit === "function") form.requestSubmit(button);
        else form.submit();
        return true;
    }

    function reachedTyranuEvavuDailyLimit() {
        return document.body && document.body.innerHTML.includes("I think that means you've played enough for today.");
    }

    function injectTyranuEvavuToggleStyle() {
        if (document.getElementById("ncc-aaa-tyranu-toggle-style")) return;
        const style = document.createElement("style");
        style.id = "ncc-aaa-tyranu-toggle-style";
        style.textContent = `
            #ncc-aaa-tyranu-toggle-button {
                position: fixed;
                right: 18px;
                bottom: 90px;
                z-index: 2147483647;
                border: 2px solid #123a58;
                border-radius: 999px;
                padding: 10px 14px;
                font-family: Verdana, Arial, sans-serif;
                font-size: 12px;
                font-weight: 900;
                letter-spacing: .2px;
                cursor: pointer;
                box-shadow: 0 8px 24px rgba(0,0,0,.28);
                user-select: none;
            }
            #ncc-aaa-tyranu-toggle-button.ncc-aaa-tyranu-on {
                background: #d9ffe2;
                color: #075c1c;
                border-color: #159144;
            }
            #ncc-aaa-tyranu-toggle-button.ncc-aaa-tyranu-off {
                background: #ffe1e1;
                color: #7b1111;
                border-color: #b92929;
            }
            #ncc-aaa-tyranu-toggle-button:hover { filter: brightness(1.04); transform: translateY(-1px); }
        `;
        document.head.appendChild(style);
    }

    function updateTyranuEvavuToggleButton(button) {
        const enabled = getTyranuEvavuEnabled();
        button.classList.toggle("ncc-aaa-tyranu-on", enabled);
        button.classList.toggle("ncc-aaa-tyranu-off", !enabled);
        button.textContent = enabled ? "Tyranu: LIGADO" : "Tyranu: DESLIGADO";
        button.title = enabled
            ? "Clique para desligar o Tyranu Evavu Autoplayer do NCC AAA."
            : "Clique para ligar o Tyranu Evavu Autoplayer do NCC AAA.";
    }

    function createTyranuEvavuToggleButton() {
        injectTyranuEvavuToggleStyle();
        let button = document.getElementById("ncc-aaa-tyranu-toggle-button");
        if (!button) {
            button = document.createElement("button");
            button.id = "ncc-aaa-tyranu-toggle-button";
            button.type = "button";
            button.addEventListener("click", event => {
                event.preventDefault();
                event.stopPropagation();
                const next = !getTyranuEvavuEnabled();
                setTyranuEvavuEnabled(next);
                updateTyranuEvavuToggleButton(button);
                if (next) window.setTimeout(() => runTyranuEvavuAutoplayer("toggle-on"), tyranuEvavuDelay());
            });
            document.body.appendChild(button);
        }
        updateTyranuEvavuToggleButton(button);
        return button;
    }

    function runTyranuEvavuAutoplayer(reason) {
        if (!isTyranuEvavuPage()) return false;
        createTyranuEvavuToggleButton();
        if (!getTyranuEvavuEnabled()) {
            saveTyranuEvavuReport("standby", "Botão visível; autoplay desligado.", { reason });
            return false;
        }
        if (reachedTyranuEvavuDailyLimit()) {
            clearTyranuEvavuDeck();
            saveTyranuEvavuReport("daily-limit", "Limite diário detectado; baralho local limpo.", { reason });
            return false;
        }
        const playBtn = getTyranuEvavuSubmitButtonByValue("Play Now!");
        const replayBtn = getTyranuEvavuSubmitButtonByValue("Play Again");
        if (playBtn) {
            resetTyranuEvavuDeck();
            return submitTyranuEvavuButton(playBtn, "Play Now");
        }
        if (replayBtn) {
            resetTyranuEvavuDeck();
            return submitTyranuEvavuButton(replayBtn, "Play Again");
        }
        const currentCard = extractTyranuEvavuCurrentCard();
        if (!currentCard || !parseTyranuEvavuCard(currentCard)) {
            saveTyranuEvavuReport("waiting-card", "Carta atual ainda não detectada.", { reason });
            return false;
        }
        let deck = getTyranuEvavuCardList();
        if (!deck.length) deck = [...TYRANU_EVAVU_FULL_DECK];
        const currentIndex = deck.indexOf(currentCard);
        if (currentIndex === -1) {
            saveTyranuEvavuReport("unknown-card", "Carta atual não estava no baralho local; aguardando próxima rodada.", { currentCard, deckCount: deck.length, reason });
            return false;
        }
        deck.splice(currentIndex, 1);
        setTyranuEvavuCardList(deck);
        if (!deck.length) {
            saveTyranuEvavuReport("deck-empty", "Baralho local esgotado nesta rodada.", { currentCard, reason });
            return false;
        }
        const action = chooseTyranuEvavuAction(currentCard, deck);
        if (!action) {
            saveTyranuEvavuReport("no-action", "Não foi possível decidir Tyranu/Evavu.", { currentCard, deckCount: deck.length, reason });
            return false;
        }
        return clickTyranuEvavuAction(action);
    }

    function bootTyranuEvavuAutoplayer(reason) {
        if (!isTyranuEvavuPage()) return null;
        if (window.__NCC_AAA_TYRANU_EVAVU_BOOTED__) return buildTyranuEvavuReport("already-active");
        window.__NCC_AAA_TYRANU_EVAVU_BOOTED__ = true;
        createTyranuEvavuToggleButton();
        saveTyranuEvavuReport("boot", getTyranuEvavuEnabled() ? "Tyranu Evavu ligado nesta sessão; execução agendada." : "Tyranu Evavu desligado; botão flutuante disponível.", { reason });
        window.setTimeout(() => runTyranuEvavuAutoplayer("boot-delay"), tyranuEvavuDelay());
        return buildTyranuEvavuReport("boot");
    }

    function saveTyranuEvavuReport(status, message, extra = {}) {
        lastTyranuEvavuReport = clonePlain(Object.assign({
            schema: TYRANU_EVAVU_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "tyranu-evavu-autoplayer",
            module: "tyranu-evavu",
            pageOnly: TYRANU_EVAVU_PATH,
            href: TYRANU_EVAVU_URL,
            currentPage: location.pathname,
            status: status || "unknown",
            active: isTyranuEvavuPage(),
            enabled: getTyranuEvavuEnabled(),
            deckCount: getTyranuEvavuCardList().length,
            currentCard: isTyranuEvavuPage() ? extractTyranuEvavuCurrentCard() : null,
            defaultEnabled: false,
            requiresUserToggle: true,
            sessionOnlyToggle: true,
            automationScope: "Tyranu/Evavu page only",
            programmaticClicks: "only when floating toggle is ON in this session",
            formSubmit: "only when floating toggle is ON in this session",
            networkAccessAdded: false,
            storageKeys: {
                enabledSession: TYRANU_EVAVU_SESSION_ENABLED_KEY,
                enabledLegacyCleared: TYRANU_EVAVU_STORAGE_ENABLED_KEY,
                cards: TYRANU_EVAVU_STORAGE_CARDS_KEY,
                report: TYRANU_EVAVU_REPORT_KEY
            },
            message: message || "",
            updatedAt: isoNow()
        }, extra || {}));
        writeJson(TYRANU_EVAVU_REPORT_KEY, lastTyranuEvavuReport);
        dispatchStateEvent(window, TYRANU_EVAVU_EVENT, lastTyranuEvavuReport);
        try {
            const publicWindow = getPublicWindow();
            if (publicWindow && publicWindow !== window) dispatchStateEvent(publicWindow, TYRANU_EVAVU_EVENT, lastTyranuEvavuReport);
        } catch (_) {}
        return lastTyranuEvavuReport;
    }

    function buildTyranuEvavuReport(reason) {
        const fallback = readJson(TYRANU_EVAVU_REPORT_KEY, null);
        return clonePlain(lastTyranuEvavuReport || fallback || {
            schema: TYRANU_EVAVU_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "tyranu-evavu-autoplayer",
            module: "tyranu-evavu",
            pageOnly: TYRANU_EVAVU_PATH,
            href: TYRANU_EVAVU_URL,
            currentPage: location.pathname,
            status: isTyranuEvavuPage() ? "standby" : "available",
            active: isTyranuEvavuPage(),
            enabled: getTyranuEvavuEnabled(),
            deckCount: getTyranuEvavuCardList().length,
            currentCard: isTyranuEvavuPage() ? extractTyranuEvavuCurrentCard() : null,
            defaultEnabled: false,
            requiresUserToggle: true,
            sessionOnlyToggle: true,
            automationScope: "Tyranu/Evavu page only",
            programmaticClicks: "only when floating toggle is ON in this session",
            formSubmit: "only when floating toggle is ON in this session",
            networkAccessAdded: false,
            message: reason === "bridge" ? "Relatório lido via bridge." : "Abra Tyranu Evavu para usar o botão flutuante.",
            updatedAt: null
        });
    }


    function kissTheMortogRandomDelay(min = KISS_THE_MORTOG_ACTION_DELAY_MIN, max = KISS_THE_MORTOG_ACTION_DELAY_MAX) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function normalizeKissTheMortogTarget(value) {
        const number = Number(value) || KISS_THE_MORTOG_MAX_SAFE_SCORE;
        const allowedValues = KISS_THE_MORTOG_TARGET_OPTIONS.map(option => option.value);
        if (number > KISS_THE_MORTOG_MAX_SAFE_SCORE) return KISS_THE_MORTOG_MAX_SAFE_SCORE;
        return allowedValues.includes(number) ? number : KISS_THE_MORTOG_MAX_SAFE_SCORE;
    }

    function formatKissTheMortogNumber(value) {
        return Number(value || 0).toLocaleString("en-US");
    }

    function getKissTheMortogTarget() {
        return normalizeKissTheMortogTarget(readJson(KISS_THE_MORTOG_TARGET_KEY, KISS_THE_MORTOG_MAX_SAFE_SCORE));
    }

    function setKissTheMortogTarget(value) {
        const normalized = normalizeKissTheMortogTarget(value);
        writeJson(KISS_THE_MORTOG_TARGET_KEY, normalized);
        return normalized;
    }

    function getKissTheMortogPlaying() {
        try {
            return sessionStorage.getItem(KISS_THE_MORTOG_SESSION_PLAYING_KEY) === "1";
        } catch (_) {
            return false;
        }
    }

    function setKissTheMortogPlaying(value) {
        const next = value === true;
        try {
            if (next) sessionStorage.setItem(KISS_THE_MORTOG_SESSION_PLAYING_KEY, "1");
            else sessionStorage.removeItem(KISS_THE_MORTOG_SESSION_PLAYING_KEY);
        } catch (_) {}
        kissTheMortogAlreadyActing = false;
        if (!next && kissTheMortogTimeout) {
            window.clearTimeout(kissTheMortogTimeout);
            kissTheMortogTimeout = null;
        }
        saveKissTheMortogReport(next ? "enabled-session" : "disabled-session", next ? "Kiss The Mortog ligado nesta sessão pelo painel." : "Kiss The Mortog desligado nesta sessão pelo painel.");
        return next;
    }

    function getKissTheMortogGameRoot() {
        return document.querySelector("td.content") || document.querySelector("#content") || document.querySelector("#main") || document.body;
    }

    function getKissTheMortogCleanRootClone() {
        const rootEl = getKissTheMortogGameRoot();
        const clone = rootEl.cloneNode(true);
        const panelClone = clone.querySelector("#ncc-aaa-kiss-the-mortog-panel");
        if (panelClone) panelClone.remove();
        return clone;
    }

    function getKissTheMortogPageText() {
        const clone = getKissTheMortogCleanRootClone();
        return (clone.innerText || "").replace(/\s+/g, " ").trim();
    }

    function updateKissTheMortogStatus(text) {
        const status = document.querySelector("#ncc-aaa-kiss-the-mortog-status");
        if (status) status.textContent = text || "";
    }

    function updateKissTheMortogPanelState() {
        const button = document.querySelector("#ncc-aaa-kiss-the-mortog-toggle");
        const select = document.querySelector("#ncc-aaa-kiss-the-mortog-target");
        const playing = getKissTheMortogPlaying();
        if (button) {
            button.textContent = playing ? "Parar" : "Iniciar";
            button.classList.toggle("ncc-aaa-ktm-running", playing);
        }
        if (select) {
            select.disabled = playing;
            select.value = String(getKissTheMortogTarget());
        }
        updateKissTheMortogStatus(playing ? "Ligado. Aguardando ação..." : "Desligado.");
    }

    function getKissTheMortogMortogLinks() {
        const rootEl = getKissTheMortogGameRoot();
        return Array.from(rootEl.querySelectorAll('a[href*="process_kissthemortog.phtml"]')).filter(anchor => {
            let url;
            try { url = new URL(anchor.getAttribute("href") || anchor.href || "", location.href); } catch (_) { return false; }
            const mortogImage = anchor.querySelector('img[src*="pet_mortog.gif"]');
            return /^(www\.)?neopets\.com$/i.test(url.hostname)
                && url.pathname === KISS_THE_MORTOG_PROCESS_PATH
                && url.searchParams.get("type") === "frogprince"
                && Boolean(url.searchParams.get("num"))
                && Boolean(mortogImage);
        });
    }

    function getKissTheMortogButtonByValue(regex) {
        const rootEl = getKissTheMortogGameRoot();
        return Array.from(rootEl.querySelectorAll('input[type="submit"], input[type="button"], button')).find(button => {
            const label = String(button.value || button.textContent || "").trim();
            return regex.test(label);
        }) || null;
    }

    function getKissTheMortogCollectButton() {
        return getKissTheMortogButtonByValue(/^Collect Your Winnings/i);
    }

    function getKissTheMortogContinueButton() {
        return getKissTheMortogButtonByValue(/^Continue$/i);
    }

    function getKissTheMortogTryAgainButton() {
        return getKissTheMortogButtonByValue(/^Try again/i);
    }

    function getKissTheMortogReturnToGameLink() {
        const rootEl = getKissTheMortogGameRoot();
        return Array.from(rootEl.querySelectorAll("a[href]")).find(anchor => {
            let url;
            try { url = new URL(anchor.getAttribute("href") || anchor.href || "", location.href); } catch (_) { return false; }
            const text = String(anchor.textContent || "").trim().toLowerCase();
            return /^(www\.)?neopets\.com$/i.test(url.hostname)
                && url.pathname === KISS_THE_MORTOG_PLAY_PATH
                && (!url.search || text.includes("here") || text.includes("game") || text.includes("try"));
        }) || null;
    }

    function getKissTheMortogCurrentScoreFromCollectButton(collectButton) {
        if (!collectButton) return 0;
        const rawScore = String(collectButton.value || collectButton.textContent || "").replace(/[^0-9]+/g, "");
        return Number(rawScore) || 0;
    }

    function detectKissTheMortogPageState() {
        const pageText = getKissTheMortogPageText();
        const collectButton = getKissTheMortogCollectButton();
        const tryAgainButton = getKissTheMortogTryAgainButton();
        const mortogLinks = getKissTheMortogMortogLinks();
        if (/ERROR|wrong button/i.test(pageText)) return { type: KISS_THE_MORTOG_STATE.ERROR_PAGE, reason: "Texto de erro detectado na área do jogo." };
        if (tryAgainButton) return { type: KISS_THE_MORTOG_STATE.TRY_AGAIN, tryAgainButton };
        if (collectButton) return { type: KISS_THE_MORTOG_STATE.COLLECT_OR_CONTINUE, collectButton, continueButton: getKissTheMortogContinueButton(), currentScore: getKissTheMortogCurrentScoreFromCollectButton(collectButton) };
        if (mortogLinks.length) return { type: KISS_THE_MORTOG_STATE.MORTOG_SELECT, mortogLinks };
        return { type: KISS_THE_MORTOG_STATE.UNKNOWN, reason: "Nenhum elemento conhecido foi encontrado na área do jogo." };
    }

    function clickKissTheMortogSafely(element, statusText, extra = {}) {
        if (!element || kissTheMortogAlreadyActing) return false;
        kissTheMortogAlreadyActing = true;
        updateKissTheMortogStatus(statusText);
        saveKissTheMortogReport("action-click-scheduled", statusText, extra);
        window.setTimeout(() => {
            try {
                element.click();
            } catch (error) {
                kissTheMortogAlreadyActing = false;
                const message = `Erro ao clicar: ${errorToMessage(error)}`;
                updateKissTheMortogStatus(message);
                saveKissTheMortogReport("click-error", message, { error: errorToMessage(error) });
            }
        }, kissTheMortogRandomDelay(KISS_THE_MORTOG_CLICK_DELAY_MIN, KISS_THE_MORTOG_CLICK_DELAY_MAX));
        return true;
    }

    function collectAndStopKissTheMortog(collectButton, currentScore) {
        if (!collectButton || kissTheMortogAlreadyActing) return false;
        try { sessionStorage.removeItem(KISS_THE_MORTOG_SESSION_PLAYING_KEY); } catch (_) {}
        if (kissTheMortogTimeout) window.clearTimeout(kissTheMortogTimeout);
        kissTheMortogTimeout = null;
        kissTheMortogAlreadyActing = true;
        updateKissTheMortogPanelState();
        const message = `Meta atingida: ${formatKissTheMortogNumber(currentScore)} NP. Coletando e desligando...`;
        updateKissTheMortogStatus(message);
        saveKissTheMortogReport("collect-scheduled", message, { currentScore });
        window.setTimeout(() => {
            try {
                collectButton.click();
            } catch (error) {
                kissTheMortogAlreadyActing = false;
                const errorMessage = `Erro ao coletar: ${errorToMessage(error)}`;
                updateKissTheMortogStatus(errorMessage);
                saveKissTheMortogReport("collect-error", errorMessage, { error: errorToMessage(error), currentScore });
            }
        }, kissTheMortogRandomDelay(KISS_THE_MORTOG_CLICK_DELAY_MIN, KISS_THE_MORTOG_CLICK_DELAY_MAX));
        return true;
    }

    function handleKissTheMortogPageState(state) {
        if (kissTheMortogAlreadyActing) {
            updateKissTheMortogStatus("Ação já enviada. Aguardando carregamento...");
            return false;
        }
        switch (state.type) {
            case KISS_THE_MORTOG_STATE.MORTOG_SELECT: {
                const links = state.mortogLinks || [];
                if (!links.length) {
                    updateKissTheMortogStatus("Estado Mortog detectado, mas nenhum link válido foi encontrado.");
                    return false;
                }
                const chosenLink = links[kissTheMortogRandomDelay(0, links.length - 1)];
                return clickKissTheMortogSafely(chosenLink, `Escolhendo 1 de ${links.length} Mortogs...`, { state: state.type, availableMortogs: links.length });
            }
            case KISS_THE_MORTOG_STATE.COLLECT_OR_CONTINUE: {
                const currentScore = Number(state.currentScore) || 0;
                const safeTarget = getKissTheMortogTarget();
                if (currentScore >= KISS_THE_MORTOG_MAX_SAFE_SCORE || currentScore >= safeTarget) {
                    return collectAndStopKissTheMortog(state.collectButton, currentScore);
                }
                if (state.continueButton) {
                    return clickKissTheMortogSafely(state.continueButton, `Pontuação atual: ${formatKissTheMortogNumber(currentScore)} NP. Continuando até ${formatKissTheMortogNumber(safeTarget)} NP...`, { state: state.type, currentScore, target: safeTarget });
                }
                updateKissTheMortogStatus("Pontuação encontrada, mas o botão Continue não foi localizado.");
                return false;
            }
            case KISS_THE_MORTOG_STATE.TRY_AGAIN:
                return clickKissTheMortogSafely(state.tryAgainButton, "Rodada perdida. Tentando novamente...", { state: state.type });
            case KISS_THE_MORTOG_STATE.ERROR_PAGE: {
                const returnLink = getKissTheMortogReturnToGameLink();
                if (returnLink) return clickKissTheMortogSafely(returnLink, "Tela de erro detectada. Voltando ao jogo...", { state: state.type });
                updateKissTheMortogStatus("Tela de erro detectada, mas nenhum link de retorno foi encontrado.");
                return false;
            }
            case KISS_THE_MORTOG_STATE.UNKNOWN:
            default:
                updateKissTheMortogStatus("Nenhuma ação encontrada na área do jogo.");
                return false;
        }
    }

    function runKissTheMortogLoop(reason) {
        if (!isKissTheMortogPage()) return false;
        createKissTheMortogPanel();
        if (kissTheMortogTimeout) window.clearTimeout(kissTheMortogTimeout);
        if (!getKissTheMortogPlaying()) {
            updateKissTheMortogStatus("Desligado.");
            saveKissTheMortogReport("standby", "Painel visível; autoplayer desligado.", { reason });
            return false;
        }
        const delay = kissTheMortogRandomDelay();
        updateKissTheMortogStatus(`Ligado. Próxima ação em ${(delay / 1000).toFixed(1)}s...`);
        kissTheMortogTimeout = window.setTimeout(() => {
            if (!getKissTheMortogPlaying()) {
                updateKissTheMortogStatus("Desligado.");
                saveKissTheMortogReport("disabled-before-action", "Autoplayer desligado antes da ação.", { reason });
                return;
            }
            const state = detectKissTheMortogPageState();
            saveKissTheMortogReport("state-detected", `Estado detectado: ${state.type}.`, { stateType: state.type, reason });
            handleKissTheMortogPageState(state);
        }, delay);
        return true;
    }

    function injectKissTheMortogPanelStyle() {
        if (document.getElementById("ncc-aaa-kiss-the-mortog-style")) return;
        const style = document.createElement("style");
        style.id = "ncc-aaa-kiss-the-mortog-style";
        style.textContent = `
            #ncc-aaa-kiss-the-mortog-panel {
                position: fixed;
                top: 135px;
                right: 16px;
                z-index: 2147483647;
                width: 232px;
                padding: 11px;
                box-sizing: border-box;
                background: #fffbe8;
                border: 2px solid #7d9b35;
                border-radius: 12px;
                box-shadow: 0 8px 26px rgba(0, 0, 0, 0.26);
                text-align: center;
                font-family: Arial, sans-serif;
                font-size: 12px;
                color: #333;
            }
            #ncc-aaa-kiss-the-mortog-panel .ncc-aaa-ktm-title {
                display: flex;
                align-items: center;
                justify-content: center;
                gap: 7px;
                font-weight: 900;
                font-size: 14px;
                color: #3f6418;
                margin-bottom: 8px;
            }
            #ncc-aaa-kiss-the-mortog-panel .ncc-aaa-ktm-title img { width: 24px; height: 24px; object-fit: contain; }
            #ncc-aaa-kiss-the-mortog-panel label { display: block; margin-bottom: 8px; font-weight: 700; }
            #ncc-aaa-kiss-the-mortog-target { width: 100%; margin-top: 5px; font-size: 12px; }
            #ncc-aaa-kiss-the-mortog-toggle {
                width: 100%;
                padding: 7px;
                border-radius: 7px;
                border: 1px solid #4d7022;
                background: #dff2bf;
                color: #263b10;
                font-weight: 900;
                cursor: pointer;
            }
            #ncc-aaa-kiss-the-mortog-toggle.ncc-aaa-ktm-running {
                background: #ffd4d4;
                border-color: #9b2d2d;
                color: #6d1111;
            }
            #ncc-aaa-kiss-the-mortog-status { margin-top: 8px; font-size: 11px; line-height: 1.35; color: #555; }
        `;
        document.head.appendChild(style);
    }

    function createKissTheMortogPanel() {
        if (!isKissTheMortogPage()) return null;
        injectKissTheMortogPanelStyle();
        let panel = document.querySelector("#ncc-aaa-kiss-the-mortog-panel");
        if (panel) {
            updateKissTheMortogPanelState();
            return panel;
        }
        panel = document.createElement("div");
        panel.id = "ncc-aaa-kiss-the-mortog-panel";
        panel.innerHTML = `
            <div class="ncc-aaa-ktm-title"><img src="${escapeAttribute(KISS_THE_MORTOG_ICON_URL)}" alt=""> Kiss The Mortog</div>
            <label>
                Parar em:
                <select id="ncc-aaa-kiss-the-mortog-target">
                    ${KISS_THE_MORTOG_TARGET_OPTIONS.map(option => `<option value="${escapeAttribute(option.value)}">${escapeHtml(option.label)}</option>`).join("")}
                </select>
            </label>
            <button id="ncc-aaa-kiss-the-mortog-toggle" type="button"></button>
            <div id="ncc-aaa-kiss-the-mortog-status"></div>
        `;
        document.body.appendChild(panel);
        const button = panel.querySelector("#ncc-aaa-kiss-the-mortog-toggle");
        const select = panel.querySelector("#ncc-aaa-kiss-the-mortog-target");
        if (select) {
            select.value = String(getKissTheMortogTarget());
            select.addEventListener("change", event => {
                setKissTheMortogTarget(event.target.value);
                updateKissTheMortogPanelState();
                saveKissTheMortogReport("target-changed", `Meta alterada para ${formatKissTheMortogNumber(getKissTheMortogTarget())} NP.`, { target: getKissTheMortogTarget() });
            });
        }
        if (button) {
            button.addEventListener("click", event => {
                event.preventDefault();
                event.stopPropagation();
                const next = !getKissTheMortogPlaying();
                setKissTheMortogPlaying(next);
                updateKissTheMortogPanelState();
                if (next) runKissTheMortogLoop("toggle-on");
            });
        }
        updateKissTheMortogPanelState();
        return panel;
    }

    function bootKissTheMortogAutoplayer(reason) {
        if (!isKissTheMortogPage()) return null;
        if (window.__NCC_AAA_KISS_THE_MORTOG_BOOTED__) return buildKissTheMortogReport("already-active");
        window.__NCC_AAA_KISS_THE_MORTOG_BOOTED__ = true;
        createKissTheMortogPanel();
        saveKissTheMortogReport("boot", getKissTheMortogPlaying() ? "Kiss The Mortog ligado nesta sessão; execução agendada." : "Kiss The Mortog desligado; painel flutuante disponível.", { reason });
        if (getKissTheMortogPlaying()) runKissTheMortogLoop("boot");
        return buildKissTheMortogReport("boot");
    }

    function saveKissTheMortogReport(status, message, extra = {}) {
        lastKissTheMortogReport = clonePlain(Object.assign({
            schema: KISS_THE_MORTOG_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "kiss-the-mortog-autoplayer",
            module: "kiss-the-mortog",
            pageOnly: KISS_THE_MORTOG_PLAY_PATH,
            processPage: KISS_THE_MORTOG_PROCESS_PATH,
            href: KISS_THE_MORTOG_URL,
            currentPage: location.pathname,
            status: status || "unknown",
            active: isKissTheMortogPage(),
            enabled: getKissTheMortogPlaying(),
            target: getKissTheMortogTarget(),
            maxSafeScore: KISS_THE_MORTOG_MAX_SAFE_SCORE,
            defaultEnabled: false,
            requiresUserToggle: true,
            sessionOnlyToggle: true,
            automationScope: "Kiss The Mortog pages only",
            programmaticClicks: "only when floating panel is ON in this session",
            formSubmit: "only when floating panel is ON in this session",
            networkAccessAdded: false,
            storageKeys: {
                enabledSession: KISS_THE_MORTOG_SESSION_PLAYING_KEY,
                target: KISS_THE_MORTOG_TARGET_KEY,
                report: KISS_THE_MORTOG_REPORT_KEY
            },
            message: message || "",
            updatedAt: isoNow()
        }, extra || {}));
        writeJson(KISS_THE_MORTOG_REPORT_KEY, lastKissTheMortogReport);
        dispatchStateEvent(window, KISS_THE_MORTOG_EVENT, lastKissTheMortogReport);
        try {
            const publicWindow = getPublicWindow();
            if (publicWindow && publicWindow !== window) dispatchStateEvent(publicWindow, KISS_THE_MORTOG_EVENT, lastKissTheMortogReport);
        } catch (_) {}
        return lastKissTheMortogReport;
    }

    function buildKissTheMortogReport(reason) {
        const fallback = readJson(KISS_THE_MORTOG_REPORT_KEY, null);
        return clonePlain(lastKissTheMortogReport || fallback || {
            schema: KISS_THE_MORTOG_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "kiss-the-mortog-autoplayer",
            module: "kiss-the-mortog",
            pageOnly: KISS_THE_MORTOG_PLAY_PATH,
            processPage: KISS_THE_MORTOG_PROCESS_PATH,
            href: KISS_THE_MORTOG_URL,
            currentPage: location.pathname,
            status: isKissTheMortogPage() ? "standby" : "available",
            active: isKissTheMortogPage(),
            enabled: getKissTheMortogPlaying(),
            target: getKissTheMortogTarget(),
            maxSafeScore: KISS_THE_MORTOG_MAX_SAFE_SCORE,
            defaultEnabled: false,
            requiresUserToggle: true,
            sessionOnlyToggle: true,
            automationScope: "Kiss The Mortog pages only",
            programmaticClicks: "only when floating panel is ON in this session",
            formSubmit: "only when floating panel is ON in this session",
            networkAccessAdded: false,
            message: reason === "bridge" ? "Relatório lido via bridge." : "Abra Kiss The Mortog para usar o painel flutuante.",
            updatedAt: null
        });
    }


    function readGodoriSessionEnabled() {
        try {
            return sessionStorage.getItem(GODORI_SESSION_ENABLED_KEY) === "1";
        } catch (_) {
            return false;
        }
    }

    function readGodoriStoredBool(key, defaultValue = false) {
        try {
            const raw = localStorage.getItem(key);
            if (raw === null) return defaultValue;
            return raw === "1";
        } catch (_) {
            return defaultValue;
        }
    }

    function getGodoriPageState() {
        const publicWindow = getPublicWindow();
        const api = window.NCC_GODORI_API || (publicWindow && publicWindow.NCC_GODORI_API) || null;
        const state = window.NCC_GODORI_STATE || (publicWindow && publicWindow.NCC_GODORI_STATE) || null;
        const ui = document.getElementById("ncc-godori-safe-core");
        return { api, state, uiMounted: Boolean(ui) };
    }

    function bootGodoriAutoplayer(reason) {
        if (!isGodoriAutoplayerPage()) return null;
        if (window.__NCC_AAA_GODORI_BOOTED__) return buildGodoriReport("already-active");
        window.__NCC_AAA_GODORI_BOOTED__ = true;
        injectGodoriAutoplayerSource(reason || "boot");
        window.setTimeout(() => saveGodoriReport("boot", readGodoriSessionEnabled() ? "Godori ligado nesta sessão; módulo injetado." : "Godori desligado; painel flutuante disponível.", { reason }), 350);
        return buildGodoriReport("boot");
    }

    function injectGodoriAutoplayerSource(reason) {
        if (document.getElementById("ncc-aaa-godori-source")) return true;
        const script = document.createElement("script");
        script.id = "ncc-aaa-godori-source";
        script.type = "text/javascript";
        script.dataset.nccAaaModule = "godori-autoplayer";
        script.dataset.nccAaaVersion = VERSION;
        script.textContent = GODORI_MODULE_SOURCE;
        (document.head || document.documentElement || document.body).appendChild(script);
        saveGodoriReport("source-injected", "Godori Autoplayer injetado na página oficial.", { reason });
        return true;
    }

    function saveGodoriReport(status, message, extra = {}) {
        const live = getGodoriPageState();
        const liveState = live.state || {};
        lastGodoriReport = clonePlain(Object.assign({
            schema: GODORI_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "godori-autoplayer",
            godoriVersion: GODORI_SOURCE_VERSION,
            status,
            message: message || "",
            active: isGodoriAutoplayerPage(),
            enabled: Boolean(liveState.enabled || readGodoriSessionEnabled()),
            autoPlayAgain: Boolean(liveState.autoPlayAgain ?? readGodoriStoredBool(GODORI_AUTO_PLAY_AGAIN_KEY, false)),
            autoStopAtTarget: Boolean(liveState.autoStopAtTarget ?? readGodoriStoredBool(GODORI_AUTO_STOP_250_KEY, true)),
            pageOnly: GODORI_PATH_PREFIX,
            href: GODORI_URL,
            uiMounted: live.uiMounted,
            bridgeReady: Boolean(live.api || liveState.integration?.bridgeReady),
            avatarTargetHands: Number(liveState.avatarTargetHands || 250),
            handsWon: Number(liveState.handsWon || 0),
            remainingHands: Number(liveState.remainingHands || 250),
            avatarProgressPct: Number(liveState.avatarProgressPct || 0),
            lastLog: liveState.lastLog || "",
            lastMove: liveState.lastMove || "",
            lastDecision: liveState.lastDecision || "",
            automationScope: "Godori pages only",
            safetyClass: "autoplayer-explicit-toggle",
            enabledByDefault: false,
            sessionOnlyToggle: true,
            canClickGameAction: true,
            canSubmitGameForm: true,
            formSubmit: "only when floating panel is ON in this session",
            networkAccessAdded: false,
            storageKeys: {
                enabledSession: GODORI_SESSION_ENABLED_KEY,
                autoPlayAgain: GODORI_AUTO_PLAY_AGAIN_KEY,
                autoStop250: GODORI_AUTO_STOP_250_KEY,
                report: GODORI_REPORT_KEY
            },
            updatedAt: Date.now(),
            updatedAtIso: isoNow()
        }, extra || {}));
        writeJson(GODORI_REPORT_KEY, lastGodoriReport);
        dispatchStateEvent(window, GODORI_EVENT, lastGodoriReport);
        const publicWindow = getPublicWindow();
        if (publicWindow && publicWindow !== window) dispatchStateEvent(publicWindow, GODORI_EVENT, lastGodoriReport);
        return lastGodoriReport;
    }

    function buildGodoriReport(reason) {
        const live = getGodoriPageState();
        if (live.state) {
            return saveGodoriReport(reason === "bridge" ? "bridge-live" : "live", reason === "bridge" ? "Relatório Godori lido via bridge." : "Estado Godori sincronizado.", { liveStateAvailable: true });
        }
        const fallback = readJson(GODORI_REPORT_KEY, null);
        return clonePlain(lastGodoriReport || fallback || {
            schema: GODORI_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "godori-autoplayer",
            godoriVersion: GODORI_SOURCE_VERSION,
            status: isGodoriAutoplayerPage() ? "standby" : "available",
            active: isGodoriAutoplayerPage(),
            enabled: readGodoriSessionEnabled(),
            autoPlayAgain: readGodoriStoredBool(GODORI_AUTO_PLAY_AGAIN_KEY, false),
            autoStopAtTarget: readGodoriStoredBool(GODORI_AUTO_STOP_250_KEY, true),
            pageOnly: GODORI_PATH_PREFIX,
            href: GODORI_URL,
            uiMounted: live.uiMounted,
            bridgeReady: Boolean(live.api),
            avatarTargetHands: 250,
            handsWon: 0,
            remainingHands: 250,
            avatarProgressPct: 0,
            safetyClass: "autoplayer-explicit-toggle",
            enabledByDefault: false,
            sessionOnlyToggle: true,
            canClickGameAction: true,
            canSubmitGameForm: true,
            formSubmit: "only when floating panel is ON in this session",
            networkAccessAdded: false,
            message: reason === "bridge" ? "Relatório lido via bridge." : "Abra Godori para usar o painel flutuante.",
            updatedAt: Date.now(),
            updatedAtIso: isoNow()
        });
    }

    function bootCellblockHelper() {
        const delays = [150, 500, 1000, 1800, 3000, 4500];
        delays.forEach((delay, index) => {
            window.setTimeout(() => runCellblockHelper(`boot-scan-${index + 1}`), delay);
        });
    }

    function runCellblockHelper(reason) {
        const startedAt = isoNow();
        const report = {
            schema: "ncc-aaa-cellblock-helper-v1",
            version: VERSION,
            source: "ncc-aaa",
            status: "scanning",
            reason: reason || "manual-scan",
            page: location.pathname,
            boardFound: false,
            knownCells: 0,
            unknownCells: 0,
            suggestions: 0,
            painted: 0,
            colors: { darkgreen: 0, darkred: 0, limegreen: 0, yellow: 0 },
            tagCounts: { me: 0, enemy: 0, open: 0, block: 0, unknown: 0 },
            boardScore: 0,
            message: "Lendo tabuleiro do Cellblock.",
            updatedAt: startedAt
        };

        try {
            const board = findCellblockBoard();
            if (!board) {
                report.status = "waiting-board";
                report.message = "Tabuleiro 10x10 ainda não foi localizado.";
                mountCellblockStatusNote(report);
                saveCellblockReport(report);
                return false;
            }

            report.boardFound = true;
            report.knownCells = board.knownCells;
            report.unknownCells = Math.max(0, 100 - board.knownCells);
            report.boardScore = board.score || 0;
            clearCellblockHints(board);

            const coords = [];
            const groups = { right: [], left: [], down: [], up: [], upRight: [], upLeft: [], downRight: [], downLeft: [] };
            const suggestions = [];
            let iter = -1;

            for (let y = 1; y <= 10; y++) {
                for (let x = 1; x <= 10; x++) {
                    const cell = getCellblockGridCell(y, x, board);
                    const tag = getCellblockCellTag(cell, true);
                    if (report.tagCounts[tag] != null) report.tagCounts[tag] += 1;
                    else report.tagCounts.unknown += 1;
                    coords.push({ x, y, tag });
                }
            }

            coords.forEach((origin, index) => {
                if (origin.tag !== "me" && origin.tag !== "enemy") return;
                iter += 1;
                for (let d = 1; d < 5; d++) {
                    addCellblockOffset(coords, groups.right, index, d, 1, 0, origin.tag, iter);
                    addCellblockOffset(coords, groups.left, index, d, -1, 0, origin.tag, iter);
                    addCellblockOffset(coords, groups.down, index, d, 0, 1, origin.tag, iter);
                    addCellblockOffset(coords, groups.up, index, d, 0, -1, origin.tag, iter);
                    addCellblockOffset(coords, groups.upRight, index, d, 1, -1, origin.tag, iter);
                    addCellblockOffset(coords, groups.upLeft, index, d, -1, -1, origin.tag, iter);
                    addCellblockOffset(coords, groups.downRight, index, d, 1, 1, origin.tag, iter);
                    addCellblockOffset(coords, groups.downLeft, index, d, -1, 1, origin.tag, iter);
                }
            });

            Object.keys(groups).forEach(key => checkCellblockCombo(groups[key], iter, board, suggestions));
            report.suggestions = suggestions.length;
            report.painted = suggestions.filter(item => item.painted).length;
            suggestions.forEach(item => {
                if (report.colors[item.color] != null) report.colors[item.color] += 1;
            });
            report.status = report.painted > 0 ? "active-painted" : "active-no-suggestions";
            report.message = report.painted > 0
                ? `Sugestões coloridas: ${report.painted}.`
                : "Helper rodou, mas esta leitura não encontrou jogada destacável pelo padrão do helper.";
            report.updatedAt = isoNow();
            mountCellblockStatusNote(report);
            saveCellblockReport(report);
            return report.painted > 0;
        } catch (error) {
            report.status = "error";
            report.message = errorToMessage(error);
            report.updatedAt = isoNow();
            mountCellblockStatusNote(report);
            saveCellblockReport(report);
            return false;
        }
    }

    function findCellblockBoard() {
        const exact = findExactCellblockBoard();
        if (exact) return exact;

        const scope = document.querySelector("#neopost") || document.querySelector("td.content") || document;
        const tables = Array.from(scope.querySelectorAll("table"));
        let best = null;

        tables.forEach((table, tableIndex) => {
            const candidate = extractCellblockBoardFromTable(table, tableIndex, "scan");
            if (candidate && (!best || candidate.score > best.score)) best = candidate;
        });

        if (!best) return null;
        const hasBoardShape = best.squareScore >= 70 || best.cellblockImageHits >= 20 || best.pieceCells >= 2 || best.exactCellblockBackground;
        const hasEnoughCells = best.knownCells >= 80 || (best.openCells + best.blockCells + best.pieceCells) >= 80;
        return hasBoardShape && hasEnoughCells ? best : null;
    }

    function findExactCellblockBoard() {
        const scope = document.querySelector("#neopost") || document.querySelector("td.content") || document;
        const selectors = [
            "table[background*='games/cellblock/bg.jpg']",
            "table[background*='cellblock/bg.jpg']",
            "table[bgcolor='#27324e']",
            "table[bgcolor='#27324E']"
        ];
        const seen = new Set();
        for (const selector of selectors) {
            const tables = Array.from(scope.querySelectorAll(selector));
            for (const table of tables) {
                if (seen.has(table)) continue;
                seen.add(table);
                const candidate = extractCellblockBoardFromTable(table, 0, "exact");
                if (candidate) return candidate;
            }
        }
        return null;
    }

    function extractCellblockBoardFromTable(table, tableIndex, mode) {
        const directRows = getCellblockDirectRows(table);
        if (directRows.length < 10) return null;
        let best = null;
        const exactCellblockBackground = tableHasCellblockBackground(table);

        for (let start = 0; start <= directRows.length - 10; start++) {
            const boardRows = directRows.slice(start, start + 10);
            if (!boardRows.every(row => getCellblockDirectCells(row).length >= 10)) continue;

            let knownCells = 0;
            let pieceCells = 0;
            let openCells = 0;
            let blockCells = 0;
            let cellblockImageHits = 0;
            let squareScore = 0;

            for (let y = 0; y < 10; y++) {
                const cells = getCellblockDirectCells(boardRows[y]);
                for (let x = 0; x < 10; x++) {
                    const cell = cells[x];
                    const sources = getCellblockCellSources(cell);
                    const tag = getCellblockCellTag(cell, true);
                    if (tag !== "unknown") knownCells += 1;
                    if (tag === "me" || tag === "enemy") pieceCells += 1;
                    if (tag === "open") openCells += 1;
                    if (tag === "block") blockCells += 1;
                    if (sources.some(src => isLikelyCellblockAsset(src))) cellblockImageHits += 1;
                    if (looksLikeCellblockSquare(cell)) squareScore += 1;
                }
            }

            const score = (exactCellblockBackground ? 500 : 0) + (knownCells * 2) + (pieceCells * 35) + (cellblockImageHits * 4) + squareScore - Math.max(0, blockCells - 80);
            const candidate = { table, rows: boardRows, knownCells, pieceCells, openCells, blockCells, cellblockImageHits, squareScore, score, tableIndex, rowOffset: start, mode, exactCellblockBackground };
            if (!best || candidate.score > best.score) best = candidate;
        }

        return best;
    }

    function getCellblockDirectRows(table) {
        const rows = [];
        Array.from(table && table.children ? table.children : []).forEach(child => {
            const tag = String(child.tagName || "").toUpperCase();
            if (tag === "TR") rows.push(child);
            if (tag === "TBODY" || tag === "THEAD" || tag === "TFOOT") {
                Array.from(child.children || []).forEach(row => {
                    if (String(row.tagName || "").toUpperCase() === "TR") rows.push(row);
                });
            }
        });
        return rows;
    }

    function getCellblockDirectCells(row) {
        return Array.from(row && row.children ? row.children : []).filter(child => /^(TD|TH)$/i.test(String(child.tagName || "")));
    }

    function tableHasCellblockBackground(table) {
        const raw = [
            table && table.getAttribute ? table.getAttribute("background") : "",
            table && table.style ? table.style.backgroundImage : "",
            table && table.getAttribute ? table.getAttribute("style") : ""
        ].join(" ").toLowerCase();
        return raw.includes("games/cellblock/bg.jpg") || raw.includes("cellblock/bg.jpg") || raw.includes("#27324e");
    }

    function getCellblockGridCell(row, col, board) {
        if (board && board.rows && board.rows[row - 1]) {
            const cells = getCellblockDirectCells(board.rows[row - 1]);
            if (cells[col - 1]) return cells[col - 1];
        }
        const exact = findExactCellblockBoard();
        if (exact && exact.rows && exact.rows[row - 1]) {
            const cells = getCellblockDirectCells(exact.rows[row - 1]);
            if (cells[col - 1]) return cells[col - 1];
        }
        return document.querySelector(`#neopost table[background*="cellblock/bg.jpg"] tr:nth-child(${row}) > td:nth-child(${col})`)
            || document.querySelector(`#neopost table[bgcolor="#27324e"] tr:nth-child(${row}) > td:nth-child(${col})`);
    }

    function getCellblockCellTag(cell, assumeOpenFallback) {
        if (!cell) return "unknown";
        const sources = getCellblockCellSources(cell);
        let hasBlock = false;
        let hasOpen = false;
        for (const source of sources) {
            const tag = getCellblockTag(source);
            if (tag === "me" || tag === "enemy") return tag;
            if (tag === "open") hasOpen = true;
            if (tag === "block") hasBlock = true;
        }
        if (hasOpen) return "open";
        if (hasBlock) return "block";
        return assumeOpenFallback ? "open" : "unknown";
    }

    function getCellblockCellSources(cell) {
        if (!cell) return [];
        const sources = [];
        Array.from(cell.querySelectorAll("img, input[type='image']")).forEach(node => {
            const current = node.currentSrc || node.src || node.getAttribute("src") || "";
            if (current) sources.push(current);
        });
        const attrs = [cell.getAttribute("background"), cell.style && cell.style.backgroundImage, cell.getAttribute("style")];
        attrs.forEach(value => extractUrlsFromCss(value).forEach(url => sources.push(url)));
        return sources.filter(Boolean);
    }

    function extractUrlsFromCss(value) {
        const text = String(value || "");
        const urls = [];
        text.replace(/url\(["']?([^"')]+)["']?\)/gi, (_, url) => {
            if (url) urls.push(url);
            return "";
        });
        if (/\.(?:gif|png|jpg|jpeg|webp)(?:$|[?#])/i.test(text) && !text.includes("url(")) urls.push(text);
        return urls;
    }

    function isLikelyCellblockAsset(src) {
        const value = String(src || "").toLowerCase();
        return value.includes("/games/cellblock/") || /(?:^|\/)(?:block[1-4]|blank|merridell|darigan|red[^\/]*shield|black[^\/]*shield)[^\/]*\.gif(?:$|[?#])/.test(value);
    }

    function looksLikeCellblockSquare(cell) {
        if (!cell || typeof cell.getBoundingClientRect !== "function") return false;
        const rect = cell.getBoundingClientRect();
        const width = rect && rect.width ? rect.width : Number(cell.width || 0);
        const height = rect && rect.height ? rect.height : Number(cell.height || 0);
        if (!width || !height) return false;
        if (width < 18 || width > 55 || height < 18 || height > 55) return false;
        return Math.abs(width - height) <= 12;
    }

    function getCellblockTag(src) {
        const value = String(src || "").toLowerCase();
        const filename = value.split("/").pop() || value;
        if (/block[1-4]\.gif(?:$|[?#])/.test(filename)) return "block";
        if (/blank\.gif(?:$|[?#])/.test(filename)) return "open";
        if (filename.includes("merridell") || filename.includes("redshield") || filename.includes("red-shield") || filename.includes("merri")) return "me";
        if (filename.includes("darigan") || filename.includes("blackshield") || filename.includes("black-shield") || filename.includes("darkshield")) return "enemy";
        return "unknown";
    }

    function addCellblockOffset(coords, bucket, index, distance, dx, dy, originTag, iter) {
        const origin = coords[index];
        const x = origin.x + dx * distance;
        const y = origin.y + dy * distance;
        if (x < 1 || x > 10 || y < 1 || y > 10) return;
        const target = coords[(y - 1) * 10 + (x - 1)];
        if (!target) return;
        bucket.push({ x: target.x, y: target.y, tag: target.tag, orig: originTag, iter });
    }

    function checkCellblockCombo(items, maxIter, board, suggestions) {
        let setupWinLocked = false;
        let setupBlockLocked = false;
        for (let i = 0; i <= maxIter; i++) {
            const combo = items.filter(item => item.iter === i);
            const meCount = combo.filter(item => item.tag === "me").length;
            const enemyCount = combo.filter(item => item.tag === "enemy").length;
            const openCount = combo.filter(item => item.tag === "open").length;
            combo.forEach(item => {
                if (item.tag !== "open") return;
                if (meCount === 3 && item.orig === "me") paintCellblockHint(item, "darkgreen", true, board, suggestions, "win-now");
                else if (enemyCount === 3 && item.orig === "enemy") paintCellblockHint(item, "darkred", true, board, suggestions, "block-now");
                else if (!setupWinLocked && meCount === 2 && openCount === 2 && item.orig === "me") {
                    paintCellblockHint(item, "limegreen", false, board, suggestions, "setup-win");
                    setupWinLocked = true;
                } else if (!setupBlockLocked && enemyCount === 2 && openCount === 2 && item.orig === "enemy") {
                    paintCellblockHint(item, "yellow", false, board, suggestions, "setup-block");
                    setupBlockLocked = true;
                }
            });
        }
    }

    function paintCellblockHint(item, color, bordered, board, suggestions, type) {
        const cell = getCellblockGridCell(item.y, item.x, board);
        if (!cell) return;
        const current = cell.getAttribute("data-ncc-aaa-cellblock-color") || "";
        if (cellblockColorPriority(current) > cellblockColorPriority(color)) return;

        cell.classList.add(CELLBLOCK_HINT_CLASS);
        cell.setAttribute("data-ncc-aaa-cellblock-color", color);
        cell.setAttribute("title", cellblockHintTitle(color, item));
        cell.style.backgroundColor = color;
        cell.style.border = bordered ? `2px ${color} solid` : `1px ${color} solid`;
        cell.style.outline = `2px solid ${color}`;
        cell.style.outlineOffset = "-2px";
        cell.style.boxShadow = `inset 0 0 0 999px ${cellblockOverlayColor(color)}`;

        const img = cell.querySelector("img");
        if (img) {
            img.style.outline = `2px solid ${color}`;
            img.style.outlineOffset = "-2px";
            img.style.filter = `drop-shadow(0 0 4px ${color})`;
        }
        ensureCellblockOverlay(cell, color);

        const existing = suggestions.find(entry => entry.x === item.x && entry.y === item.y);
        const suggestion = { x: item.x, y: item.y, color, type, painted: true };
        if (existing) Object.assign(existing, suggestion);
        else suggestions.push(suggestion);
    }

    function ensureCellblockOverlay(cell, color) {
        if (!cell) return;
        try {
            const computed = window.getComputedStyle ? window.getComputedStyle(cell) : null;
            if (!computed || computed.position === "static") cell.style.position = "relative";
        } catch (_) {
            cell.style.position = "relative";
        }
        let overlay = cell.querySelector(`.${CELLBLOCK_OVERLAY_CLASS}`);
        if (!overlay) {
            overlay = document.createElement("span");
            overlay.className = CELLBLOCK_OVERLAY_CLASS;
            overlay.setAttribute("aria-hidden", "true");
            cell.appendChild(overlay);
        }
        overlay.style.position = "absolute";
        overlay.style.left = "0";
        overlay.style.top = "0";
        overlay.style.right = "0";
        overlay.style.bottom = "0";
        overlay.style.pointerEvents = "none";
        overlay.style.zIndex = "9999";
        overlay.style.borderRadius = "2px";
        overlay.style.boxSizing = "border-box";
        overlay.style.border = `3px solid ${color}`;
        overlay.style.background = cellblockOverlayColor(color);
        overlay.style.boxShadow = `0 0 10px ${color}, inset 0 0 10px ${color}`;
    }

    function clearCellblockHints(board) {
        if (!board || !board.rows) return;
        board.rows.forEach(row => {
            Array.from(row.cells || []).forEach(cell => {
                if (!cell.classList || !cell.classList.contains(CELLBLOCK_HINT_CLASS)) return;
                cell.classList.remove(CELLBLOCK_HINT_CLASS);
                cell.removeAttribute("data-ncc-aaa-cellblock-color");
                cell.removeAttribute("title");
                cell.style.backgroundColor = "";
                cell.style.border = "";
                cell.style.outline = "";
                cell.style.outlineOffset = "";
                cell.style.boxShadow = "";
                Array.from(cell.querySelectorAll(`.${CELLBLOCK_OVERLAY_CLASS}`)).forEach(node => node.remove());
                const img = cell.querySelector("img");
                if (img) {
                    img.style.outline = "";
                    img.style.outlineOffset = "";
                    img.style.filter = "";
                }
            });
        });
    }

    function cellblockColorPriority(color) {
        if (color === "darkgreen") return 4;
        if (color === "darkred") return 3;
        if (color === "limegreen") return 2;
        if (color === "yellow") return 1;
        return 0;
    }

    function cellblockOverlayColor(color) {
        if (color === "darkgreen") return "rgba(0, 100, 0, .22)";
        if (color === "darkred") return "rgba(139, 0, 0, .22)";
        if (color === "limegreen") return "rgba(50, 205, 50, .20)";
        if (color === "yellow") return "rgba(255, 230, 0, .22)";
        return "rgba(255, 255, 255, 0)";
    }

    function cellblockHintTitle(color, item) {
        const label = color === "darkgreen" ? "Vitória possível"
            : color === "darkred" ? "Bloqueio urgente"
            : color === "limegreen" ? "Prepara vitória"
            : color === "yellow" ? "Bloquear preparação"
            : "Sugestão";
        return `NCC AAA Cellblock: ${label} em ${item.x},${item.y}`;
    }

    function mountCellblockStatusNote(report) {
        if (!isCellblockPage()) return;
        const root = document.querySelector("#neopost") || document.querySelector("td.content") || document.body;
        if (!root || !root.insertBefore) return;
        let note = document.getElementById(CELLBLOCK_STATUS_NOTE_ID);
        if (!note) {
            note = document.createElement("div");
            note.id = CELLBLOCK_STATUS_NOTE_ID;
            root.insertBefore(note, root.firstChild || null);
        }
        const total = Number(report.painted || report.suggestions || 0);
        const counts = report.tagCounts || {};
        const ok = report.boardFound ? "OK" : "Aguardando";
        note.textContent = total > 0
            ? `NCC AAA Cellblock: ${total} destaque(s) aplicado(s). Vitória ${report.colors?.darkgreen || 0}, bloqueio ${report.colors?.darkred || 0}, preparo ${report.colors?.limegreen || 0}, alerta ${report.colors?.yellow || 0}.`
            : `NCC AAA Cellblock: leitura ${ok}. ${report.boardFound ? `Tabuleiro detectado (${counts.open || 0} vazias, ${counts.me || 0} suas, ${counts.enemy || 0} inimigas, ${counts.block || 0} bloqueadas), mas esta posição não tem destaque pelo padrão do helper original.` : report.message}`;
        note.style.cssText = [
            "max-width:620px",
            "margin:8px auto 12px",
            "padding:7px 10px",
            "border:1px solid #5b71ad",
            "border-left:5px solid #5b71ad",
            "background:#eef3ff",
            "color:#18284f",
            "font:700 12px Verdana, Arial, sans-serif",
            "text-align:left",
            "border-radius:6px"
        ].join(";");
    }

    function saveCellblockReport(report) {
        lastCellblockReport = Object.assign({}, report);
        writeJson(CELLBLOCK_LATEST_KEY, lastCellblockReport);
        dispatchCellblockReport(lastCellblockReport);
    }

    function buildCellblockReport() {
        return clonePlain(lastCellblockReport || readJson(CELLBLOCK_LATEST_KEY, {
            schema: "ncc-aaa-cellblock-helper-v1",
            version: VERSION,
            source: "ncc-aaa",
            status: "not-run-yet",
            page: location.pathname,
            boardFound: false,
            knownCells: 0,
            unknownCells: 0,
            suggestions: 0,
            painted: 0,
            message: "Abra a página oficial do Cellblock para o helper atuar.",
            updatedAt: null
        }));
    }

    function dispatchCellblockReport(report) {
        const detail = clonePlain(report);
        try {
            window.dispatchEvent(new CustomEvent(CELLBLOCK_EVENT, { detail }));
        } catch (_) {}
        const publicWindow = getPublicWindow();
        if (publicWindow && publicWindow !== window) {
            try {
                publicWindow.dispatchEvent(new CustomEvent(CELLBLOCK_EVENT, { detail }));
            } catch (_) {}
        }
    }

    function bootKikoPopFix() {
        [250, 900, 1800].forEach((delay, index) => {
            window.setTimeout(() => mountKikoPopFix(`boot-${index + 1}`), delay);
        });
    }

    function mountKikoPopFix(reason) {
        if (!isKikoPopPage()) return;
        ensureKikoPopFixStyle();

        const pageDesc = document.querySelector("#pageDesc");
        const content = document.querySelector("td.content") || document.body;
        const parent = pageDesc?.parentNode || content;
        const originalDifficultyForm = document.querySelector("#difficultyForm");
        if (originalDifficultyForm) originalDifficultyForm.style.display = "none";

        let panel = document.getElementById(KIKO_POP_FIX_ID);
        if (!panel) {
            panel = document.createElement("section");
            panel.id = KIKO_POP_FIX_ID;
            panel.setAttribute("aria-label", "Kiko Pop Fix do NCC AAA");

            const title = document.createElement("h3");
            title.textContent = "🎯 Kiko Pop Fix";

            const desc = document.createElement("p");
            desc.textContent = "Escolha a dificuldade. Depois, confirme separadamente no botão GET PRIZE.";

            const actions = document.createElement("div");
            actions.className = "ncc-aaa-kiko-actions";

            [[1, "EASY"], [2, "MEDIUM"], [3, "HARD"]].forEach(([difficulty, label]) => {
                const button = document.createElement("button");
                button.type = "button";
                button.className = "ncc-aaa-kiko-choice";
                button.textContent = label;
                button.addEventListener("click", () => chooseKikoPopDifficulty(difficulty));
                actions.appendChild(button);
            });

            const status = document.createElement("div");
            status.className = "ncc-aaa-kiko-status";
            status.setAttribute("data-kiko-status", "ready");
            status.textContent = "Kiko Pop Fix pronto. Escolha uma dificuldade.";

            const prizeWrap = document.createElement("div");
            prizeWrap.className = "ncc-aaa-kiko-prize-wrap";
            prizeWrap.hidden = true;

            const prizeButton = document.createElement("button");
            prizeButton.type = "button";
            prizeButton.id = "ncc-aaa-kiko-get-prize";
            prizeButton.className = "ncc-aaa-kiko-prize";
            prizeButton.textContent = "GET PRIZE";
            prizeButton.addEventListener("click", requestKikoPopPrize);
            prizeWrap.appendChild(prizeButton);

            const original = document.createElement("button");
            original.type = "button";
            original.className = "ncc-aaa-kiko-original";
            original.textContent = "Usar o formulário original";
            original.addEventListener("click", () => {
                panel.hidden = true;
                if (originalDifficultyForm) originalDifficultyForm.style.display = "block";
                saveKikoPopReport("original-form", "Formulário original reexibido pelo usuário.");
            });

            panel.appendChild(title);
            panel.appendChild(desc);
            panel.appendChild(actions);
            panel.appendChild(status);
            panel.appendChild(prizeWrap);
            panel.appendChild(original);

            if (pageDesc && pageDesc.insertAdjacentElement) pageDesc.insertAdjacentElement("afterend", panel);
            else parent.insertBefore(panel, parent.firstChild || null);
        }

        saveKikoPopReport("ready", "Kiko Pop Fix pronto.", { reason: reason || "mount" });
    }

    function chooseKikoPopDifficulty(difficulty) {
        const validDifficulty = normalizeKikoDifficulty(difficulty);
        const label = getKikoDifficultyLabel(validDifficulty);
        setKikoPopStatus(`Selecionando dificuldade ${label}...`, "busy");
        setKikoPopButtonsDisabled(true);

        const nativeResult = tryNativeKikoSetDifficulty(validDifficulty);
        if (nativeResult.called) {
            finishKikoPopDifficultySelection(validDifficulty, label, "native-setDifficulty", nativeResult);
            return;
        }

        setKikoPopStatus(`setDifficulty original indisponível. Usando fallback oficial ${label}...`, "busy");
        kikoPopAjax("ajax/difficulty.php", { difficulty: validDifficulty }, (data) => {
            if (data && data.success) {
                finishKikoPopDifficultySelection(validDifficulty, label, "fallback-ajax-difficulty", { response: data || null, native: nativeResult });
            } else {
                setKikoPopButtonsDisabled(false);
                setKikoPopStatus("Não consegui registrar a dificuldade. Talvez você já tenha jogado hoje.", "error");
                saveKikoPopReport("difficulty-failed", "Falha ao registrar dificuldade.", { difficulty: validDifficulty, response: data || null, native: nativeResult });
            }
        }, (message) => {
            setKikoPopButtonsDisabled(false);
            setKikoPopStatus(message || "Erro ao registrar dificuldade.", "error");
            saveKikoPopReport("difficulty-error", message || "Erro ao registrar dificuldade.", { difficulty: validDifficulty, native: nativeResult });
        });
    }

    function tryNativeKikoSetDifficulty(difficulty) {
        const publicWindow = getPublicWindow();
        const nativeSetDifficulty = publicWindow?.setDifficulty || window.setDifficulty;
        if (typeof nativeSetDifficulty !== "function") {
            return { called: false, ok: false, reason: "setDifficulty-not-available" };
        }
        try {
            nativeSetDifficulty.call(publicWindow || window, difficulty);
            return { called: true, ok: true, method: "setDifficulty" };
        } catch (error) {
            return { called: false, ok: false, reason: errorToMessage(error) };
        }
    }

    function finishKikoPopDifficultySelection(difficulty, label, method, extra) {
        setKikoPopButtonsDisabled(false);
        const panel = document.getElementById(KIKO_POP_FIX_ID);
        if (panel) panel.setAttribute("data-difficulty", String(difficulty));
        const prizeWrap = panel?.querySelector(".ncc-aaa-kiko-prize-wrap");
        if (prizeWrap) prizeWrap.hidden = false;
        playKikoPopSound("soundYes");
        setKikoPopStatus(`Dificuldade ${label} registrada. Agora clique em GET PRIZE.`, "ready");
        saveKikoPopReport("difficulty-selected", `Dificuldade ${label} registrada.`, {
            difficulty,
            label,
            method,
            nativeSetDifficultyPreferred: true,
            detail: extra || null
        });
    }

    function requestKikoPopPrize() {
        const panel = document.getElementById(KIKO_POP_FIX_ID);
        const difficulty = normalizeKikoDifficulty(panel?.getAttribute("data-difficulty") || 0);
        const label = getKikoDifficultyLabel(difficulty);
        if (!difficulty) {
            setKikoPopStatus("Escolha uma dificuldade antes de pedir o prêmio.", "error");
            return;
        }

        setKikoPopStatus(`Pedindo prêmio em ${label}...`, "busy");
        const button = document.getElementById("ncc-aaa-kiko-get-prize");
        if (button) button.disabled = true;

        kikoPopAjax("ajax/prize.php", { difficulty }, (data) => {
            if (button) button.disabled = false;
            const publicWindow = getPublicWindow();
            if (data && data.success) {
                revealKikoPopPrizeResult(data, difficulty, label);
            } else {
                setKikoPopStatus("Sem prêmio desta vez ou Kiko Pop já usado hoje.", "error");
                saveKikoPopReport("no-prize", "Sem prêmio ou tentativa já usada.", { difficulty, response: data || null });
                try { publicWindow.alert("Sorry, you didn't win anything this time. :("); } catch (_) {}
            }
        }, (message) => {
            if (button) button.disabled = false;
            setKikoPopStatus(message || "Erro ao pedir o prêmio.", "error");
            saveKikoPopReport("prize-error", message || "Erro ao pedir o prêmio.", { difficulty });
        });
    }

    function revealKikoPopPrizeResult(data, difficulty, label) {
        const publicWindow = getPublicWindow();
        try { publicWindow.prize = data.prize; } catch (_) {}
        try { publicWindow.avatar = data.avatar; } catch (_) {}

        const nativePrizeButton = document.querySelector("#prizeButton");
        if (nativePrizeButton) {
            nativePrizeButton.style.display = "block";
            nativePrizeButton.style.visibility = "visible";
            nativePrizeButton.removeAttribute("hidden");
        }

        const hasNativeShowPrize = typeof publicWindow?.showPrize === "function";
        setKikoPopStatus(hasNativeShowPrize ? "Prêmio recebido. Abrindo o resultado..." : "Prêmio recebido. Use o botão Reveal Prize original.", "success");
        saveKikoPopReport("prize-ready", "Prêmio recebido pelo Kiko Pop Fix.", {
            difficulty,
            label,
            hasPrize: Boolean(data.prize),
            avatar: Boolean(data.avatar),
            nativePrizeButtonFound: Boolean(nativePrizeButton),
            nativeShowPrizeFound: hasNativeShowPrize
        });

        if (hasNativeShowPrize) {
            window.setTimeout(() => {
                try {
                    publicWindow.showPrize();
                } catch (error) {
                    setKikoPopStatus("Prêmio recebido, mas o popup não abriu. Use o botão Reveal Prize original.", "success");
                    saveKikoPopReport("show-prize-fallback", "showPrize falhou; botão original liberado.", {
                        difficulty,
                        label,
                        error: errorToMessage(error),
                        nativePrizeButtonFound: Boolean(nativePrizeButton)
                    });
                }
            }, 120);
        }
    }

    function kikoPopAjax(url, data, onSuccess, onError) {
        const publicWindow = getPublicWindow();
        const pageJquery = publicWindow?.jQuery || publicWindow?.$ || window.jQuery || window.$;
        if (!pageJquery || typeof pageJquery.ajax !== "function") {
            onError("jQuery da página não está disponível para o Kiko Pop.");
            return;
        }

        try {
            pageJquery.ajax({
                url,
                data,
                type: "post",
                dataType: "json",
                success: (response) => onSuccess(response),
                error: () => onError("A chamada oficial do Kiko Pop falhou.")
            });
        } catch (error) {
            onError(errorToMessage(error));
        }
    }

    function setKikoPopStatus(message, kind) {
        const status = document.querySelector(`#${KIKO_POP_FIX_ID} .ncc-aaa-kiko-status`);
        if (!status) return;
        status.textContent = message;
        status.setAttribute("data-kiko-status", kind || "ready");
    }

    function setKikoPopButtonsDisabled(disabled) {
        document.querySelectorAll(`#${KIKO_POP_FIX_ID} .ncc-aaa-kiko-choice`).forEach(button => {
            button.disabled = Boolean(disabled);
        });
    }

    function normalizeKikoDifficulty(value) {
        const number = Number(value);
        return number === 1 || number === 2 || number === 3 ? number : 0;
    }

    function getKikoDifficultyLabel(difficulty) {
        if (difficulty === 1) return "EASY";
        if (difficulty === 2) return "MEDIUM";
        if (difficulty === 3) return "HARD";
        return "desconhecida";
    }

    function playKikoPopSound(id) {
        try {
            const audio = document.getElementById(id);
            if (audio && typeof audio.play === "function") audio.play();
        } catch (_) {}
    }

    function saveKikoPopReport(status, message, extra) {
        lastKikoPopReport = clonePlain(Object.assign({
            schema: "ncc-aaa-kiko-pop-fix-v1",
            version: VERSION,
            source: "ncc-aaa",
            module: "kiko-pop",
            pageOnly: KIKO_POP_PATH,
            currentPage: location.pathname,
            status: status || "unknown",
            active: isKikoPopPage(),
            manualOnly: true,
            backgroundAction: false,
            usesPageJqueryAjaxAfterManualClick: true,
            fetchAdded: false,
            explicitXhrAdded: false,
            message: message || "",
            updatedAt: isoNow()
        }, extra || {}));
        return lastKikoPopReport;
    }

    function buildKikoPopReport() {
        return clonePlain(lastKikoPopReport || {
            schema: "ncc-aaa-kiko-pop-fix-v1",
            version: VERSION,
            source: "ncc-aaa",
            module: "kiko-pop",
            pageOnly: KIKO_POP_PATH,
            currentPage: location.pathname,
            status: isKikoPopPage() ? "not-mounted-yet" : "available",
            active: isKikoPopPage(),
            manualOnly: true,
            backgroundAction: false,
            usesPageJqueryAjaxAfterManualClick: true,
            fetchAdded: false,
            explicitXhrAdded: false,
            message: isKikoPopPage() ? "Aguardando montagem do Kiko Pop Fix." : "Abra a página oficial do Kiko Pop para o fix atuar.",
            updatedAt: null
        });
    }

    function ensureKikoPopFixStyle() {
        if (document.getElementById(KIKO_POP_STYLE_ID)) return;
        const style = document.createElement("style");
        style.id = KIKO_POP_STYLE_ID;
        style.textContent = `
            #${KIKO_POP_FIX_ID} {
                max-width: 640px;
                margin: 18px auto 20px;
                padding: 16px 18px;
                border: 3px solid #2f1e72;
                border-radius: 18px;
                background: rgba(255,255,255,.94);
                color: #1d1848;
                text-align: center;
                box-shadow: 0 8px 24px rgba(31,18,82,.18);
                font-family: Verdana, Arial, Helvetica, sans-serif;
                position: relative;
                z-index: 30;
            }
            #${KIKO_POP_FIX_ID} h3 {
                margin: 0 0 8px;
                font-size: 18px;
                color: #24125c;
            }
            #${KIKO_POP_FIX_ID} p {
                margin: 0 0 12px;
                color: #312957;
                line-height: 1.35;
            }
            #${KIKO_POP_FIX_ID} .ncc-aaa-kiko-actions {
                display: grid;
                grid-template-columns: repeat(3, 1fr);
                gap: 10px;
                margin: 10px 0 12px;
            }
            #${KIKO_POP_FIX_ID} button {
                border: 2px solid #372071;
                border-radius: 11px;
                background: linear-gradient(180deg, #fff8cf, #f1e3a6);
                color: #1b1642;
                cursor: pointer;
                font-weight: 900;
                min-height: 42px;
                font-family: Verdana, Arial, Helvetica, sans-serif;
            }
            #${KIKO_POP_FIX_ID} button:hover:not(:disabled) {
                filter: brightness(1.04);
                transform: translateY(-1px);
            }
            #${KIKO_POP_FIX_ID} button:disabled {
                opacity: .6;
                cursor: wait;
            }
            #${KIKO_POP_FIX_ID} .ncc-aaa-kiko-status {
                margin: 10px auto;
                padding: 9px 10px;
                border: 1px solid #d7c67a;
                border-radius: 10px;
                background: #fff9dd;
                font-weight: 800;
            }
            #${KIKO_POP_FIX_ID} .ncc-aaa-kiko-status[data-kiko-status="success"] { background: #e8fff0; border-color: #62bf80; }
            #${KIKO_POP_FIX_ID} .ncc-aaa-kiko-status[data-kiko-status="error"] { background: #fff0f0; border-color: #d46a6a; }
            #${KIKO_POP_FIX_ID} .ncc-aaa-kiko-status[data-kiko-status="busy"] { background: #eef3ff; border-color: #6d82cc; }
            #${KIKO_POP_FIX_ID} .ncc-aaa-kiko-prize-wrap {
                margin: 10px 0;
            }
            #${KIKO_POP_FIX_ID} .ncc-aaa-kiko-prize {
                min-width: 190px;
                background: linear-gradient(180deg, #28aa50, #177532);
                border-color: #0d5e25;
                color: #fff;
            }
            #${KIKO_POP_FIX_ID} .ncc-aaa-kiko-original {
                border: 0;
                background: transparent;
                min-height: auto;
                padding: 4px 6px;
                color: #2f1e72;
                text-decoration: underline;
                font-size: 11px;
            }
            @media (max-width: 620px) {
                #${KIKO_POP_FIX_ID} .ncc-aaa-kiko-actions { grid-template-columns: 1fr; }
            }
        `;
        document.head.appendChild(style);
    }

    function detectIdentity() {
        const now = isoNow();
        const candidates = collectIdentityCandidates();
        const chosen = chooseBestIdentityCandidate(candidates);
        const username = chosen.username || "Neopiano";
        const isFallback = Boolean(chosen.isFallback || !chosen.username || username === "Neopiano");
        const playerId = isFallback ? getStableLocalGuestId() : normalizePlayerId(username);
        const identityRecord = Object.freeze({
            schema: IDENTITY_SCHEMA,
            username,
            playerId,
            source: chosen.source || "fallback-neopiano",
            confidence: chosen.confidence || 0,
            isFallback,
            detectedAt: now,
            candidates: candidates.map(candidate => ({
                username: candidate.username,
                source: candidate.source,
                confidence: candidate.confidence,
                isFallback: Boolean(candidate.isFallback)
            }))
        });

        writeText(PLAYER_USERNAME_KEY, username);
        writeText(PLAYER_ID_KEY, playerId);
        writeJson(IDENTITY_KEY, identityRecord);

        return identityRecord;
    }

    function collectIdentityCandidates() {
        const candidates = [];
        addIdentityCandidate(candidates, getHeaderUserLinkCandidate());
        addIdentityCandidate(candidates, getHeaderWelcomeTextCandidate());
        addIdentityCandidate(candidates, getAppInsightsVariableCandidate());
        addIdentityCandidate(candidates, getScriptAppInsightsCandidate());
        addIdentityCandidate(candidates, getStoredIdentityCandidate());
        addIdentityCandidate(candidates, {
            username: "Neopiano",
            source: "fallback-neopiano",
            confidence: 1,
            isFallback: true
        });
        return candidates;
    }

    function addIdentityCandidate(candidates, candidate) {
        if (!candidate || !candidate.username) return;
        const username = normalizeUsername(candidate.username);
        if (!username) return;
        candidates.push({
            username,
            source: String(candidate.source || "unknown"),
            confidence: Number(candidate.confidence || 0),
            isFallback: Boolean(candidate.isFallback)
        });
    }

    function chooseBestIdentityCandidate(candidates) {
        const sorted = candidates.slice().sort((left, right) => {
            const confidenceDiff = Number(right.confidence || 0) - Number(left.confidence || 0);
            if (confidenceDiff) return confidenceDiff;
            return Number(Boolean(left.isFallback)) - Number(Boolean(right.isFallback));
        });
        return sorted[0] || { username: "Neopiano", source: "fallback-neopiano", confidence: 1, isFallback: true };
    }

    function getHeaderUserLinkCandidate() {
        const scopedSelectors = [
            '#header td.user a[href*="/userlookup.phtml?user="]',
            '#header .user a[href*="/userlookup.phtml?user="]',
            'td.user a[href*="/userlookup.phtml?user="]',
            'a[href*="/userlookup.phtml?user="]'
        ];
        for (const selector of scopedSelectors) {
            const link = document.querySelector(selector);
            if (!link) continue;
            const href = link.getAttribute("href") || "";
            const queryUser = extractQueryParam(href, "user");
            const username = normalizeUsername(queryUser || link.textContent || "");
            if (username) {
                return {
                    username,
                    source: queryUser ? "header-userlookup-param" : "header-userlookup-text",
                    confidence: selector.includes("#header") || selector.includes("td.user") ? 100 : 65,
                    isFallback: false
                };
            }
        }
        return null;
    }

    function getHeaderWelcomeTextCandidate() {
        const header = document.querySelector("#header td.user, #header .user, td.user");
        const text = normalizeWhitespace(header?.textContent || "");
        const match = text.match(/Welcome,\s*([a-zA-Z0-9_.-]+)/i);
        if (!match || !match[1]) return null;
        return {
            username: match[1],
            source: "header-welcome-text",
            confidence: 88,
            isFallback: false
        };
    }

    function getAppInsightsVariableCandidate() {
        try {
            const value = window.appInsightsUserName || getPublicWindow()?.appInsightsUserName;
            if (!value) return null;
            return {
                username: value,
                source: "window-appInsightsUserName",
                confidence: 82,
                isFallback: false
            };
        } catch (_) {
            return null;
        }
    }

    function getScriptAppInsightsCandidate() {
        const scripts = Array.from(document.scripts || []);
        for (const script of scripts) {
            const match = String(script.textContent || "").match(/appInsightsUserName\s*=\s*['\"]([^'\"]+)['\"]/);
            if (match && match[1]) {
                return {
                    username: match[1],
                    source: "script-appInsightsUserName",
                    confidence: 78,
                    isFallback: false
                };
            }
        }
        return null;
    }

    function getStoredIdentityCandidate() {
        const storedIdentity = readJson(IDENTITY_KEY, null);
        if (storedIdentity?.username && storedIdentity?.playerId) {
            return {
                username: storedIdentity.username,
                source: "stored-identity-record",
                confidence: 45,
                isFallback: Boolean(storedIdentity.isFallback)
            };
        }

        const storedUsername = readText(PLAYER_USERNAME_KEY, "");
        if (storedUsername) {
            return {
                username: storedUsername,
                source: "stored-username",
                confidence: 38,
                isFallback: false
            };
        }
        return null;
    }

    function getStableLocalGuestId() {
        const existing = normalizeToken(readText(LOCAL_GUEST_ID_KEY, ""));
        if (existing) return existing;
        const generated = `guest-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
        writeText(LOCAL_GUEST_ID_KEY, generated);
        return generated;
    }

    function stateKeyForPlayer(playerId) {
        return `${STATE_KEY_PREFIX}.${normalizePlayerId(playerId) || normalizeToken(playerId) || "local"}`;
    }

    function visualPreferencesKeyForPlayer(playerId) {
        return `${VISUAL_PREFS_KEY_PREFIX}.${normalizePlayerId(playerId) || normalizeToken(playerId) || "local"}`;
    }

    function getRequestedViewFromLocation() {
        if (String(location.pathname || "") !== MOUNT_PATH) return "";
        try {
            const params = new URLSearchParams(String(location.search || ""));
            const fromParam = normalizeViewId(params.get("nccaaa_view") || params.get("ncc_aaa_view") || params.get("view") || "");
            if (fromParam) return fromParam;
            const hash = String(location.hash || "").replace(/^#/, "");
            const match = hash.match(/(?:^|[&?])(?:nccaaa_view|ncc_aaa_view|view)=([^&]+)/i);
            return normalizeViewId(match ? decodeURIComponent(match[1]) : hash);
        } catch (_) {
            return "";
        }
    }

    function loadStateForIdentity(currentIdentity) {
        const now = isoNow();
        storageKey = stateKeyForPlayer(currentIdentity.playerId);
        visualPreferencesKey = visualPreferencesKeyForPlayer(currentIdentity.playerId);

        const exactRaw = readJson(storageKey, null);
        const latestRaw = readJson(LATEST_STATE_KEY, null);
        const visualPrefsRaw = readJson(visualPreferencesKey, null);
        const canUseLatest = isObject(latestRaw) && latestRaw.playerId === currentIdentity.playerId;
        const raw = isObject(exactRaw) ? exactRaw : (canUseLatest ? latestRaw : {});
        const visualPrefs = normalizeVisualPreferences(visualPrefsRaw);
        const loadedState = hydrateStateForIdentity({ ...(isObject(raw) ? raw : {}), ...visualPrefs }, currentIdentity, now, "load");

        saveState(loadedState, "load-state", { immediateSecondary: true });
        return loadedState;
    }

    function createDefaultState(currentIdentity, now) {
        return {
            schema: STATE_SCHEMA,
            version: VERSION,
            playerId: currentIdentity.playerId,
            username: currentIdentity.username,
            identitySource: currentIdentity.source,
            identityConfidence: currentIdentity.confidence,
            identityIsFallback: currentIdentity.isFallback,
            view: FALLBACK_VIEW_ID,
            avatarId: FALLBACK_AVATAR_ID,
            teamId: FALLBACK_TEAM_ID,
            themeId: FALLBACK_THEME_ID,
            level: 1,
            xp: 0,
            xpPolicy: "pending",
            storagePolicy: "per-user-isolated-visual-preferences",
            modules: {},
            revision: 0,
            sessionId: SESSION_ID,
            lastCommitReason: "default",
            createdAt: now,
            updatedAt: now
        };
    }

    function hydrateStateForIdentity(rawState, currentIdentity, now, reason) {
        const fallback = createDefaultState(currentIdentity, now);
        const raw = isObject(rawState) ? rawState : {};
        const merged = {
            ...fallback,
            ...raw,
            schema: STATE_SCHEMA,
            version: VERSION,
            playerId: currentIdentity.playerId,
            username: currentIdentity.username,
            identitySource: currentIdentity.source,
            identityConfidence: currentIdentity.confidence,
            identityIsFallback: currentIdentity.isFallback,
            storageKey,
            visualPreferencesKey,
            updatedAt: now
        };

        merged.view = getRequestedViewFromLocation() || normalizeViewId(merged.view) || FALLBACK_VIEW_ID;
        merged.avatarId = normalizeAvatarId(merged.avatarId) || FALLBACK_AVATAR_ID;
        merged.teamId = normalizeTeamId(merged.teamId) || FALLBACK_TEAM_ID;
        merged.themeId = normalizeThemeId(merged.themeId) || FALLBACK_THEME_ID;
        merged.level = 1;
        merged.xp = 0;
        merged.xpPolicy = "pending";
        merged.modules = {};
        merged.storagePolicy = "per-user-isolated-visual-preferences";
        merged.revision = normalizeRevision(merged.revision);
        merged.sessionId = String(merged.sessionId || SESSION_ID);
        merged.lastCommitReason = String(reason || merged.lastCommitReason || "load");
        return clonePlain(merged);
    }

    function saveState(nextState, reason, options) {
        const writeReason = reason || "save-state";
        const safeState = clonePlain({
            ...nextState,
            schema: STATE_SCHEMA,
            version: VERSION,
            sessionId: nextState?.sessionId || SESSION_ID,
            revision: normalizeRevision(nextState?.revision),
            updatedAt: nextState?.updatedAt || isoNow()
        });
        writeJson(storageKey, safeState);
        writeVisualPreferences(safeState, writeReason);

        const auditExtra = {
            storageKey,
            visualPreferencesKey,
            revision: safeState.revision,
            sessionId: safeState.sessionId,
            debouncedSecondary: !Boolean(options?.immediateSecondary)
        };

        if (options?.immediateSecondary) {
            writeJson(LATEST_STATE_KEY, safeState);
            writeStorageManifest(writeReason);
            writeStorageAudit(writeReason, auditExtra);
        } else {
            scheduleWriteJson(LATEST_STATE_KEY, safeState, SECONDARY_STORAGE_WRITE_DELAY_MS);
            scheduleWriteJson(STORAGE_MANIFEST_KEY, buildStorageManifest(writeReason), SECONDARY_STORAGE_WRITE_DELAY_MS);
            scheduleWriteJson(STORAGE_AUDIT_KEY, buildStorageAudit(writeReason, auditExtra), SECONDARY_STORAGE_WRITE_DELAY_MS);
        }
        return safeState;
    }

    function normalizeVisualPreferences(raw) {
        if (!isObject(raw)) return {};
        return {
            view: normalizeViewId(raw.view) || "",
            avatarId: normalizeAvatarId(raw.avatarId) || "",
            teamId: normalizeTeamId(raw.teamId) || "",
            themeId: normalizeThemeId(raw.themeId) || ""
        };
    }

    function buildVisualPreferencesRecord(nextState, reason) {
        return clonePlain({
            schema: "ncc-aaa-visual-preferences-v1",
            version: VERSION,
            reason: reason || "unknown",
            playerId: nextState?.playerId || identity?.playerId || "local",
            username: nextState?.username || identity?.username || "Neopiano",
            view: normalizeViewId(nextState?.view) || FALLBACK_VIEW_ID,
            avatarId: normalizeAvatarId(nextState?.avatarId) || FALLBACK_AVATAR_ID,
            teamId: normalizeTeamId(nextState?.teamId) || FALLBACK_TEAM_ID,
            themeId: normalizeThemeId(nextState?.themeId) || FALLBACK_THEME_ID,
            updatedAt: isoNow()
        });
    }

    function writeVisualPreferences(nextState, reason) {
        if (!visualPreferencesKey) return false;
        return writeJson(visualPreferencesKey, buildVisualPreferencesRecord(nextState, reason));
    }

    function resetVisualPreferences() {
        commitState("reset-visual", previous => ({
            ...previous,
            avatarId: FALLBACK_AVATAR_ID,
            teamId: FALLBACK_TEAM_ID,
            themeId: FALLBACK_THEME_ID,
            view: normalizeViewId(previous.view) || FALLBACK_VIEW_ID
        }));
    }

    function writeStorageManifest(reason) {
        const manifest = buildStorageManifest(reason);
        writeJson(STORAGE_MANIFEST_KEY, manifest);
        return manifest;
    }

    function buildStorageManifest(reason) {
        return clonePlain({
            schema: "ncc-aaa-storage-manifest-v1",
            version: VERSION,
            reason: reason || "unknown",
            updatedAt: isoNow(),
            namespace: "ncc.aaa",
            owner: "NCC AAA",
            isolation: "per-user",
            currentPlayerId: identity?.playerId || "",
            currentUsername: identity?.username || "",
            keys: {
                identity: IDENTITY_KEY,
                localGuestId: LOCAL_GUEST_ID_KEY,
                playerId: PLAYER_ID_KEY,
                username: PLAYER_USERNAME_KEY,
                latestState: LATEST_STATE_KEY,
                state: storageKey || "",
                visualPreferences: visualPreferencesKey || "",
                externalState: EXTERNAL_STATE_KEY,
                externalStateEvent: EXTERNAL_STATE_EVENT,
                stateEvent: STATE_EVENT,
                globalBridge: GLOBAL_BRIDGE_NAME,
                globalExternalState: GLOBAL_EXTERNAL_STATE_NAME,
                storageManifest: STORAGE_MANIFEST_KEY,
                storageAudit: STORAGE_AUDIT_KEY,
                lastDiagnostic: LAST_DIAGNOSTIC_KEY,
                lastHealthReport: LAST_HEALTH_REPORT_KEY,
                lastSelfTest: LAST_SELF_TEST_KEY,
                baseEntryContract: BASE_ENTRY_CONTRACT_KEY,
                baseEntryReadiness: BASE_ENTRY_READINESS_KEY,
                baseEntryEvent: BASE_ENTRY_EVENT,
                globalBaseEntryContract: GLOBAL_BASE_ENTRY_CONTRACT_NAME,
                globalBaseEntryReadiness: GLOBAL_BASE_ENTRY_READINESS_NAME,
                trainingExternalState: TRAINING_EXTERNAL_STATE_KEY,
                trainingExternalStateEvent: TRAINING_EXTERNAL_STATE_EVENT,
                globalTrainingState: GLOBAL_TRAINING_STATE_NAME,
                battledomeExternalState: BATTLEDOME_EXTERNAL_STATE_KEY,
                battledomeExternalStateEvent: BATTLEDOME_EXTERNAL_STATE_EVENT,
                globalBattledomeState: GLOBAL_BATTLEDOME_STATE_NAME,
                battledomeState: BATTLEDOME_STORAGE_KEY,
                battledomeReport: BATTLEDOME_REPORT_KEY,
                trophyTrackerStatePrefix: TROPHY_TRACKER_STORAGE_KEY_PREFIX,
                trophyTrackerReport: TROPHY_TRACKER_REPORT_KEY,
                tyranuEvavuCards: TYRANU_EVAVU_STORAGE_CARDS_KEY,
                tyranuEvavuReport: TYRANU_EVAVU_REPORT_KEY,
                kissTheMortogTarget: KISS_THE_MORTOG_TARGET_KEY,
                kissTheMortogReport: KISS_THE_MORTOG_REPORT_KEY,
                godoriReport: GODORI_REPORT_KEY
            },
            safety: {
                ownsOnlyNccAaaNamespace: true,
                touchesNccBaseKeys: false,
                touchesNccRpgKeys: false,
                destructiveClearAll: false,
                visualResetOverwritesOwnVisualDefaultsOnly: true,
                cssCustomPropertiesSanitized: true,
                themeToneHexOnly: true
            }
        });
    }

    function buildStorageAudit(reason, extra) {
        return clonePlain({
            schema: "ncc-aaa-storage-audit-v1",
            version: VERSION,
            reason: reason || "unknown",
            updatedAt: isoNow(),
            playerId: identity?.playerId || "",
            username: identity?.username || "",
            storageKey: storageKey || "",
            visualPreferencesKey: visualPreferencesKey || "",
            revision: normalizeRevision(state?.revision),
            sessionId: SESSION_ID,
            lastStorageSyncAt,
            extra: clonePlain(extra || {})
        });
    }

    function writeStorageAudit(reason, extra) {
        const audit = buildStorageAudit(reason, extra);
        writeJson(STORAGE_AUDIT_KEY, audit);
        return audit;
    }

    function buildStorageReport() {
        return clonePlain({
            schema: "ncc-aaa-storage-report-v1",
            version: VERSION,
            at: isoNow(),
            available: storageAvailable(),
            manifest: buildStorageManifest("report"),
            currentStateKey: storageKey || "",
            visualPreferencesKey: visualPreferencesKey || "",
            externalStateKey: EXTERNAL_STATE_KEY,
            visualPreferences: readJson(visualPreferencesKey, null),
            latestAudit: readJson(STORAGE_AUDIT_KEY, null),
            baseEntryContract: readJson(BASE_ENTRY_CONTRACT_KEY, null),
            baseEntryReadiness: readJson(BASE_ENTRY_READINESS_KEY, null)
        });
    }

    function buildBaseEntryContract(reason) {
        return clonePlain({
            schema: "ncc-aaa-base-entry-contract-v1",
            version: VERSION,
            reason: reason || "unknown",
            updatedAt: isoNow(),
            source: "ncc-aaa",
            sourceScript: "NCC AAA",
            sourceModule: "foundation",
            targetConsumer: "NCC Base",
            targetBaseCategory: "Jogos/Arena",
            mountPage: MOUNT_PATH,
            legacyMountPage: LEGACY_MOUNT_PATH,
            entryPoint: ENTRY_POINT,
            legacyEntryPoint: LEGACY_ENTRY_POINT,
            display: {
                cardTitle: "NCC AAA · Jogos",
                cardSubtitle: "Central auxiliar pronta para receber a lógica da categoria Jogos.",
                cardStatus: `${GAME_MODULE_REGISTRY.length} módulos de jogos ativos · Arena e Trophy Tracker no NCC AAA`,
                actionLabel: "Abrir NCC AAA",
                href: ENTRY_POINT,
                badge: "External Mirror",
                tone: "active-helper"
            },
            externalRead: {
                storageKey: EXTERNAL_STATE_KEY,
                schema: EXTERNAL_STATE_SCHEMA,
                globalStateName: GLOBAL_EXTERNAL_STATE_NAME,
                bridgeName: GLOBAL_BRIDGE_NAME,
                events: [EXTERNAL_STATE_EVENT, STATE_EVENT, BASE_ENTRY_EVENT],
                requiredFields: [
                    "schema",
                    "source",
                    "sourceModule",
                    "version",
                    "updatedAt",
                    "status",
                    "ready",
                    "active",
                    "mirrorOnly",
                    "entryPoint",
                    "summary",
                    "safety"
                ],
                acceptedSource: "ncc-aaa",
                acceptedSourceModule: "foundation",
                acceptedStatus: ["ready-cellblock-helper"],
                freshness: {
                    maxAgeMs: EXTERNAL_STATE_MAX_AGE_MS,
                    label: "7d"
                }
            },
            baseBehavior: {
                mirrorOnly: false,
                shouldExecuteGameLogic: false,
                shouldCallBridgeForActions: false,
                shouldSubmitForms: false,
                shouldClickOfficialButtons: false,
                shouldSimulateKeyboard: false,
                suggestedBaseAction: "show-card-and-open-entry-point",
                suggestedPlacement: "categoria Jogos",
                optionalOnly: true
            },
            currentPayload: {
                moduleCount: GAME_MODULE_REGISTRY.length,
                alertCount: 0,
                capabilities: ["cellblock-visual-helper", "kiko-pop-manual-fix", "tyranu-evavu-toggle-autoplayer", "neoquest1-guide-helper", "neoquest2-guide-helper"],
                modules: GAME_MODULE_REGISTRY.map(module => clonePlain(module)),
                alerts: []
            },
            safety: clonePlain(COMMUNICATION_CONTRACT.safety)
        });
    }

    function writeBaseEntryContract(reason) {
        const contract = buildBaseEntryContract(reason || "write");
        writeJson(BASE_ENTRY_CONTRACT_KEY, contract);
        publishBaseEntryContractToGlobals(contract);
        return contract;
    }

    function buildBaseEntryReadiness(reason) {
        const external = readJson(EXTERNAL_STATE_KEY, null);
        const contract = buildBaseEntryContract(reason || "readiness");
        const checks = [
            { id: "entry-point", ok: contract.entryPoint === ENTRY_POINT, detail: ENTRY_POINT },
            { id: "external-state-key", ok: contract.externalRead.storageKey === EXTERNAL_STATE_KEY, detail: EXTERNAL_STATE_KEY },
            { id: "external-state-schema", ok: contract.externalRead.schema === EXTERNAL_STATE_SCHEMA, detail: EXTERNAL_STATE_SCHEMA },
            { id: "mirror-only", ok: contract.baseBehavior.mirrorOnly === true, detail: "Base deve apenas ler e abrir link" },
            { id: "minimal-game-modules", ok: contract.currentPayload.moduleCount === GAME_MODULE_REGISTRY.length, detail: `${GAME_MODULE_REGISTRY.length} módulos de jogos ativos` },
            { id: "safe-actions", ok: !contract.baseBehavior.shouldSubmitForms && !contract.baseBehavior.shouldClickOfficialButtons && !contract.baseBehavior.shouldSimulateKeyboard, detail: "sem ação oficial" },
            { id: "external-state-present", ok: Boolean(external), detail: external?.schema || "será publicado no boot" }
        ];
        const failed = checks.filter(check => !check.ok);
        return clonePlain({
            schema: "ncc-aaa-base-entry-readiness-v1",
            version: VERSION,
            reason: reason || "unknown",
            updatedAt: isoNow(),
            readyForBaseCard: failed.filter(check => check.id !== "external-state-present").length === 0,
            readyForBaseMirror: failed.length === 0,
            entryPoint: ENTRY_POINT,
            externalStateKey: EXTERNAL_STATE_KEY,
            baseEntryContractKey: BASE_ENTRY_CONTRACT_KEY,
            recommendedBaseCard: clonePlain(contract.display),
            checks,
            failed,
            notes: [
                "A Fase 7 não altera o NCC Base.",
                "O NCC Base pode futuramente criar apenas um card/link para /~Yillcivi na categoria Jogos.",
                "O NCC Base deve tratar o NCC AAA como External Mirror e nunca pedir ação oficial nesta fundação."
            ]
        });
    }

    function writeBaseEntryReadiness(reason) {
        const readiness = buildBaseEntryReadiness(reason || "write");
        writeJson(BASE_ENTRY_READINESS_KEY, readiness);
        publishBaseEntryReadinessToGlobals(readiness);
        dispatchBaseEntryReady(readiness);
        return readiness;
    }

    function publishBaseEntryContractToGlobals(contract) {
        const publicWindow = getPublicWindow();
        try { window[GLOBAL_BASE_ENTRY_CONTRACT_NAME] = contract; } catch (_) {}
        if (publicWindow && publicWindow !== window) {
            try { publicWindow[GLOBAL_BASE_ENTRY_CONTRACT_NAME] = contract; } catch (_) {}
        }
    }

    function publishBaseEntryReadinessToGlobals(readiness) {
        const publicWindow = getPublicWindow();
        try { window[GLOBAL_BASE_ENTRY_READINESS_NAME] = readiness; } catch (_) {}
        if (publicWindow && publicWindow !== window) {
            try { publicWindow[GLOBAL_BASE_ENTRY_READINESS_NAME] = readiness; } catch (_) {}
        }
    }

    function dispatchBaseEntryReady(readiness) {
        dispatchStateEvent(window, BASE_ENTRY_EVENT, readiness);
        dispatchStateEvent(document, BASE_ENTRY_EVENT, readiness);
        const publicWindow = getPublicWindow();
        if (publicWindow && publicWindow !== window) {
            dispatchStateEvent(publicWindow, BASE_ENTRY_EVENT, readiness);
        }
    }



    function prepareFullWidthMount(target) {
        if (!target || !isMountPage()) return;
        try {
            document.documentElement.setAttribute("data-ncc-aaa-fullwidth", "true");
            if (document.body) document.body.classList.add("ncc-aaa-fullwidth-page");

            target.classList.add("ncc-aaa-fullwidth-content");
            target.setAttribute("data-ncc-aaa-fullwidth-content", "true");

            const row = target.closest("tr");
            if (row) row.setAttribute("data-ncc-aaa-content-row", "true");

            const table = target.closest("table");
            if (table) table.setAttribute("data-ncc-aaa-content-table", "true");

            const content = document.querySelector("#content");
            if (content) content.setAttribute("data-ncc-aaa-fullwidth-zone", "true");

            const main = document.querySelector("#main");
            if (main) main.setAttribute("data-ncc-aaa-fullwidth-main", "true");

            const sidebar = row ? row.querySelector("td.sidebar, .sidebar") : document.querySelector("#content td.sidebar, #content .sidebar");
            if (sidebar && sidebar !== target) {
                sidebar.setAttribute("data-ncc-aaa-hidden-sidebar", "true");
                sidebar.setAttribute("aria-hidden", "true");
            }

            if (typeof target.colSpan === "number" && target.colSpan < 2) {
                target.colSpan = 2;
            }
        } catch (error) {
            const message = "Full-width mount fix falhou: " + errorToMessage(error);
            bootErrors.push(message);
            console.warn("[NCC AAA]", message, error);
        }
    }

    function mount() {
        try {
            injectStyles();
            const target = findMountTarget();
            if (!target) {
                lastRenderError = "Alvo visual do NCC AAA não encontrado na página principal/fallback.";
                publishExternalState("mount-target-missing");
                return;
            }

            prepareFullWidthMount(target);

            mountedTarget = target;
            target.setAttribute("data-ncc-aaa-mounted", "true");
            target.innerHTML = `<div id="${APP_ID}" class="ncc-aaa"></div>`;
            root = target.querySelector(`#${APP_ID}`);

            if (!root) {
                lastRenderError = "Root visual não foi criado.";
                publishExternalState("root-missing");
                return;
            }

            root.addEventListener("click", handleClick);
            root.addEventListener("change", handleChange);
            root.addEventListener("input", handleInput);
            markBoot();
            updateDocumentTitle();
            render({ reason: "mounted", forceFull: true });
            publishExternalState("mounted");
        } catch (error) {
            lastRenderError = errorToMessage(error);
            bootErrors.push(lastRenderError);
            publishExternalState("mount-error");
            console.warn("[NCC AAA] Falha na montagem visual.", error);
        }
    }

    function findMountTarget() {
        if (isPrimaryMountPage() && document.body) return document.body;
        const selectors = [
            "#content td.content",
            "td.content",
            "td[class~='content']",
            "#content table tr > td:not(.sidebar):last-child"
        ];
        for (const selector of selectors) {
            const found = document.querySelector(selector);
            if (found) return found;
        }
        if (isMountPage() && document.body) return document.body;
        return null;
    }

    function markBoot() {
        if (document.getElementById(MARKER_ID)) return;
        const marker = document.createElement("meta");
        marker.id = MARKER_ID;
        marker.name = "ncc-aaa-version";
        marker.content = VERSION;
        document.head.appendChild(marker);
    }

    function updateDocumentTitle() {
        if (!document.title.includes("NCC AAA")) {
            document.title = `NCC AAA - ${document.title || "Neopets"}`;
        }
    }

    function render(context) {
        if (!root) return false;
        const renderContext = isObject(context) ? context : {};
        const reason = renderContext.reason || "manual-render";
        try {
            lastRenderError = "";
            const selectedAvatar = getAvatar(state.avatarId);
            const selectedTeam = getTeam(state.teamId);
            const selectedTheme = getTheme(state.themeId);
            const tone = getThemeTone(state.themeId);
            const viewportSnapshot = captureRenderSnapshot();

            if (shouldUsePartialRender(reason, renderContext) && renderActiveViewPartial(selectedAvatar, selectedTeam, selectedTheme)) {
                lastRenderMode = "partial";
                lastRenderReason = reason;
                lastPartialRenderAt = isoNow();
                partialRenderCount += 1;
                restoreRenderSnapshot(viewportSnapshot);
                return true;
            }

            root.innerHTML = shellMarkup({ selectedAvatar, selectedTeam, selectedTheme, tone });
            lastRenderMode = "full";
            lastRenderReason = reason;
            lastFullRenderAt = isoNow();
            fullRenderCount += 1;
            restoreRenderSnapshot(viewportSnapshot);
            return true;
        } catch (error) {
            lastRenderError = errorToMessage(error);
            lastRenderMode = "recovery";
            lastRenderReason = reason;
            root.innerHTML = recoveryMarkup(lastRenderError);
            publishExternalState("render-error");
            console.warn("[NCC AAA] Falha no render.", error);
            return false;
        }
    }

    function shouldUsePartialRender(reason, context) {
        if (!root || !root.querySelector(".ncc-aaa-shell")) return false;
        if (!root.querySelector(".ncc-aaa-tabs") || !root.querySelector(".ncc-aaa-view")) return false;
        if (context && context.forceFull) return false;
        return reason === "switch-view";
    }

    function renderActiveViewPartial(selectedAvatar, selectedTeam, selectedTheme) {
        const tabs = root.querySelector(".ncc-aaa-tabs");
        const view = root.querySelector(".ncc-aaa-view");
        if (!tabs || !view) return false;
        tabs.innerHTML = tabsMarkup();
        view.innerHTML = currentViewMarkup(selectedAvatar, selectedTeam, selectedTheme);
        view.setAttribute("data-ncc-aaa-render-mode", "partial");
        view.setAttribute("data-ncc-aaa-rendered-at", isoNow());
        return true;
    }

    function tabsMarkup() {
        return VIEWS.map(view => `
                        <button class="ncc-aaa-tab${state.view === view.id ? " is-active" : ""}" type="button" data-action="switch-view" data-view-id="${escapeAttribute(view.id)}" aria-pressed="${state.view === view.id ? "true" : "false"}">
                            <span class="ncc-aaa-tab-icon" aria-hidden="true">${escapeHtml(view.icon)}</span>
                            <span class="ncc-aaa-tab-label">${escapeHtml(view.label)}</span>
                        </button>
                    `).join("");
    }

    function captureRenderSnapshot() {
        const active = document.activeElement;
        const activeAction = active && root && root.contains(active) ? active.getAttribute("data-action") : "";
        const activeViewId = active && root && root.contains(active) ? active.getAttribute("data-view-id") : "";
        const activeValue = active && root && root.contains(active) ? active.value : "";
        const view = root ? root.querySelector(".ncc-aaa-view") : null;
        return {
            windowX: window.scrollX || 0,
            windowY: window.scrollY || 0,
            viewScrollTop: view ? view.scrollTop : 0,
            viewScrollLeft: view ? view.scrollLeft : 0,
            activeAction,
            activeViewId,
            activeValue
        };
    }

    function restoreRenderSnapshot(snapshot) {
        if (!snapshot || !root) return;
        try {
            const view = root.querySelector(".ncc-aaa-view");
            if (view) {
                view.scrollTop = snapshot.viewScrollTop || 0;
                view.scrollLeft = snapshot.viewScrollLeft || 0;
            }
            if (snapshot.activeAction) {
                const actionSelector = `[data-action="${escapeCssAttribute(snapshot.activeAction)}"]`;
                const viewSelector = snapshot.activeViewId ? `[data-view-id="${escapeCssAttribute(snapshot.activeViewId)}"]` : "";
                const valueSelector = snapshot.activeValue ? `[value="${escapeCssAttribute(snapshot.activeValue)}"]` : "";
                const target = root.querySelector(`${actionSelector}${viewSelector}`) || root.querySelector(`${actionSelector}${valueSelector}`) || root.querySelector(actionSelector);
                if (target && typeof target.focus === "function") target.focus({ preventScroll: true });
            }
            if (typeof window.scrollTo === "function") window.scrollTo(snapshot.windowX || 0, snapshot.windowY || 0);
        } catch (_) {}
    }

    function escapeCssAttribute(value) {
        return String(value || "").replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\]/g, "\\]");
    }

    function buildRenderReport() {
        return clonePlain({
            schema: "ncc-aaa-render-report-v1",
            version: VERSION,
            at: isoNow(),
            lastRenderMode,
            lastRenderReason,
            lastRenderError: lastRenderError || "",
            lastFullRenderAt,
            lastPartialRenderAt,
            fullRenderCount,
            partialRenderCount,
            partialRenderEnabled: true,
            partialReasons: ["switch-view"],
            fullRenderFallback: true,
            preservesWindowScroll: true,
            preservesFocusedAction: true,
            shellMounted: Boolean(root?.querySelector(".ncc-aaa-shell")),
            activeView: state?.view || FALLBACK_VIEW_ID
        });
    }

    function buildCoreOrganizationReport() {
        return clonePlain({
            ...CORE_ORGANIZATION,
            at: isoNow(),
            sectionIds: CORE_AREAS.map(section => section.id),
            gameModuleRegistry: {
                ready: true,
                total: GAME_MODULE_REGISTRY.length,
                modules: GAME_MODULE_REGISTRY.map(module => clonePlain(module)),
                realModules: true,
                migrationStatus: GAME_MODULE_REGISTRY.length ? "has-placeholders" : "empty-ready"
            },
            arenaModuleRegistry: {
                ready: true,
                total: ARENA_MODULE_REGISTRY.length,
                modules: ARENA_MODULE_REGISTRY.map(module => clonePlain(module))
            },
            trophyTracker: {
                ready: true,
                sourceName: TROPHY_TRACKER_SOURCE_NAME,
                sourceVersion: TROPHY_TRACKER_SOURCE_VERSION,
                sourceLicense: TROPHY_TRACKER_SOURCE_LICENSE,
                sourceHomepage: TROPHY_TRACKER_SOURCE_HOMEPAGE,
                reportKey: TROPHY_TRACKER_REPORT_KEY,
                entryUrl: TROPHY_TRACKER_ENTRY_URL
            },
            tyranuEvavu: {
                ready: true,
                pageOnly: TYRANU_EVAVU_PATH,
                href: TYRANU_EVAVU_URL,
                defaultEnabled: false,
                sessionOnlyToggle: true,
                storageKeys: {
                    enabledSession: TYRANU_EVAVU_SESSION_ENABLED_KEY,
                    cards: TYRANU_EVAVU_STORAGE_CARDS_KEY,
                    report: TYRANU_EVAVU_REPORT_KEY
                }
            },
            kissTheMortog: {
                ready: true,
                pageOnly: KISS_THE_MORTOG_PLAY_PATH,
                processPage: KISS_THE_MORTOG_PROCESS_PATH,
                href: KISS_THE_MORTOG_URL,
                defaultEnabled: false,
                sessionOnlyToggle: true,
                maxSafeScore: KISS_THE_MORTOG_MAX_SAFE_SCORE,
                storageKeys: {
                    enabledSession: KISS_THE_MORTOG_SESSION_PLAYING_KEY,
                    target: KISS_THE_MORTOG_TARGET_KEY,
                    report: KISS_THE_MORTOG_REPORT_KEY
                }
            },
            godori: {
                ready: true,
                pageOnly: GODORI_PATH_PREFIX,
                href: GODORI_URL,
                defaultEnabled: false,
                sessionOnlyToggle: true,
                avatarTargetHands: 250,
                storageKeys: {
                    enabledSession: GODORI_SESSION_ENABLED_KEY,
                    autoPlayAgain: GODORI_AUTO_PLAY_AGAIN_KEY,
                    autoStop250: GODORI_AUTO_STOP_250_KEY,
                    report: GODORI_REPORT_KEY
                }
            },
            publicBridgeMethod: "getCoreOrganizationReport",
            storageNamespace: "ncc.aaa",
            safeToExposeToBase: true
        });
    }

    function heroQuickStatsMarkup() {
        let report = null;
        try {
            report = buildTrophyTrackerReport("hero-quick-stats");
        } catch (_) {
            report = null;
        }
        const totals = report?.analysis?.totals || {};
        const hasScan = Boolean(report && Number(report.ownedImageCount || 0) > 0);
        const total = normalizeNumber(totals.total, 0);
        const acquired = normalizeNumber(totals.acquired, 0);
        const acquiredPct = normalizeNumber(totals.acquiredPct, 0);
        const bronze = normalizeNumber(totals.bronze, 0);
        const silver = normalizeNumber(totals.silver, 0);
        const gold = normalizeNumber(totals.gold, 0);
        const unique = normalizeNumber(totals.unique, 0);
        const upgradable = normalizeNumber(totals.upgradable, 0);
        const missing = normalizeNumber(totals.missing, 0);
        const note = hasScan
            ? `${acquired}/${total} encontrados · ${acquiredPct}%`
            : "abra /prizes.phtml para sincronizar";
        return `
                    <aside class="ncc-aaa-progress-card ncc-aaa-progress-panel-stage2 ncc-aaa-quick-stats-card" aria-label="Estatísticas rápidas do NCC AAA">
                        <p class="ncc-aaa-progress-kicker">Estatísticas rápidas</p>
                        <div class="ncc-aaa-quick-medal-grid" aria-label="Troféus por nível">
                            <span class="ncc-aaa-quick-medal is-bronze"><strong>${escapeHtml(bronze)}</strong><em>Bronze</em></span>
                            <span class="ncc-aaa-quick-medal is-silver"><strong>${escapeHtml(silver)}</strong><em>Prata</em></span>
                            <span class="ncc-aaa-quick-medal is-gold"><strong>${escapeHtml(gold)}</strong><em>Ouro</em></span>
                        </div>
                        <strong class="ncc-aaa-progress-title">Trophy Tracker</strong>
                        <div class="ncc-aaa-progress-bar" aria-label="Progresso local de troféus">
                            <span style="width:${escapeAttribute(String(acquiredPct))}%"></span>
                        </div>
                        <span class="ncc-aaa-progress-note">${escapeHtml(note)}</span>
                        <div class="ncc-aaa-quick-mini-row" aria-label="Resumo extra de troféus">
                            <span>${escapeHtml(upgradable)} melhorar</span>
                            <span>${escapeHtml(missing)} faltando</span>
                            ${unique ? `<span>${escapeHtml(unique)} únicos</span>` : ""}
                        </div>
                    </aside>`;
    }

    function shellMarkup({ selectedAvatar, selectedTeam, selectedTheme, tone }) {
        return `
            <section class="ncc-aaa-shell" aria-label="NCC AAA - Central visual" style="${buildThemeStyleAttribute(tone)}">
                <div class="ncc-aaa-topbar" aria-label="Status da fundação NCC AAA">
                    <span class="ncc-aaa-top-pill ncc-aaa-top-pill-brand"><img class="ncc-aaa-brand-icon" src="${escapeAttribute(NCC_AAA_ICON_URL)}" alt="" loading="lazy" decoding="async">NCC AAA</span>
                    <strong class="ncc-aaa-top-pill ncc-aaa-top-pill-version">v${escapeHtml(VERSION)}</strong>
                    <span class="ncc-aaa-top-pill ncc-aaa-top-pill-path">/~YILLCIVI</span>
                    <span class="ncc-aaa-top-pill ncc-aaa-top-pill-section">CENTRAL DE MÓDULOS</span>
                    <span class="ncc-aaa-top-pill ncc-aaa-top-pill-gold">MÓDULOS ATIVOS</span>
                </div>

                <header class="ncc-aaa-hero ncc-aaa-stage-layout" aria-label="Palco visual NCC AAA">
                    <img class="ncc-aaa-stage-host" src="https://images.neopets.com/games/aaa/dailydare/2012/hub/aaa.png" alt="" loading="lazy" decoding="async">

                    <aside class="ncc-aaa-player-pass ncc-aaa-player-pass-stage2" aria-label="Passe do jogador">
                        <div class="ncc-aaa-pass-title">Passe do Jogador</div>
                        <div class="ncc-aaa-pass-avatar-wrap">
                            <img src="${escapeAttribute(selectedAvatar.imageUrl)}" alt="${escapeAttribute(selectedAvatar.label)}" loading="lazy" decoding="async">
                            <button class="ncc-aaa-avatar-edit-button" type="button" data-action="open-avatar-modal" aria-label="Trocar avatar visual" title="Trocar avatar">✎</button>
                        </div>
                        <strong class="ncc-aaa-pass-name">${escapeHtml(state.username)}</strong>
                        <span class="ncc-aaa-pass-avatar-name">${escapeHtml(selectedAvatar.label)}</span>
                        <span class="ncc-aaa-pass-team">${escapeHtml(selectedTeam.icon)} ${escapeHtml(selectedTeam.name)}</span>
                    </aside>

                    <section class="ncc-aaa-stage-board ncc-aaa-stage-board-main" aria-label="Quadro central do NCC AAA">
                        <img class="ncc-aaa-board-badge" src="https://images.neopets.com/bestof/2008/gamesmasterchallenge.gif" alt="Games Master Challenge" loading="lazy" decoding="async">
                        <p class="ncc-aaa-kicker ncc-aaa-stage-kicker">Gamesmaster Hub · módulos ativos do NCC AAA</p>
                        <h1>Central AAA de Módulos</h1>
                        <p class="ncc-aaa-subtitle">O palco visual em <strong>/~Yillcivi</strong> organiza os módulos auxiliares já instalados no NCC AAA. Cellblock, Kiko Pop, Tyranu Evavu, Kiss The Mortog, Godori, NeoQuest I e NeoQuest II continuam em escopo fechado: cada helper só atua na própria página oficial.</p>
                        <div class="ncc-aaa-status-pills" aria-label="Resumo rápido da fundação">
                            <span><strong>4</strong> módulos</span>
                            <span><strong>0</strong> alertas</span>
                            <span>Ícone oficial</span>
                            <span>Base Entry pronto</span>
                        </div>
                        <div class="ncc-aaa-stage-actions" aria-label="Ações principais do palco">
                            <button class="ncc-aaa-btn ncc-aaa-btn-primary ncc-aaa-stage-button" type="button" data-action="switch-view" data-view-id="games">
                                <span class="ncc-aaa-btn-icon" aria-hidden="true">🎮</span>
                                <span>Ver módulos</span>
                            </button>
                            <button class="ncc-aaa-btn ncc-aaa-btn-secondary ncc-aaa-stage-button" type="button" data-action="switch-view" data-view-id="arena">
                                <span class="ncc-aaa-btn-icon" aria-hidden="true">⚔️</span>
                                <span>Abrir Arena</span>
                            </button>
                            <button class="ncc-aaa-btn ncc-aaa-btn-secondary ncc-aaa-stage-button" type="button" data-action="switch-view" data-view-id="profile">
                                <span class="ncc-aaa-btn-icon" aria-hidden="true">🏆</span>
                                <span>Trophy Tracker</span>
                            </button>
                            <button class="ncc-aaa-btn ncc-aaa-btn-tech ncc-aaa-stage-button" type="button" data-action="switch-view" data-view-id="diagnostic">
                                <span class="ncc-aaa-btn-icon" aria-hidden="true">🧭</span>
                                <span>Bastidores técnicos</span>
                            </button>
                        </div>
                    </section>

${heroQuickStatsMarkup()}
                </header>

                <nav class="ncc-aaa-tabs ncc-aaa-tabs-strip" aria-label="Abas NCC AAA">
                    ${tabsMarkup()}
                </nav>

                <main class="ncc-aaa-view" data-ncc-aaa-render-mode="full" data-ncc-aaa-rendered-at="${escapeAttribute(isoNow())}">
                    ${currentViewMarkup(selectedAvatar, selectedTeam, selectedTheme)}
                </main>

                ${avatarModalMarkup(selectedAvatar)}
            </section>
        `;
    }

    function avatarModalMarkup(selectedAvatar) {
        return `
            <div class="ncc-aaa-avatar-modal" data-ncc-aaa-avatar-modal hidden role="dialog" aria-modal="true" aria-label="Escolher avatar visual">
                <button class="ncc-aaa-avatar-modal-backdrop" type="button" data-action="close-avatar-modal" aria-label="Fechar escolha de avatar"></button>
                <section class="ncc-aaa-avatar-modal-panel" aria-label="Avatares disponíveis">
                    <div class="ncc-aaa-avatar-modal-head">
                        <div>
                            <p class="ncc-aaa-kicker">Avatar Sketch</p>
                            <h2>Escolha seu visual</h2>
                            <p>Esta escolha altera apenas o visual do NCC AAA e fica salva nas preferências locais do jogador.</p>
                        </div>
                        <button class="ncc-aaa-avatar-modal-close" type="button" data-action="close-avatar-modal" aria-label="Fechar">×</button>
                    </div>
                    <div class="ncc-aaa-avatar-grid ncc-aaa-avatar-grid-modal">
                        ${AAA_AVATARS.map(avatar => `
                            <button class="ncc-aaa-avatar-option${avatar.id === selectedAvatar.id ? " is-selected" : ""}" type="button" data-action="select-avatar" data-avatar-id="${escapeAttribute(avatar.id)}" aria-pressed="${avatar.id === selectedAvatar.id ? "true" : "false"}">
                                <img src="${escapeAttribute(avatar.imageUrl)}" alt="${escapeAttribute(avatar.label)}" loading="lazy" decoding="async">
                                <span>${escapeHtml(avatar.label)}</span>
                            </button>
                        `).join("")}
                    </div>
                </section>
            </div>
        `;
    }

    function currentViewMarkup(selectedAvatar, selectedTeam, selectedTheme) {
        if (state.view === "games") return gamesViewMarkup();
        if (state.view === "arena") return arenaViewMarkup();
        if (state.view === "profile") return trophyTrackerViewMarkup();
        if (state.view === "diagnostic") return diagnosticViewMarkup();
        return dashboardViewMarkup(selectedAvatar, selectedTeam, selectedTheme);
    }

    function dashboardViewMarkup(selectedAvatar, selectedTeam, selectedTheme) {
        const emptySlots = Array.from({ length: 4 }, (_, index) => index + 1);
        return `
            <section class="ncc-aaa-home-refactor" aria-label="Painel inicial do NCC AAA">
                <article class="ncc-aaa-home-welcome-panel" aria-label="Boas-vindas NCC AAA">
                    <div class="ncc-aaa-home-welcome-copy">
                        <p class="ncc-aaa-kicker">Painel</p>
                        <h2>Bem-vindo ao NCC AAA</h2>
                        <p>Central unificada dos módulos do NCC AAA. Aqui você acompanha status, páginas-alvo, visual do jogador, Arena e bastidores técnicos dos helpers instalados.</p>
                        <button class="ncc-aaa-home-link" type="button" data-action="switch-view" data-view-id="diagnostic">Saiba mais sobre o NCC AAA</button>
                    </div>
                    <aside class="ncc-aaa-home-status-stack" aria-label="Status resumido da fundação">
                        <div class="ncc-aaa-home-status-item is-ok">
                            <span aria-hidden="true">✓</span>
                            <div>
                                <strong>Sistema operacional</strong>
                                <small>Tudo funcionando</small>
                            </div>
                        </div>
                        <div class="ncc-aaa-home-status-item">
                            <span aria-hidden="true">◉</span>
                            <div>
                                <strong>Modo atual</strong>
                                <small>Arena + módulos ativos</small>
                            </div>
                        </div>
                        <div class="ncc-aaa-home-status-item">
                            <span aria-hidden="true">↔</span>
                            <div>
                                <strong>Contrato externo</strong>
                                <small>Bridge ativo</small>
                            </div>
                        </div>
                    </aside>
                </article>

                <section class="ncc-aaa-home-stat-grid" aria-label="Métricas da fundação">
                    <article class="ncc-aaa-home-stat-card is-modules">
                        <span class="ncc-aaa-home-stat-icon" aria-hidden="true">▣</span>
                        <strong>4</strong>
                        <span>Módulos<br>ativos</span>
                    </article>
                    <article class="ncc-aaa-home-stat-card is-alerts">
                        <span class="ncc-aaa-home-stat-icon" aria-hidden="true">⚔</span>
                        <strong>Arena</strong>
                        <span>Training<br>ativo</span>
                    </article>
                    <article class="ncc-aaa-home-stat-card is-actions">
                        <span class="ncc-aaa-home-stat-icon" aria-hidden="true">⚡</span>
                        <strong>0</strong>
                        <span>Ações<br>no boot</span>
                    </article>
                    <article class="ncc-aaa-home-stat-card is-safe">
                        <span class="ncc-aaa-home-stat-icon" aria-hidden="true">◆</span>
                        <strong>OK</strong>
                        <span>Segurança<br>confirmada</span>
                    </article>
                </section>

                <article class="ncc-aaa-home-player-strip ncc-aaa-home-visual-panel" aria-label="Visual do jogador">
                    <div class="ncc-aaa-home-player-mini">
                        <div class="ncc-aaa-home-avatar-edit-wrap">
                            <img src="${escapeAttribute(selectedAvatar.imageUrl)}" alt="${escapeAttribute(selectedAvatar.label)}" loading="lazy" decoding="async">
                            <button class="ncc-aaa-avatar-edit-button ncc-aaa-avatar-edit-button-mini" type="button" data-action="open-avatar-modal" aria-label="Trocar avatar visual" title="Trocar avatar">✎</button>
                        </div>
                        <div>
                            <p class="ncc-aaa-kicker">Visual do jogador</p>
                            <strong>${escapeHtml(state.username)}</strong>
                            <span>${escapeHtml(selectedAvatar.label)} · ${escapeHtml(selectedTeam.icon)} ${escapeHtml(selectedTeam.name)}</span>
                            <small>Tema atual: ${escapeHtml(selectedTheme.label)}</small>
                        </div>
                    </div>
                    <div class="ncc-aaa-home-visual-controls" aria-label="Preferências visuais">
                        <label class="ncc-aaa-label" for="ncc-aaa-dashboard-team-select">Time visual</label>
                        <select id="ncc-aaa-dashboard-team-select" class="ncc-aaa-select" data-action="select-team">
                            ${TEAMS.map(team => `<option value="${escapeAttribute(team.id)}"${team.id === selectedTeam.id ? " selected" : ""}>${escapeHtml(team.icon)} ${escapeHtml(team.name)}</option>`).join("")}
                        </select>
                        <label class="ncc-aaa-label" for="ncc-aaa-dashboard-theme-select">Tema visual</label>
                        <select id="ncc-aaa-dashboard-theme-select" class="ncc-aaa-select" data-action="select-theme">
                            ${THEMES.map(theme => `<option value="${escapeAttribute(theme.id)}"${theme.id === selectedTheme.id ? " selected" : ""}>${escapeHtml(theme.label)}</option>`).join("")}
                        </select>
                        <div class="ncc-aaa-home-visual-actions">
                            <button class="ncc-aaa-btn ncc-aaa-btn-secondary ncc-aaa-home-mini-button" type="button" data-action="open-avatar-modal">Escolher avatar</button>
                            <button class="ncc-aaa-btn ncc-aaa-btn-tech ncc-aaa-home-mini-button" type="button" data-action="reset-visual">Resetar visual</button>
                        </div>
                    </div>
                </article>

                <article class="ncc-aaa-home-slots-panel" aria-label="Prévia de slots de Jogos visual-only">
                    <div class="ncc-aaa-section-heading ncc-aaa-home-section-heading">
                        <div>
                            <p class="ncc-aaa-kicker">Módulos</p>
                            <h2>Módulos ativos</h2>
                        </div>
                        <button class="ncc-aaa-btn ncc-aaa-btn-secondary ncc-aaa-home-mini-button" type="button" data-action="switch-view" data-view-id="games">Ver módulos</button>
                    </div>
                    <p class="ncc-aaa-home-slots-note">Cellblock, Kiko Pop, Tyranu Evavu, Kiss The Mortog, Godori e NeoQuest já estão instalados. A aba Módulos mostra status, páginas-alvo e links de abertura.</p>
                    <div class="ncc-aaa-home-empty-slot-row">
                        ${emptySlots.map(index => `
                            <article class="ncc-aaa-home-empty-slot" aria-label="Slot disponível ${index}">
                                <span class="ncc-aaa-home-empty-slot-icon" aria-hidden="true">▥</span>
                                <strong>Módulo ativo</strong>
                            </article>
                        `).join("")}
                    </div>
                </article>
            </section>
        `;
    }

    function gamesViewMarkup() {
        const activeModules = GAME_MODULE_REGISTRY.map(module => normalizeModuleCardData(module));
        const plannedModules = PLANNED_GAME_SLOTS.filter(slot => !GAME_MODULE_REGISTRY.some(module => module.id === slot.id));
        return `
            <section class="ncc-aaa-games-hub ncc-aaa-modules-hub" aria-label="Central de Módulos do NCC AAA">
                <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-games-stage-panel ncc-aaa-modules-stage-panel">
                    <div class="ncc-aaa-games-announcer" aria-hidden="true">
                        <img src="https://images.neopets.com/games/aaa/dailydare/2012/popups/aaa.png" alt="" loading="lazy" decoding="async">
                    </div>
                    <div class="ncc-aaa-games-stage-copy">
                        <p class="ncc-aaa-kicker">Central de Módulos</p>
                        <h2>Módulos instalados no NCC AAA</h2>
                        <p>Esta página mostra somente o inventário dos helpers já instalados e das reservas futuras. O painel não joga, não envia formulário e não executa ações oficiais; cada módulo roda apenas na própria página-alvo.</p>
                        <div class="ncc-aaa-games-stage-badges ncc-aaa-modules-stage-badges">
                            <span>4 ativos</span>
                            <span>2 fixes manuais/visuais</span>
                            <span>2 guias NeoQuest</span>
                            <span>0 espelhos</span>
                            <span>Sem ação no painel</span>
                        </div>
                    </div>
                </article>

                <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-modules-active-card" aria-label="Módulos ativos do NCC AAA">
                    <div class="ncc-aaa-section-heading">
                        <div>
                            <p class="ncc-aaa-kicker">Instalados</p>
                            <h2>Módulos ativos</h2>
                        </div>
                        <span class="ncc-aaa-token">${activeModules.length} ativos</span>
                    </div>
                    <p class="ncc-aaa-modules-section-note">Helpers disponíveis agora. O botão apenas abre a página oficial; a lógica de cada helper continua isolada nessa página.</p>
                    <div class="ncc-aaa-module-grid">
                        ${activeModules.map(module => activeModuleCardMarkup(module)).join("")}
                    </div>
                </article>

                <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-modules-planned-card" aria-label="Módulos planejados e reservas">
                    <div class="ncc-aaa-section-heading">
                        <div>
                            <p class="ncc-aaa-kicker">Próximos</p>
                            <h2>Planejados e reservas</h2>
                        </div>
                        <span class="ncc-aaa-token">Reservado</span>
                    </div>
                    <p class="ncc-aaa-modules-section-note">Entradas sem runtime. Elas ficam aqui só para organizar o backlog e evitar parecer que já estão funcionando.</p>
                    <div class="ncc-aaa-module-planned-grid">
                        ${plannedModules.map(slot => plannedModuleCardMarkup(slot)).join("")}
                    </div>
                </article>

                <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-games-rulebook ncc-aaa-modules-rulebook">
                    <div class="ncc-aaa-section-heading">
                        <div>
                            <p class="ncc-aaa-kicker">Regras dos módulos</p>
                            <h2>Limites preservados</h2>
                        </div>
                        <span class="ncc-aaa-token">Seguro</span>
                    </div>
                    <div class="ncc-aaa-rulebook-grid">
                        <span>Painel sem ação de jogo</span>
                        <span>Escopo por página oficial</span>
                        <span>Sem alteração no NCC Base</span>
                        <span>Sem alteração no NCC RPG</span>
                        <span>Teclado só nos NeoQuests</span>
                        <span>Rede só por clique/tecla do helper</span>
                    </div>
                </article>
            </section>
        `;
    }

    function arenaViewMarkup() {
        const trainingState = buildTrainingExternalState("arena-view");
        const schoolCards = Object.values(trainingState.schools || {}).map(school => trainingSchoolCardMarkup(school)).join("");
        const nextLabel = formatArenaNextTrainingLabel(trainingState);
        const activeCount = Number(trainingState.activeCount || 0);
        const completeCount = Number(trainingState.completeCount || 0);
        const hasAlerts = completeCount > 0;
        const featuredSchools = Object.values(trainingState.schools || {});
        const battledomeState = buildBattledomeExternalState("arena-view");
        const battledomeSummary = battledomeState.summary || {};
        const hasPrizeLog = Number(battledomeSummary.prizes || 0) > 0;
        const hasLoadout = Number(battledomeSummary.loadoutWeapons || 0) > 0;
        return `
            <section class="ncc-aaa-arena-hub ncc-aaa-games-hub ncc-aaa-arena-clean ncc-aaa-consumer-arena" aria-label="Arena do NCC AAA">
                <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-games-stage-panel ncc-aaa-arena-stage-panel ncc-aaa-arena-clean-hero ncc-aaa-consumer-hero">
                    <div class="ncc-aaa-games-announcer ncc-aaa-arena-announcer" aria-hidden="true">
                        <img src="${escapeAttribute(TRAINING_ICON_URL)}" alt="" loading="lazy" decoding="async">
                    </div>
                    <div class="ncc-aaa-games-stage-copy">
                        <p class="ncc-aaa-kicker">Treino e combate</p>
                        <h2>Arena do NCC AAA</h2>
                        <p>Treinos, academias e recursos de combate em um só lugar.</p>
                        <div class="ncc-aaa-games-stage-badges ncc-aaa-arena-stage-badges ncc-aaa-arena-clean-badges">
                            <span>${escapeHtml(activeCount === 1 ? "1 treino ativo" : `${activeCount} treinos ativos`)}</span>
                            <span>${escapeHtml(nextLabel)}</span>
                            <span>${hasAlerts ? `${escapeHtml(String(completeCount))} pronto(s)` : "0 prontos"}</span>
                            <span>Battledome ativo</span>
                            <span>Ações protegidas</span>
                        </div>
                    </div>
                </article>

                <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-arena-ready-card ncc-aaa-arena-summary-card ncc-aaa-consumer-section" aria-label="Resumo do Training Assistant">
                    <div class="ncc-aaa-section-heading">
                        <div>
                            <p class="ncc-aaa-kicker">Training Assistant</p>
                            <h2>Treinos e academias</h2>
                        </div>
                        <span class="ncc-aaa-token">seguro</span>
                    </div>
                    <p class="ncc-aaa-modules-section-note">Treinos e cursos com ações sensíveis protegidas por confirmação.</p>
                    <div class="ncc-aaa-arena-feature-grid ncc-aaa-consumer-grid">
                        ${trainingArenaFeatureCardMarkup({
                            icon: "⏱️",
                            title: "Treinos",
                            text: `${activeCount} ativo(s) · ${completeCount} pronto(s).`,
                            tags: [nextLabel]
                        })}
                        ${trainingArenaFeatureCardMarkup({
                            icon: "🏫",
                            title: "Academias",
                            text: `${featuredSchools.length} escolas com painel próprio.`,
                            tags: ["Swashbuckling", "Mystery", "Ninja"]
                        })}
                        ${trainingArenaFeatureCardMarkup({
                            icon: "🛡️",
                            title: "Proteção",
                            text: "SDB e cursos só por clique confirmado.",
                            tags: ["SDB", "Cursos", "Confirmação"]
                        })}
                    </div>
                </article>

                <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-battledome-foundation-card ncc-aaa-consumer-section" aria-label="Battledome Tracker & Fav">
                    <div class="ncc-aaa-section-heading">
                        <div>
                            <p class="ncc-aaa-kicker">Battledome</p>
                            <h2>Battledome Tracker & Fav</h2>
                        </div>
                        <span class="ncc-aaa-token">batalha manual</span>
                    </div>
                    <p class="ncc-aaa-modules-section-note">Recompensas, favoritos e loadout visual para batalhas manuais.</p>
                    <div class="ncc-aaa-arena-feature-grid ncc-aaa-battledome-foundation-grid ncc-aaa-consumer-grid">
                        ${battledomeFoundationFeatureCardMarkup({
                            icon: "🧾",
                            title: "Recompensas",
                            text: `${battledomeSummary.prizes || 0} item(ns) salvo(s).`,
                            tags: [battledomeSummary.prizeLoggerPaused ? "pausado" : "ativo", battledomeSummary.observerActive ? "na arena" : "manual"]
                        })}
                        ${battledomeFoundationFeatureCardMarkup({
                            icon: "⭐",
                            title: "Favoritos",
                            text: `${battledomeSummary.favoriteOpponents || 0} favorito(s) de ${battledomeSummary.opponents || 0} oponente(s).`,
                            tags: ["estrelas", "topo visual"]
                        })}
                        ${battledomeFoundationFeatureCardMarkup({
                            icon: "♥",
                            title: "Loadout",
                            text: `${battledomeSummary.loadoutWeapons || 0}/2 arma(s) favorita(s).`,
                            tags: [battledomeSummary.loadoutEnabled ? "ligado" : "desligado", "sem Fight auto"]
                        })}
                    </div>
                    <div class="ncc-aaa-actions ncc-aaa-battledome-actions ncc-aaa-consumer-actions">
                        <a class="ncc-aaa-button" href="${escapeAttribute(BATTLEDOME_HOME_URL)}">Abrir Battledome</a>
                        <a class="ncc-aaa-button ncc-aaa-button-muted" href="${escapeAttribute(BATTLEDOME_FIGHT_URL)}">Oponentes</a>
                        <a class="ncc-aaa-button ncc-aaa-button-muted" href="${escapeAttribute(BATTLEDOME_ARENA_URL)}">Arena</a>
                        ${hasPrizeLog ? `<button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-copy-log">Copiar log</button>` : ""}
                        ${hasLoadout ? `<button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-apply-loadout">Aplicar loadout</button>` : ""}
                    </div>
                    <div class="ncc-aaa-consumer-detail-grid">
                        ${battledomeOpponentSummaryMarkup(battledomeState)}
                        ${battledomePrizeLogSummaryMarkup(battledomeState)}
                        ${battledomeLoadoutSummaryMarkup(battledomeState)}
                    </div>
                    <div class="ncc-aaa-safe-note ncc-aaa-battledome-safe-note ncc-aaa-consumer-safe-note">Seguro: a luta continua manual. O NCC AAA não escolhe oponente, não coleta prêmio, não clica em Fight e não envia ação de batalha.</div>
                </article>

                <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-arena-schools-card ncc-aaa-arena-clean-schools ncc-aaa-consumer-section" aria-label="Academias mapeadas na Arena">
                    <div class="ncc-aaa-section-heading">
                        <div>
                            <p class="ncc-aaa-kicker">Academias</p>
                            <h2>Status das escolas</h2>
                        </div>
                        <span class="ncc-aaa-token">${Object.keys(TRAINING_SCHOOLS).length} escolas</span>
                    </div>
                    <div class="ncc-aaa-arena-school-grid">
                        ${schoolCards}
                    </div>
                </article>
            </section>
        `;
    }

    function formatArenaNextTrainingLabel(trainingState) {
        if (trainingState?.nextCourse) {
            const pet = trainingState.nextCourse.name || "treino";
            const left = trainingState.nextCourse.remainingLabel || "em breve";
            return `Próximo: ${pet} · ${left}`;
        }
        return "Sem timer ativo";
    }

    function trainingArenaFeatureCardMarkup(item) {
        const tags = Array.isArray(item.tags) ? item.tags.filter(Boolean).slice(0, 4) : [];
        return `
            <article class="ncc-aaa-arena-feature-card">
                <div class="ncc-aaa-arena-feature-icon" aria-hidden="true">${escapeHtml(item.icon || "✨")}</div>
                <div>
                    <h3>${escapeHtml(item.title || "Recurso")}</h3>
                    <p>${escapeHtml(item.text || "")}</p>
                    ${tags.length ? `<div class="ncc-aaa-module-tags">${tags.map(tag => `<span>${escapeHtml(tag)}</span>`).join("")}</div>` : ""}
                </div>
            </article>
        `;
    }

    function battledomeFoundationFeatureCardMarkup(item) {
        return trainingArenaFeatureCardMarkup(item);
    }

    function battledomeOpponentSummaryMarkup(battledomeState) {
        const reportState = battledomeState?.report?.state || loadBattledomeState();
        const favorites = getBattledomeFavoriteOpponents(reportState);
        const opponents = Array.isArray(reportState.opponents) ? reportState.opponents : [];
        const summary = opponents.length ? `${opponents.length} oponente(s) · ${favorites.length} favorito(s)` : "aguardando sincronização";
        return `
            <details class="ncc-aaa-consumer-details ncc-aaa-bd-local-summary ${opponents.length || favorites.length ? "" : "is-empty"}">
                <summary><span>Oponentes favoritos</span><small>${escapeHtml(summary)}</small></summary>
                ${opponents.length || favorites.length ? `
                    ${favorites.length ? `
                        <div class="ncc-aaa-bd-favorite-strip">
                            ${favorites.map(opponent => `
                                <span class="ncc-aaa-bd-favorite-chip" title="ID ${escapeAttribute(opponent.id)}">
                                    ${opponent.image ? `<img src="${escapeAttribute(opponent.image)}" alt="" loading="lazy" decoding="async">` : `<span class="ncc-aaa-bd-favorite-thumb-fallback">★</span>`}
                                    <span>${escapeHtml(opponent.name)}</span>
                                </span>
                            `).join("")}
                        </div>
                    ` : `<div class="ncc-aaa-bd-local-summary-note">Nenhum favorito marcado ainda. Use as estrelas na página de escolher oponente.</div>`}
                    <details class="ncc-aaa-bd-advanced">
                        <summary>Opções avançadas</summary>
                        <p>Estas ações afetam apenas oponentes e favoritos salvos neste navegador.</p>
                        <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-clear-opponents">Limpar favoritos deste navegador</button>
                    </details>
                ` : `<div class="ncc-aaa-bd-local-summary-note">Abra a página de oponentes para sincronizar a lista e marcar favoritos com estrela.</div>`}
            </details>
        `;
    }

    function battledomePrizeLogSummaryMarkup(battledomeState) {
        const reportState = battledomeState?.report?.state || loadBattledomeState();
        const filtered = getBattledomeFilteredPrizes(reportState);
        const total = Array.isArray(reportState.prizes) ? reportState.prizes.length : 0;
        const paused = reportState.paused === true;
        const summary = `${filtered.length} no filtro · ${total} salvo(s)`;
        return `
            <details class="ncc-aaa-consumer-details ncc-aaa-bd-prize-summary ${paused ? "is-paused" : ""}">
                <summary><span>Registro de recompensas</span><small>${escapeHtml(summary)}</small></summary>
                <div class="ncc-aaa-bd-prize-controls">
                    <label>Filtro
                        <select data-bd-setting="filterMode">
                            <option value="today" ${reportState.filterMode === "today" ? "selected" : ""}>Hoje NST</option>
                            <option value="seven" ${reportState.filterMode === "seven" ? "selected" : ""}>Últimos 7 dias</option>
                            <option value="all" ${reportState.filterMode === "all" ? "selected" : ""}>Tudo</option>
                            <option value="custom" ${reportState.filterMode === "custom" ? "selected" : ""}>Intervalo manual</option>
                        </select>
                    </label>
                    <label>Formato
                        <select data-bd-setting="format">
                            <option value="lines" ${reportState.format === "lines" ? "selected" : ""}>Linhas com hora</option>
                            <option value="counts" ${reportState.format === "counts" ? "selected" : ""}>Contagem por item</option>
                        </select>
                    </label>
                    <label>Início
                        <input type="datetime-local" value="${escapeAttribute(reportState.filterStart || "")}" data-bd-setting="filterStart">
                    </label>
                    <label>Fim
                        <input type="datetime-local" value="${escapeAttribute(reportState.filterEnd || "")}" data-bd-setting="filterEnd">
                    </label>
                </div>
                <pre class="ncc-aaa-bd-prize-log">${escapeHtml(formatBattledomePrizeLog(filtered, reportState.format) || "Nenhum item registrado neste filtro ainda.")}</pre>
                <div class="ncc-aaa-consumer-detail-actions">
                    <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-copy-log">Copiar log</button>
                    <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-toggle-prize-paused">${paused ? "Retomar logger" : "Pausar logger"}</button>
                </div>
                <details class="ncc-aaa-bd-advanced">
                    <summary>Opções avançadas</summary>
                    <p>Estas ações afetam apenas o histórico salvo neste navegador.</p>
                    <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-clear-log">Zerar log local</button>
                </details>
                <div class="ncc-aaa-bd-local-summary-note">Status: ${paused ? "pausado" : "ativo durante batalha com recompensa visível"}.</div>
            </details>
        `;
    }


    function battledomeLoadoutSummaryMarkup(battledomeState) {
        const reportState = battledomeState?.report?.state || loadBattledomeState();
        const weapons = Array.isArray(reportState.loadoutWeapons) ? reportState.loadoutWeapons.slice(0, 2) : [];
        const enabled = reportState.loadoutEnabled !== false;
        const lastApplied = reportState.loadoutLastAppliedAt ? formatBattledomePrizeTime(reportState.loadoutLastAppliedAt) : "Ainda não aplicado";
        const summary = `${weapons.length}/2 arma(s) · ${enabled ? "ligado" : "desligado"}`;
        return `
            <details class="ncc-aaa-consumer-details ncc-aaa-bd-loadout-summary ${enabled ? "" : "is-disabled"}">
                <summary><span>Gerenciar loadout</span><small>${escapeHtml(summary)}</small></summary>
                ${weapons.length ? `
                    <div class="ncc-aaa-bd-loadout-strip">
                        ${weapons.map((weapon, index) => `
                            <span class="ncc-aaa-bd-loadout-chip">
                                ${weapon.src ? `<img src="${escapeAttribute(weapon.src)}" alt="" loading="lazy" decoding="async">` : `<span class="ncc-aaa-bd-favorite-thumb-fallback">♥</span>`}
                                <span><b>${escapeHtml(String(index + 1))}.</b> ${escapeHtml(weapon.name || weapon.image || "Arma favorita")}</span>
                            </span>
                        `).join("")}
                    </div>
                ` : `<div class="ncc-aaa-bd-local-summary-note">Nenhuma arma favorita marcada. Abra uma arena ativa e use os corações sobre os equipamentos.</div>`}
                <div class="ncc-aaa-bd-local-summary-note">Última aplicação: ${escapeHtml(lastApplied)}. A pré-seleção é visual; confira os campos antes de lutar.</div>
                <div class="ncc-aaa-consumer-detail-actions">
                    <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-toggle-loadout-enabled">${enabled ? "Desligar loadout" : "Ligar loadout"}</button>
                    <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-apply-loadout">Aplicar loadout</button>
                </div>
                <details class="ncc-aaa-bd-advanced">
                    <summary>Opções avançadas</summary>
                    <p>Estas ações afetam apenas armas favoritas salvas no navegador. Não remove equipamentos da conta.</p>
                    <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-clear-loadout">Limpar loadout local</button>
                </details>
            </details>
        `;
    }

    function arenaFutureCardMarkup(item) {
        return `
            <article class="ncc-aaa-arena-future-mini-card">
                <div class="ncc-aaa-arena-feature-icon" aria-hidden="true">${escapeHtml(item.icon || "⭐")}</div>
                <div>
                    <h3>${escapeHtml(item.title || "Expansão")}</h3>
                    <p>${escapeHtml(item.text || "Reservado para versão futura.")}</p>
                    <div class="ncc-aaa-module-tags">${(item.tags || []).map(tag => `<span>${escapeHtml(tag)}</span>`).join("")}</div>
                </div>
            </article>
        `;
    }

    function trainingSchoolCardMarkup(school) {
        const levelLabel = Number.isFinite(school.maxLevel) ? `até nível ${school.maxLevel}` : "sem limite";
        const entries = Array.isArray(school.entries) ? school.entries : [];
        const activeCount = Number(school.activeCount || 0);
        const completeCount = Number(school.completeCount || 0);
        const statusLabel = completeCount > 0
            ? `${completeCount} curso(s) pronto(s)`
            : (activeCount > 0 ? `${activeCount} treino(s) ativo(s)` : "Sem treino em andamento");
        const protectionLabel = school.id === "pirate" ? "busca manual no SDB" : "SDB protegido";
        const entryLines = entries.length ? entries.slice(0, 3).map(entry => {
            const left = Number(entry.endTime || 0) - Date.now();
            const tone = left <= 0 || entry.complete ? "Curso pronto" : formatTrainingDuration(left);
            return `<span>${escapeHtml(entry.name)} · ${escapeHtml(tone)}</span>`;
        }).join("") : "";
        return `
            <article class="ncc-aaa-module-planned-card ncc-aaa-arena-school-card" aria-label="Academia mapeada: ${escapeAttribute(school.name)}">
                <img class="ncc-aaa-arena-school-icon" src="${escapeAttribute(school.icon)}" alt="" loading="lazy" decoding="async">
                <div>
                    <h3>${escapeHtml(school.name)}</h3>
                    <p>${escapeHtml(school.currency)} · ${escapeHtml(levelLabel)} · ${escapeHtml(protectionLabel)}</p>
                    <div class="ncc-aaa-arena-school-status ${completeCount > 0 ? "ready" : (activeCount > 0 ? "active" : "idle")}">${escapeHtml(statusLabel)}</div>
                    ${entryLines ? `<div class="ncc-aaa-arena-training-lines">${entryLines}</div>` : ""}
                    <div class="ncc-aaa-module-links">
                        <a href="${escapeAttribute(school.href)}">Abrir academia</a>
                    </div>
                </div>
            </article>
        `;
    }

    function arenaSlotCardMarkup(slot) {
        return `
            <article class="ncc-aaa-module-planned-card ncc-aaa-arena-slot-card ncc-aaa-arena-slot-${escapeAttribute(slot.id)}" aria-label="Slot reservado da Arena: ${escapeAttribute(slot.name)}">
                ${moduleIconMarkup(slot, "ncc-aaa-module-icon-small")}
                <div>
                    <h3>${escapeHtml(slot.name)}</h3>
                    <p>${escapeHtml(slot.description || slot.note || "Reservado para versão futura.")}</p>
                    <div class="ncc-aaa-module-tags">
                        <span>${escapeHtml(slot.status || "reservado")}</span>
                        <span>${escapeHtml(slot.type || slot.role || "Arena")}</span>
                    </div>
                </div>
            </article>
        `;
    }

    function normalizeModuleCardData(module) {
        const meta = getModuleVisualMeta(module.id);
        return {
            ...module,
            icon: meta.icon,
            iconUrl: meta.iconUrl || module.iconUrl || "",
            typeLabel: meta.typeLabel,
            shortDescription: meta.shortDescription || module.description,
            tags: meta.tags
        };
    }

    function getModuleVisualMeta(moduleId) {
        const map = {
            "cellblock": {
                icon: "♟️",
                iconUrl: CELLBLOCK_ICON_URL,
                typeLabel: "Apoio visual",
                shortDescription: "Lê o tabuleiro e colore sugestões visuais. Não joga por você.",
                tags: ["Visual", "Sugestões", "Sem clique"]
            },
            "kiko-pop": {
                icon: "🎯",
                iconUrl: KIKO_POP_ICON_URL,
                typeLabel: "Fix manual de Flash",
                shortDescription: "Troca a etapa Flash por botões HTML manuais e confirmação separada de prêmio.",
                tags: ["Manual", "Flash fix", "GET PRIZE"]
            },
            "neoquest-one": {
                icon: "🗺️",
                iconUrl: NEOQUESTS_ICON_URL,
                typeLabel: "Guia + teclado",
                shortDescription: "Mostra objetivos, informações de combate, itens e atalhos dentro do NeoQuest I.",
                tags: ["Objetivos", "Combate", "Teclado"]
            },
            "neoquest-two": {
                icon: "⚔️",
                iconUrl: NEOQUESTS_ICON_URL,
                typeLabel: "Guia + teclado",
                shortDescription: "Marca alvo/ação sugerida e mantém atalhos manuais dentro do NeoQuest II.",
                tags: ["Alvo", "Ação", "KeyH manual"]
            }
        };
        return map[moduleId] || { icon: "🧩", typeLabel: "Módulo", shortDescription: "Módulo ativo do NCC AAA.", tags: ["Ativo"] };
    }

    function moduleIconMarkup(item, sizeClass = "") {
        const className = sizeClass ? `ncc-aaa-module-icon ${sizeClass}` : "ncc-aaa-module-icon";
        if (item && item.iconUrl) {
            return `<img class="${escapeAttribute(`${className} ncc-aaa-module-icon-img`)}" src="${escapeAttribute(item.iconUrl)}" alt="" loading="lazy" decoding="async">`;
        }
        return `<span class="${escapeAttribute(className)}" aria-hidden="true">${escapeHtml(item?.icon || "⭐")}</span>`;
    }

    function activeModuleCardMarkup(module) {
        return `
            <article class="ncc-aaa-module-card ncc-aaa-module-card-${escapeAttribute(module.id)}" aria-label="Módulo ativo: ${escapeAttribute(module.name)}">
                <div class="ncc-aaa-module-card-head">
                    ${moduleIconMarkup(module)}
                    <div>
                        <p class="ncc-aaa-kicker">Ativo</p>
                        <h3>${escapeHtml(module.name)}</h3>
                    </div>
                    <span class="ncc-aaa-module-status">Ativo</span>
                </div>
                <p>${escapeHtml(module.shortDescription || module.description)}</p>
                <dl class="ncc-aaa-module-meta">
                    <div><dt>Página</dt><dd><code>${escapeHtml(module.pageOnly)}</code></dd></div>
                    <div><dt>Tipo</dt><dd>${escapeHtml(module.typeLabel || module.type)}</dd></div>
                </dl>
                <div class="ncc-aaa-module-tags">
                    ${(module.tags || []).map(tag => `<span>${escapeHtml(tag)}</span>`).join("")}
                </div>
                <a class="ncc-aaa-btn ncc-aaa-btn-primary ncc-aaa-module-open" href="${escapeAttribute(module.href)}">Abrir ${escapeHtml(module.name.replace(" Helper", "").replace(" Fix", ""))}</a>
            </article>
        `;
    }

    function plannedModuleCardMarkup(slot) {
        return `
            <article class="ncc-aaa-module-planned-card ncc-aaa-module-planned-${escapeAttribute(slot.id)}" aria-label="Módulo planejado: ${escapeAttribute(slot.name)}">
                ${moduleIconMarkup(slot, "ncc-aaa-module-icon-small")}
                <div>
                    <h3>${escapeHtml(slot.name)}</h3>
                    <p>${escapeHtml(slot.note || "Reservado para versão futura.")}</p>
                    <div class="ncc-aaa-module-tags">
                        <span>${escapeHtml(slot.status || "planejado")}</span>
                        <span>${escapeHtml(slot.role || "Reserva")}</span>
                    </div>
                </div>
            </article>
        `;
    }

    function trophyTrackerViewMarkup() {
        const trophyState = loadTrophyTrackerState();
        const report = buildTrophyTrackerReport("view");
        const analysis = report.analysis;
        const hasScan = Boolean(trophyState && Array.isArray(trophyState.ownedImages) && trophyState.ownedImages.length);
        const lastUpdated = trophyState?.updatedAt ? formatSimpleDateTime(trophyState.updatedAt) : "ainda não sincronizado";
        const progressLabel = hasScan ? `${analysis.totals.acquiredPct}% completo` : "aguardando leitura";
        return `
            <section class="ncc-aaa-trophy-view ncc-aaa-games-hub ncc-aaa-trophy-modern" aria-label="Trophy Tracker do NCC AAA">
                <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-trophy-hero ncc-aaa-trophy-modern-hero">
                    <div class="ncc-aaa-trophy-hero-medals" aria-hidden="true">
                        <span><img src="https://images.neopets.com/trophies/216_3.gif" alt="" loading="lazy" decoding="async"></span>
                        <span><img src="https://images.neopets.com/trophies/216_2.gif" alt="" loading="lazy" decoding="async"></span>
                        <span><img src="https://images.neopets.com/trophies/216_1.gif" alt="" loading="lazy" decoding="async"></span>
                    </div>
                    <div class="ncc-aaa-trophy-hero-copy">
                        <p class="ncc-aaa-kicker">Trophy Tracker</p>
                        <h2>Rastreador de troféus</h2>
                        <p>Veja seu progresso, encontre troféus faltantes e priorize melhorias sem sair do NCC AAA.</p>
                        <div class="ncc-aaa-trophy-hero-metrics" aria-label="Resumo rápido do Trophy Tracker">
                            <span><strong>${escapeHtml(hasScan ? analysis.totals.acquired : 0)}</strong><small>encontrados</small></span>
                            <span><strong>${escapeHtml(hasScan ? analysis.totals.upgradable : 0)}</strong><small>melhorar</small></span>
                            <span><strong>${escapeHtml(hasScan ? analysis.totals.missing : analysis.totals.total)}</strong><small>faltando</small></span>
                            <span><strong>${escapeHtml(progressLabel)}</strong><small>${escapeHtml(lastUpdated)}</small></span>
                        </div>
                        <div class="ncc-aaa-trophy-actions">
                            <a class="ncc-aaa-btn ncc-aaa-btn-primary" href="${escapeAttribute(TROPHY_TRACKER_PRIZES_URL)}">Abrir página de troféus</a>
                            ${hasScan ? `<button class="ncc-aaa-btn ncc-aaa-btn-secondary" type="button" data-action="trophy-copy-summary">Copiar resumo</button>` : ""}
                            ${hasScan ? `<button class="ncc-aaa-btn ncc-aaa-btn-tech" type="button" data-action="trophy-open-popup">Abrir popup</button>` : ""}
                        </div>
                    </div>
                </article>

                ${hasScan ? trophyTrackerDashboardMarkup(report, lastUpdated) : trophyTrackerEmptyStateMarkup()}
            </section>
        `;
    }


    function formatSimpleDateTime(value) {
        try {
            const date = new Date(value);
            if (Number.isNaN(date.getTime())) return String(value || "");
            return date.toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "short" });
        } catch (_) {
            return String(value || "");
        }
    }

    function trophyTrackerEmptyStateMarkup() {
        return `
            <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-trophy-empty ncc-aaa-trophy-empty-modern">
                <div class="ncc-aaa-section-heading">
                    <div>
                        <p class="ncc-aaa-kicker">Primeira leitura</p>
                        <h2>Capture seus troféus em /prizes.phtml</h2>
                    </div>
                    <span class="ncc-aaa-token">somente leitura</span>
                </div>
                <p class="ncc-aaa-modules-section-note">O NCC AAA lê apenas as imagens visíveis quando você abre sua página de prêmios logado. Nada é buscado em background.</p>
                <div class="ncc-aaa-trophy-empty-steps">
                    <span><b>1</b> Abra sua página de troféus</span>
                    <span><b>2</b> Aguarde o painel confirmar a leitura</span>
                    <span><b>3</b> Volte ao Trophy Tracker</span>
                </div>
                <a class="ncc-aaa-btn ncc-aaa-btn-primary" href="${escapeAttribute(TROPHY_TRACKER_PRIZES_URL)}">Abrir página de troféus</a>
            </article>
        `;
    }

    function trophyTrackerDashboardMarkup(report, lastUpdated) {
        const analysis = report.analysis;
        const totals = analysis.totals;
        return `
            <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-trophy-summary-card ncc-aaa-trophy-dashboard-modern">
                <div class="ncc-aaa-trophy-dashboard-head">
                    <div>
                        <p class="ncc-aaa-kicker">Resumo local</p>
                        <h2>${escapeHtml(totals.total)} troféus no catálogo</h2>
                    </div>
                    <span class="ncc-aaa-token">Atualizado: ${escapeHtml(lastUpdated)}</span>
                </div>
                <div class="ncc-aaa-trophy-stat-grid ncc-aaa-trophy-stat-grid-modern">
                    <div class="ncc-aaa-trophy-stat is-progress"><strong>${escapeHtml(`${totals.acquiredPct}%`)}</strong><span>progresso</span></div>
                    <div class="ncc-aaa-trophy-stat is-bronze"><strong>${escapeHtml(totals.bronze || 0)}</strong><span>bronze</span></div>
                    <div class="ncc-aaa-trophy-stat is-silver"><strong>${escapeHtml(totals.silver || 0)}</strong><span>prata</span></div>
                    <div class="ncc-aaa-trophy-stat is-gold"><strong>${escapeHtml(totals.gold || 0)}</strong><span>ouro</span></div>
                    <div class="ncc-aaa-trophy-stat is-upgradable"><strong>${escapeHtml(totals.upgradable)}</strong><span>melhorar</span></div>
                    <div class="ncc-aaa-trophy-stat is-missing"><strong>${escapeHtml(totals.missing)}</strong><span>faltando</span></div>
                </div>
                <div class="ncc-aaa-trophy-progress ncc-aaa-trophy-progress-modern" aria-label="Progresso geral de troféus encontrados">
                    <span style="width:${escapeAttribute(String(totals.acquiredPct))}%"></span>
                </div>
            </article>
            ${trophyTrackerToolsMarkup(analysis)}
            <section class="ncc-aaa-trophy-category-list ncc-aaa-trophy-category-list-modern" data-trophy-board aria-label="Categorias de troféus">
                ${analysis.categories.map(trophyTrackerCategoryMarkup).join("")}
                <p class="ncc-aaa-trophy-no-results" data-trophy-no-results hidden>Nenhum troféu encontrado com esse filtro.</p>
            </section>
        `;
    }

    function trophyTrackerToolsMarkup(analysis) {
        const total = Number(analysis?.totals?.total || 0);
        return `
            <article class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-trophy-tools ncc-aaa-trophy-tools-modern" aria-label="Filtros do Trophy Tracker">
                <div class="ncc-aaa-trophy-tools-head">
                    <div>
                        <p class="ncc-aaa-kicker">Busca</p>
                        <h2>Encontrar troféus</h2>
                    </div>
                    <span class="ncc-aaa-token" data-trophy-filter-summary>${escapeHtml(total)} visíveis</span>
                </div>
                <div class="ncc-aaa-trophy-tool-row ncc-aaa-trophy-tool-row-modern">
                    <label class="ncc-aaa-trophy-search-label">
                        <span>Buscar por nome</span>
                        <input type="search" data-trophy-search placeholder="Turmac, NeoQuest, Dice-A-Roo..." autocomplete="off">
                    </label>
                    <div class="ncc-aaa-trophy-filter-buttons" role="group" aria-label="Filtrar troféus por status">
                        <button class="ncc-aaa-btn ncc-aaa-btn-secondary is-active" type="button" data-action="trophy-set-filter" data-trophy-filter="all" aria-pressed="true">Todos</button>
                        <button class="ncc-aaa-btn ncc-aaa-btn-secondary" type="button" data-action="trophy-set-filter" data-trophy-filter="missing" aria-pressed="false">Faltando</button>
                        <button class="ncc-aaa-btn ncc-aaa-btn-secondary" type="button" data-action="trophy-set-filter" data-trophy-filter="upgradable" aria-pressed="false">Melhorar</button>
                        <button class="ncc-aaa-btn ncc-aaa-btn-secondary" type="button" data-action="trophy-set-filter" data-trophy-filter="owned" aria-pressed="false">Máximo</button>
                    </div>
                </div>
            </article>
        `;
    }

    function trophyTrackerCategoryMarkup(category) {
        const open = category.upgradable.length > 0 || category.missing.length < 12;
        const found = category.owned.length + category.upgradable.length;
        const pct = category.total ? Math.round((found / category.total) * 100) : 0;
        return `
            <details class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-trophy-category ncc-aaa-trophy-category-modern" data-trophy-category-card data-trophy-category-id="${escapeAttribute(category.id)}" ${open ? "open" : ""}>
                <summary>
                    <span class="ncc-aaa-trophy-category-title">${escapeHtml(category.title)}</span>
                    <span class="ncc-aaa-trophy-category-meter" aria-hidden="true"><i style="width:${escapeAttribute(String(pct))}%"></i></span>
                    <small>${escapeHtml(found)}/${escapeHtml(category.total)} encontrados · ${escapeHtml(category.upgradable.length)} melhorar · ${escapeHtml(category.missing.length)} faltando</small>
                </summary>
                ${category.upgradable.length ? trophyTrackerGroupMarkup("Podem melhorar", category.upgradable, "upgradable") : ""}
                ${category.missing.length ? trophyTrackerGroupMarkup("Faltando", category.missing, "missing") : ""}
                ${category.owned.length ? trophyTrackerGroupMarkup("Nível máximo", category.owned, "owned", true) : ""}
            </details>
        `;
    }

    function trophyTrackerGroupMarkup(title, items, tone, collapsed = false) {
        const body = `
            <div class="ncc-aaa-trophy-grid is-${escapeAttribute(tone)}">
                ${items.map(item => trophyTrackerItemMarkup(item, tone)).join("")}
            </div>
        `;
        if (collapsed) {
            return `
                <details class="ncc-aaa-trophy-group ncc-aaa-trophy-group-modern" data-trophy-group data-trophy-group-status="${escapeAttribute(tone)}">
                    <summary>${escapeHtml(title)} <small>${escapeHtml(items.length)} item(ns)</small></summary>
                    ${body}
                </details>
            `;
        }
        return `
            <section class="ncc-aaa-trophy-group ncc-aaa-trophy-group-modern" data-trophy-group data-trophy-group-status="${escapeAttribute(tone)}">
                <h3>${escapeHtml(title)} <small>${escapeHtml(items.length)} item(ns)</small></h3>
                ${body}
            </section>
        `;
    }

    function trophyTrackerItemMarkup(item, tone) {
        const image = tone === "missing" ? item.bestImage : (item.currentImage || item.bestImage);
        const target = tone === "upgradable" && item.bestImage && item.bestImage !== image ? `<img class="ncc-aaa-trophy-target" src="${escapeAttribute(item.bestImage)}" alt="" loading="lazy" decoding="async">` : "";
        const tier = item.tierKey || (tone === "missing" ? "missing" : "unknown");
        const tierLabel = tone === "missing" ? "meta" : (item.tierLabel || item.statusLabel || "nível");
        return `
            <article class="ncc-aaa-trophy-item is-${escapeAttribute(tone)} is-tier-${escapeAttribute(tier)}" data-trophy-item data-trophy-status="${escapeAttribute(item.status)}" data-trophy-name="${escapeAttribute(normalizeTrophySearchText(item.name))}" data-trophy-category="${escapeAttribute(item.categoryId)}" title="${escapeAttribute(item.name)}">
                <span class="ncc-aaa-trophy-item-badge">${escapeHtml(tierLabel)}</span>
                <div class="ncc-aaa-trophy-image-wrap">
                    <img src="${escapeAttribute(image)}" alt="" loading="lazy" decoding="async">
                    ${target}
                </div>
                <strong>${escapeHtml(item.name)}</strong>
                <span class="ncc-aaa-trophy-status-text">${escapeHtml(item.statusLabel)}</span>
            </article>
        `;
    }

    function isTrophyPrizesPage() {
        return location.pathname === TROPHY_TRACKER_PRIZES_PATH;
    }

    function bootTrophyTrackerOnPrizes(reason) {
        if (!isTrophyPrizesPage()) return null;
        try {
            const report = syncTrophyTrackerFromCurrentPage(reason || "boot");
            injectTrophyTrackerPrizesPanel(report);
            return report;
        } catch (error) {
            const message = `trophy-tracker:${errorToMessage(error)}`;
            bootErrors.push(message);
            lastTrophyTrackerReport = buildTrophyTrackerReport("boot-error", { status: "error", message });
            writeJson(TROPHY_TRACKER_REPORT_KEY, lastTrophyTrackerReport);
            return lastTrophyTrackerReport;
        }
    }

    function trophyTrackerStorageKey() {
        const playerId = normalizePlayerId(identity?.playerId || state?.playerId || "") || normalizeToken(identity?.playerId || state?.playerId || identity?.username || "") || "local";
        return `${TROPHY_TRACKER_STORAGE_KEY_PREFIX}.${playerId}`;
    }

    function loadTrophyTrackerState() {
        const fallback = {
            schema: TROPHY_TRACKER_STATE_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "trophy-tracker",
            sourceMaterial: `${TROPHY_TRACKER_SOURCE_NAME} v${TROPHY_TRACKER_SOURCE_VERSION}`,
            playerId: identity?.playerId || state?.playerId || "local",
            username: identity?.username || state?.username || "",
            ownedImages: [],
            updatedAt: "",
            sourcePage: "",
            lastStatus: "Aguardando leitura da página de troféus."
        };
        const raw = readJson(trophyTrackerStorageKey(), null);
        if (!isObject(raw)) return fallback;
        const ownedImages = Array.isArray(raw.ownedImages) ? Array.from(new Set(raw.ownedImages.map(normalizeTrophyImageUrl).filter(Boolean))).sort() : [];
        return {
            ...fallback,
            ...raw,
            version: VERSION,
            ownedImages,
            playerId: raw.playerId || fallback.playerId,
            username: raw.username || fallback.username
        };
    }

    function saveTrophyTrackerState(nextState) {
        const stateToSave = {
            ...loadTrophyTrackerState(),
            ...(isObject(nextState) ? nextState : {}),
            schema: TROPHY_TRACKER_STATE_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "trophy-tracker",
            sourceMaterial: `${TROPHY_TRACKER_SOURCE_NAME} v${TROPHY_TRACKER_SOURCE_VERSION}`,
            playerId: identity?.playerId || state?.playerId || "local",
            username: identity?.username || state?.username || "",
            updatedAt: nextState?.updatedAt || isoNow()
        };
        stateToSave.ownedImages = Array.from(new Set((stateToSave.ownedImages || []).map(normalizeTrophyImageUrl).filter(Boolean))).sort();
        writeJson(trophyTrackerStorageKey(), stateToSave);
        return stateToSave;
    }

    function normalizeTrophyImageUrl(value) {
        const raw = String(value || "").trim();
        if (!raw) return "";
        try {
            const url = new URL(raw.replace(/^http:\/\//i, "https://"), location.origin);
            const host = String(url.hostname || "").toLowerCase().replace(/^www\./, "");
            const path = String(url.pathname || "").replace(/\\/g, "/");
            if (host === "images.neopets.com" || /(^|\.)neopets\.com$/i.test(host)) {
                const trophyPathMatch = path.match(/\/trophies\/[^?#\s]+/i);
                if (trophyPathMatch) return `https://images.neopets.com${trophyPathMatch[0]}`;
            }
            return `${url.protocol}//${url.hostname}${path}`.split("?")[0].split("#")[0];
        } catch (_) {
            return raw.split("?")[0].split("#")[0].replace(/^\/\//, "https://");
        }
    }

    function trophyImageKey(value) {
        const normalized = normalizeTrophyImageUrl(value);
        const match = normalized.match(/\/trophies\/([^/?#]+)$/i);
        return match ? match[1].toLowerCase() : normalized.toLowerCase();
    }

    function normalizeTrophySearchText(value) {
        return String(value || "")
            .normalize("NFD")
            .replace(/[̀-ͯ]/g, "")
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, " ")
            .trim();
    }

    function collectOwnedTrophyImagesFromCurrentPage() {
        const selectors = [
            '[width="600"] img[src*="/trophies/"]',
            '.content center img[src*="/trophies/"]',
            'img[src*="images.neopets.com/trophies/"]',
            'img[src*="/trophies/"]'
        ];
        const found = [];
        selectors.forEach(selector => {
            document.querySelectorAll(selector).forEach(img => {
                const normalized = normalizeTrophyImageUrl(img.getAttribute("src") || img.src || "");
                if (normalized && /\/trophies\//i.test(normalized)) found.push(normalized);
            });
        });
        return Array.from(new Set(found)).sort();
    }

    function syncTrophyTrackerFromCurrentPage(reason) {
        const ownedImages = collectOwnedTrophyImagesFromCurrentPage();
        const stateData = saveTrophyTrackerState({
            reason: reason || "page-scan",
            sourcePage: location.href,
            ownedImages,
            lastStatus: ownedImages.length ? `${ownedImages.length} imagem(ns) de troféu lida(s) em /prizes.phtml.` : "Nenhum troféu visível foi encontrado na página."
        });
        const report = buildTrophyTrackerReport(reason || "page-scan", { state: stateData, status: ownedImages.length ? "synced" : "empty-page" });
        lastTrophyTrackerReport = report;
        writeJson(TROPHY_TRACKER_REPORT_KEY, report);
        return report;
    }

    function buildTrophyTrackerReport(reason, extra = {}) {
        const stateData = extra.state || loadTrophyTrackerState();
        const analysis = analyzeTrophyTrackerCatalog(stateData.ownedImages || []);
        return {
            schema: TROPHY_TRACKER_REPORT_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "trophy-tracker",
            sourceMaterial: `${TROPHY_TRACKER_SOURCE_NAME} v${TROPHY_TRACKER_SOURCE_VERSION}`,
            sourceCredits: {
                name: TROPHY_TRACKER_SOURCE_NAME,
                version: TROPHY_TRACKER_SOURCE_VERSION,
                author: TROPHY_TRACKER_SOURCE_AUTHOR,
                license: TROPHY_TRACKER_SOURCE_LICENSE,
                homepage: TROPHY_TRACKER_SOURCE_HOMEPAGE
            },
            reason: reason || "report",
            status: extra.status || (stateData.ownedImages?.length ? "ready" : "needs-prizes-page"),
            updatedAt: isoNow(),
            storageKey: trophyTrackerStorageKey(),
            prizesPage: TROPHY_TRACKER_PRIZES_URL,
            ownedImageCount: Array.isArray(stateData.ownedImages) ? stateData.ownedImages.length : 0,
            ownedImageKeyCount: Array.isArray(stateData.ownedImages) ? new Set(stateData.ownedImages.map(trophyImageKey).filter(Boolean)).size : 0,
            analysis,
            safety: {
                readOnly: true,
                networkAccessAdded: false,
                formSubmit: false,
                officialActionAvailable: false,
                pageRequiredForScan: TROPHY_TRACKER_PRIZES_PATH
            }
        };
    }

    function trophyTrackerTierKey(item) {
        const index = Number(item?.trophyIndex ?? -1);
        const images = Array.isArray(item?.images) ? item.images : [];
        if (index < 0 || !images.length) return "";
        if (images.length >= 3) {
            if (index === 0) return "bronze";
            if (index === 1) return "silver";
            return "gold";
        }
        if (images.length === 2) return index === 0 ? "silver" : "gold";
        if (images.length === 1) return "unique";
        return "unknown";
    }

    function trophyTrackerTierLabel(tierKey) {
        const labels = { bronze: "bronze", silver: "prata", gold: "ouro", unique: "único", unknown: "nível especial" };
        return labels[tierKey] || "";
    }

    function analyzeTrophyTrackerCatalog(ownedImages) {
        const ownedSet = new Set((ownedImages || []).map(trophyImageKey).filter(Boolean));
        const categories = TROPHY_TRACKER_CATEGORIES.map(category => {
            const missing = [];
            const upgradable = [];
            const owned = [];
            (category.trophies || []).forEach(trophy => {
                const item = classifyTrophyTrackerItem(trophy, ownedSet, category);
                if (item.status === "owned") owned.push(item);
                else if (item.status === "upgradable") upgradable.push(item);
                else missing.push(item);
            });
            return {
                id: category.id,
                title: category.title,
                shortTitle: category.shortTitle,
                total: (category.trophies || []).length,
                missing,
                upgradable,
                owned
            };
        });
        const totals = categories.reduce((acc, category) => {
            acc.total += category.total;
            acc.missing += category.missing.length;
            acc.upgradable += category.upgradable.length;
            acc.owned += category.owned.length;
            [...category.owned, ...category.upgradable].forEach(item => {
                const tierKey = item.tierKey || trophyTrackerTierKey(item);
                if (tierKey === "bronze") acc.bronze += 1;
                else if (tierKey === "silver") acc.silver += 1;
                else if (tierKey === "gold") acc.gold += 1;
                else if (tierKey === "unique") acc.unique += 1;
                else if (tierKey) acc.special += 1;
            });
            return acc;
        }, { total: 0, missing: 0, upgradable: 0, owned: 0, acquired: 0, acquiredPct: 0, bestPct: 0, bronze: 0, silver: 0, gold: 0, unique: 0, special: 0 });
        totals.acquired = totals.owned + totals.upgradable;
        totals.acquiredPct = totals.total ? Math.round((totals.acquired / totals.total) * 100) : 0;
        totals.bestPct = totals.total ? Math.round((totals.owned / totals.total) * 100) : 0;
        return { totals, categories, normalization: { imageKeyMatching: true, urlNormalization: "neopets-trophy-path" }, tierPolicy: "3-image trophies count as bronze/prata/ouro; 2-image trophies count as prata/ouro; 1-image trophies count as únicos" };
    }

    function classifyTrophyTrackerItem(trophy, ownedSet, category) {
        const images = Array.isArray(trophy.images) ? trophy.images.map(normalizeTrophyImageUrl).filter(Boolean) : [];
        let trophyIndex = -1;
        images.forEach((src, index) => {
            if (ownedSet.has(trophyImageKey(src))) trophyIndex = index;
        });
        const bestIndex = Math.max(0, images.length - 1);
        const status = trophyIndex === bestIndex ? "owned" : (trophyIndex > -1 ? "upgradable" : "missing");
        const statusLabel = status === "owned" ? "nível máximo" : (status === "upgradable" ? "tem, pode melhorar" : "faltando");
        const tierKey = trophyTrackerTierKey({ trophyIndex, images });
        const tierLabel = trophyTrackerTierLabel(tierKey);
        return {
            name: String(trophy.name || "Troféu sem nome"),
            categoryId: category.id,
            categoryTitle: category.title,
            images,
            trophyIndex,
            tierKey,
            tierLabel,
            currentImage: trophyIndex > -1 ? images[trophyIndex] : "",
            bestImage: images[bestIndex] || images[0] || "",
            status,
            statusLabel
        };
    }

    function injectTrophyTrackerPrizesPanel(report) {
        if (document.getElementById("ncc-aaa-trophy-prizes-panel")) return;
        const target = document.querySelector(".content center") || document.querySelector("td.content") || document.body;
        if (!target) return;
        const panel = document.createElement("div");
        panel.id = "ncc-aaa-trophy-prizes-panel";
        panel.className = "ncc-aaa-trophy-prizes-panel";
        panel.innerHTML = `
            <div class="ncc-aaa-trophy-prizes-panel-inner">
                <strong>NCC AAA · Trophy Tracker</strong>
                <span>${escapeHtml(report.ownedImageCount)} imagem(ns) de troféu lida(s) nesta página.</span>
                <a href="${escapeAttribute(TROPHY_TRACKER_ENTRY_URL)}" target="_blank" rel="noopener noreferrer">Abrir Trophy Tracker</a>
            </div>
        `;
        const style = document.createElement("style");
        style.textContent = `
            #ncc-aaa-trophy-prizes-panel{max-width:760px;margin:16px auto;padding:10px;border:2px solid #154870;border-radius:14px;background:#f7fcff;color:#0f2d45;font:12px Verdana,Arial,sans-serif;box-shadow:0 5px 16px rgba(0,0,0,.12)}
            #ncc-aaa-trophy-prizes-panel .ncc-aaa-trophy-prizes-panel-inner{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:8px}
            #ncc-aaa-trophy-prizes-panel strong{font-size:13px;color:#073d63}
            #ncc-aaa-trophy-prizes-panel a{display:inline-flex;align-items:center;border-radius:999px;padding:6px 10px;background:#154870;color:#fff;text-decoration:none;font-weight:900}
        `;
        document.head.appendChild(style);
        target.appendChild(panel);
    }

    function copyTrophyTrackerSummary() {
        const report = buildTrophyTrackerReport("copy-summary");
        const lines = [
            `NCC AAA · Trophy Tracker`,
            `Atualizado: ${report.updatedAt}`,
            `Total no catálogo: ${report.analysis.totals.total}`,
            `Encontrados: ${report.analysis.totals.acquired}/${report.analysis.totals.total} (${report.analysis.totals.acquiredPct}%)`,
            `Bronze: ${report.analysis.totals.bronze || 0}`,
            `Prata: ${report.analysis.totals.silver || 0}`,
            `Ouro: ${report.analysis.totals.gold || 0}`,
            `Nível máximo: ${report.analysis.totals.owned}`,
            `Podem melhorar: ${report.analysis.totals.upgradable}`,
            `Faltando: ${report.analysis.totals.missing}`,
            `Catálogo: ${TROPHY_TRACKER_SOURCE_NAME} v${TROPHY_TRACKER_SOURCE_VERSION} · ${TROPHY_TRACKER_SOURCE_LICENSE}`,
            "",
            ...report.analysis.categories.map(category => `${category.title}: ${category.owned.length + category.upgradable.length}/${category.total} encontrados · ${category.owned.length} nível máximo · ${category.upgradable.length} melhorar · ${category.missing.length} faltando`)
        ];
        copyText(lines.join("\n"));
    }

    function openTrophyTrackerPopup() {
        const report = buildTrophyTrackerReport("popup");
        const win = window.open("", "NCCAAATrophyTracker");
        if (!win) return;
        win.document.title = "NCC AAA · Trophy Tracker";
        win.document.body.innerHTML = `
            <style>
                body{max-width:1180px;margin:auto;padding:18px;font:13px Verdana,Arial,sans-serif;text-align:center;background:#f8fdff;color:#102a43}
                h1{color:#154870} details{margin:16px 0;padding:12px;border:1px solid #bddcf1;border-radius:14px;background:white;text-align:left} summary{cursor:pointer;font-weight:900;color:#154870}
                .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:10px;margin:10px 0}.item{text-align:center;border:1px solid #d8eaf6;border-radius:12px;padding:8px;background:#fbfeff;min-height:126px}.item img{width:50px;height:50px;object-fit:contain}.missing img{opacity:.45}.item strong{display:block;font-size:11px;margin-top:6px}.item span{display:block;font-size:10px;color:#54708a;margin-top:3px}.stats{display:flex;gap:8px;justify-content:center;flex-wrap:wrap}.stats span{border-radius:999px;padding:7px 10px;background:#e4f6ff;color:#154870;font-weight:900}
            </style>
            <h1>NCC AAA · Trophy Tracker</h1>
            <p>Resumo local gerado a partir da página de troféus.</p>
            <p style="font-size:11px;color:#54708a">Catálogo adaptado de ${escapeHtml(TROPHY_TRACKER_SOURCE_NAME)} v${escapeHtml(TROPHY_TRACKER_SOURCE_VERSION)} · ${escapeHtml(TROPHY_TRACKER_SOURCE_LICENSE)}.</p>
            <div class="stats"><span>${escapeHtml(`${report.analysis.totals.acquiredPct}% progresso`)}</span><span>${escapeHtml(report.analysis.totals.bronze || 0)} bronze</span><span>${escapeHtml(report.analysis.totals.silver || 0)} prata</span><span>${escapeHtml(report.analysis.totals.gold || 0)} ouro</span><span>${escapeHtml(report.analysis.totals.upgradable)} melhorar</span><span>${escapeHtml(report.analysis.totals.missing)} faltando</span></div>
            ${report.analysis.categories.map(category => `
                <details ${category.upgradable.length ? "open" : ""}>
                    <summary>${escapeHtml(category.title)} · ${escapeHtml(category.owned.length)} conquistados · ${escapeHtml(category.upgradable.length)} melhorar · ${escapeHtml(category.missing.length)} faltando</summary>
                    ${[...category.upgradable, ...category.missing, ...category.owned].map(item => `<div class="item ${escapeAttribute(item.status)}"><img src="${escapeAttribute(item.currentImage || item.bestImage)}"><strong>${escapeHtml(item.name)}</strong><span>${escapeHtml(item.statusLabel)}</span></div>`).join("") ? `<div class="grid">${[...category.upgradable, ...category.missing, ...category.owned].map(item => `<div class="item ${escapeAttribute(item.status)}"><img src="${escapeAttribute(item.currentImage || item.bestImage)}"><strong>${escapeHtml(item.name)}</strong><span>${escapeHtml(item.statusLabel)}</span></div>`).join("")}</div>` : ""}
                </details>
            `).join("")}
        `;
        win.document.close();
    }

    function diagnosticViewMarkup() {
        const diagnostic = buildDiagnostic();
        const health = buildHealthReport("diagnostic-view");
        const backstage = buildBackstageReport("diagnostic-view");
        return `
            <section class="ncc-aaa-backstage-view" aria-label="Bastidores técnicos do NCC AAA">
                <article class="ncc-aaa-backstage-hero ncc-aaa-card ncc-aaa-card-wide">
                    <div class="ncc-aaa-backstage-host" aria-hidden="true">
                        <img src="https://images.neopets.com/games/aaa/dailydare/2012/popups/aaa.png" alt="" loading="lazy" decoding="async">
                    </div>
                    <div class="ncc-aaa-backstage-copy">
                        <p class="ncc-aaa-kicker">Bastidores técnicos</p>
                        <h2>Oficina de controle do NCC AAA</h2>
                        <p>Esta área guarda a parte técnica da fundação: montagem, bridge, storage, contrato externo, Base Entry e segurança. A home continua temática; os detalhes completos ficam aqui, sem adicionar módulos de jogo.</p>
                        <div class="ncc-aaa-backstage-badges" aria-label="Resumo técnico">
                            <span>${escapeHtml(backstage.statusLabel)}</span>
                            <span>${escapeHtml(backstage.moduleLabel)}</span>
                            <span>${escapeHtml(backstage.safetyLabel)}</span>
                            <span>${escapeHtml(backstage.bridgeLabel)}</span>
                        </div>
                    </div>
                </article>

                <section class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-backstage-health">
                    <p class="ncc-aaa-kicker">Painel de saúde</p>
                    <h2>Resumo rápido</h2>
                    <div class="ncc-aaa-health-grid" aria-label="Resumo de saúde do NCC AAA">
                        ${healthSummaryCardsMarkup(health)}
                    </div>
                    <div class="ncc-aaa-diagnostic-actions">
                        <button class="ncc-aaa-button" type="button" data-action="copy-diagnostic">Copiar diagnóstico</button>
                        <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="copy-health">Copiar relatório de saúde</button>
                        <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="save-health-report">Salvar relatório local</button>
                        <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="copy-base-entry">Copiar contrato Base</button>
                        <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="refresh">Republicar estado</button>
                    </div>
                </section>

                <section class="ncc-aaa-backstage-grid" aria-label="Relatórios técnicos resumidos">
                    ${backstagePanelsMarkup(backstage)}
                </section>

                <details class="ncc-aaa-card ncc-aaa-card-wide ncc-aaa-backstage-json">
                    <summary>JSON completo do diagnóstico</summary>
                    <p>Relatório integral preservado para auditoria, reprodução por IA e comparação com o NCC Base.</p>
                    <pre class="ncc-aaa-diagnostic"><code>${escapeHtml(JSON.stringify(diagnostic, null, 2))}</code></pre>
                </details>
            </section>
        `;
    }

    function backstagePanelsMarkup(report) {
        const panels = [
            {
                icon: "🎭",
                title: "Montagem",
                tone: report.mount.ok ? "ok" : "warn",
                status: report.mount.ok ? "ok" : "atenção",
                lines: [
                    ["Página", report.mount.currentPath],
                    ["Alvo", report.mount.targetFound ? "td.content encontrado" : "alvo ausente"],
                    ["Shell", report.mount.rootExists ? "root ativo" : "root ausente"],
                    ["Preservação", "header/sidebar/footer"]
                ]
            },
            {
                icon: "📡",
                title: "Bridge",
                tone: report.bridge.ready ? "ok" : "warn",
                status: report.bridge.ready ? "pronta" : "atenção",
                lines: [
                    ["Nome", report.bridge.bridgeName],
                    ["Métodos", `${report.bridge.methodCount}`],
                    ["External", report.bridge.globalExternalState ? "global ativo" : "sem global"],
                    ["Eventos", report.bridge.events.join(", ")]
                ]
            },
            {
                icon: "🗃️",
                title: "Storage",
                tone: report.storage.available ? "ok" : "warn",
                status: report.storage.available ? "isolado" : "atenção",
                lines: [
                    ["Namespace", report.storage.namespace],
                    ["Estado", report.storage.hasState ? "salvo" : "aguardando"],
                    ["Revision", `${report.storage.revision || 0}`],
                    ["Sync", report.storage.lastStorageSyncAt ? "multi-aba" : "local"],
                    ["External State", report.storage.hasExternalState ? "salvo" : "ausente"]
                ]
            },
            {
                icon: "🛡️",
                title: "Segurança",
                tone: report.safety.safe ? "ok" : "warn",
                status: report.safety.safe ? "visual-only" : "atenção",
                lines: [
                    ["Módulos", String(GAME_MODULE_REGISTRY.length)],
                    ["Rede", report.safety.networkAccess ? "ativa" : "nenhuma"],
                    ["Clique oficial", report.safety.clickAutomation ? "ativo" : "nenhum"],
                    ["Submit/teclado", report.safety.formSubmit || report.safety.keyboardAutomation ? "ativo" : "nenhum"]
                ]
            },
            {
                icon: "🔗",
                title: "Base Entry",
                tone: report.baseEntry.ready ? "ok" : "warn",
                status: report.baseEntry.ready ? "pronto" : "atenção",
                lines: [
                    ["Consumer futuro", "NCC Base"],
                    ["Categoria", "Jogos"],
                    ["Mirror", "external-state"],
                    ["Card", report.baseEntry.suggestedCardTitle]
                ]
            },
            {
                icon: "🧩",
                title: "Core",
                tone: report.coreOrganization?.sectionCount >= 10 ? "ok" : "warn",
                status: report.coreOrganization?.sectionCount >= 10 ? "organizado" : "atenção",
                lines: [
                    ["Áreas", `${report.coreOrganization?.sectionCount || 0}`],
                    ["Arquivo", report.coreOrganization?.fileMode || "single-userscript"],
                    ["Registry", `${report.coreOrganization?.gameModuleRegistry?.total || 0} módulos`],
                    ["Build", report.coreOrganization?.buildSystemRequired ? "externo" : "não necessário"]
                ]
            },
            {
                icon: "🧪",
                title: "Autoteste",
                tone: report.selfTest.passed ? "ok" : "warn",
                status: report.selfTest.passed ? "passou" : "avisos",
                lines: [
                    ["Checks", `${report.selfTest.passedCount}/${report.selfTest.total}`],
                    ["Falhas", `${report.selfTest.failedCount}`],
                    ["Último erro", report.errorCount ? `${report.errorCount} erro(s)` : "nenhum"],
                    ["Fase", "14 core organization"]
                ]
            }
        ];

        return panels.map(panel => `
            <article class="ncc-aaa-backstage-panel is-${escapeAttribute(panel.tone)}">
                <div class="ncc-aaa-backstage-panel-head">
                    <span class="ncc-aaa-backstage-icon" aria-hidden="true">${escapeHtml(panel.icon)}</span>
                    <div>
                        <h3>${escapeHtml(panel.title)}</h3>
                        <small>${escapeHtml(panel.status)}</small>
                    </div>
                </div>
                <dl class="ncc-aaa-backstage-list">
                    ${panel.lines.map(([label, value]) => `
                        <div>
                            <dt>${escapeHtml(label)}</dt>
                            <dd>${escapeHtml(value)}</dd>
                        </div>
                    `).join("")}
                </dl>
            </article>
        `).join("");
    }

    function healthSummaryCardsMarkup(health) {
        const cards = [
            { label: "Montagem", value: health.summary.mountStatus, detail: health.mount.targetSelector || "td.content" },
            { label: "Bridge", value: health.summary.bridgeStatus, detail: `${health.bridge.methodCount} métodos` },
            { label: "Storage", value: health.summary.storageStatus, detail: health.storage.available ? "localStorage disponível" : "localStorage indisponível" },
            { label: "Segurança", value: health.summary.safetyStatus, detail: "visual-only" },
            { label: "Core", value: health.summary.coreOrganizationStatus || "ok", detail: `${health.coreOrganization?.sectionCount || 0} áreas` },
            { label: "Módulos", value: String(GAME_MODULE_REGISTRY.length), detail: "Cellblock + Kiko + Tyranu + Kiss + Godori + NeoQuest I/II" },
            { label: "Erros", value: String(health.summary.errorCount), detail: health.summary.errorCount ? "ver JSON" : "sem erros ativos" }
        ];
        return cards.map(card => `
            <article class="ncc-aaa-health-card">
                <span>${escapeHtml(card.label)}</span>
                <strong>${escapeHtml(card.value)}</strong>
                <small>${escapeHtml(card.detail)}</small>
            </article>
        `).join("");
    }

    function recoveryMarkup(message) {
        return `
            <section class="ncc-aaa-shell ncc-aaa-recovery">
                <article class="ncc-aaa-card ncc-aaa-card-wide">
                    <p class="ncc-aaa-kicker">NCC AAA</p>
                    <h2>Erro visual recuperável</h2>
                    <p>${escapeHtml(message || "Falha desconhecida.")}</p>
                    <button class="ncc-aaa-button" type="button" data-action="switch-view" data-view-id="dashboard">Voltar ao Painel</button>
                    <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="copy-diagnostic">Copiar diagnóstico</button>
                </article>
            </section>
        `;
    }

    function getAvatarModalElement() {
        return root ? root.querySelector("[data-ncc-aaa-avatar-modal]") : null;
    }

    function openAvatarModal() {
        const modal = getAvatarModalElement();
        if (!modal) return;
        modal.hidden = false;
        modal.setAttribute("data-opened-at", isoNow());
        const selected = modal.querySelector(".ncc-aaa-avatar-option.is-selected") || modal.querySelector(".ncc-aaa-avatar-modal-close");
        if (selected && typeof selected.focus === "function") {
            try { selected.focus({ preventScroll: true }); } catch (_) { selected.focus(); }
        }
    }

    function closeAvatarModal() {
        const modal = getAvatarModalElement();
        if (!modal) return;
        modal.hidden = true;
    }



    function battledomeAssistedTestMarkup(battledomeState) {
        const checklist = buildBattledomeAssistedTestChecklist("arena-view");
        const progress = checklist.progress || {};
        const allDone = progress.pending === 0 && progress.failed === 0 && progress.passed > 0;
        const summary = allDone ? `validado · ${progress.passed}/${progress.total}` : `${progress.passed || 0} ok · ${progress.failed || 0} falha(s) · ${progress.pending || 0} pendente(s)`;
        return `
            <details class="ncc-aaa-bd-test-panel" ${allDone ? "" : "open"}>
                <summary><span>Teste assistido Battledome</span><small>${escapeHtml(summary)}</small></summary>
                <p class="ncc-aaa-bd-test-intro">Use este checklist durante o teste real no Neopets. Ele salva apenas o status local de cada item; não interfere em luta, prêmio, oponente, arma ou formulário.</p>
                <div class="ncc-aaa-bd-test-env">
                    <span>Página atual: <b>${escapeHtml(checklist.environment?.currentPath || location.pathname)}</b></span>
                    <span>Oponentes DOM: <b>${escapeHtml(checklist.environment?.detectedOpponentRows || 0)}</b></span>
                    <span>Prêmios DOM: <b>${escapeHtml(checklist.environment?.detectedPrizeNames || 0)}</b></span>
                    <span>Armas DOM: <b>${escapeHtml(checklist.environment?.detectedWeaponImages || 0)}</b></span>
                </div>
                <div class="ncc-aaa-bd-test-progress" aria-label="Progresso do teste Battledome">
                    <span class="is-pass">OK: ${escapeHtml(progress.passed || 0)}</span>
                    <span class="is-fail">Falhas: ${escapeHtml(progress.failed || 0)}</span>
                    <span class="is-skip">Pulados: ${escapeHtml(progress.skipped || 0)}</span>
                    <span class="is-pending">Pendentes: ${escapeHtml(progress.pending || 0)}</span>
                </div>
                <div class="ncc-aaa-bd-test-sections">
                    ${checklist.sections.map(section => `
                        <section class="ncc-aaa-bd-test-section">
                            <h3>${escapeHtml(section.title)}</h3>
                            <p>${escapeHtml(section.description || "")}</p>
                            <div class="ncc-aaa-bd-test-step-list">
                                ${section.steps.map(step => `
                                    <article class="ncc-aaa-bd-test-step is-${escapeAttribute(step.status)}">
                                        <div><strong>${escapeHtml(step.label)}</strong><span>${escapeHtml(step.expected)}</span></div>
                                        <div class="ncc-aaa-bd-test-step-actions">
                                            <button type="button" class="ncc-aaa-bd-test-step-button is-pass" data-action="bd-test-step-status" data-test-step-id="${escapeAttribute(step.id)}" data-test-status="pass">OK</button>
                                            <button type="button" class="ncc-aaa-bd-test-step-button is-fail" data-action="bd-test-step-status" data-test-step-id="${escapeAttribute(step.id)}" data-test-status="fail">Falhou</button>
                                            <button type="button" class="ncc-aaa-bd-test-step-button is-skip" data-action="bd-test-step-status" data-test-step-id="${escapeAttribute(step.id)}" data-test-status="skip">Pular</button>
                                            <button type="button" class="ncc-aaa-bd-test-step-button is-pending" data-action="bd-test-step-status" data-test-step-id="${escapeAttribute(step.id)}" data-test-status="pending">Pendente</button>
                                        </div>
                                    </article>
                                `).join("")}
                            </div>
                        </section>
                    `).join("")}
                </div>
                <div class="ncc-aaa-actions ncc-aaa-bd-test-actions">
                    <button class="ncc-aaa-button" type="button" data-action="bd-test-copy-report">Copiar relatório do teste</button>
                    <button class="ncc-aaa-button ncc-aaa-button-muted" type="button" data-action="bd-test-reset">Resetar checklist</button>
                </div>
                <div class="ncc-aaa-safe-note">Critério de aprovação: favoritos sobem ao topo sem escolher oponente, logger só registra recompensa já visível, loadout pré-seleciona até 2 armas e nenhum Fight/submit/POST acontece automaticamente.</div>
            </details>`;
    }
    function handleClick(event) {
        const button = event.target.closest("[data-action]");
        if (!button || !root || !root.contains(button)) return;
        const action = button.dataset.action;

        if (action === "open-avatar-modal") {
            openAvatarModal();
            return;
        }

        if (action === "close-avatar-modal") {
            closeAvatarModal();
            return;
        }

        if (action === "switch-view") {
            const viewId = normalizeViewId(button.dataset.viewId);
            if (!viewId) return;
            commitState("switch-view", () => ({ view: viewId }));
            return;
        }

        if (action === "select-avatar") {
            const avatarId = normalizeAvatarId(button.dataset.avatarId);
            if (!avatarId) return;
            commitState("select-avatar", () => ({ avatarId }));
            return;
        }

        if (action === "copy-diagnostic") {
            copyDiagnostic();
            return;
        }

        if (action === "copy-health") {
            copyHealthReport();
            return;
        }

        if (action === "save-health-report") {
            saveHealthReport("manual-save");
            return;
        }

        if (action === "copy-base-entry") {
            copyBaseEntryContract();
            return;
        }

        if (action === "refresh") {
            publishExternalState("manual-refresh");
            writeBaseEntryContract("manual-refresh");
            writeBaseEntryReadiness("manual-refresh");
            render({ reason: "manual-refresh", forceFull: true });
            return;
        }

        if (action === "bd-copy-log") {
            copyBattledomeCurrentLog();
            return;
        }

        if (action === "bd-toggle-prize-paused") {
            toggleBattledomePrizePaused();
            return;
        }

        if (action === "bd-clear-log") {
            clearBattledomePrizes();
            return;
        }

        if (action === "bd-clear-opponents") {
            clearBattledomeOpponentsAndFavorites();
            return;
        }

        if (action === "bd-toggle-loadout-enabled") {
            toggleBattledomeLoadoutEnabled();
            return;
        }

        if (action === "bd-apply-loadout") {
            applyBattledomeFavoriteLoadout({ force: true });
            return;
        }

        if (action === "bd-clear-loadout") {
            clearBattledomeLoadoutWeapons();
            return;
        }

        if (action === "bd-test-step-status") {
            setBattledomeAssistedTestStepStatus(button.dataset.testStepId, button.dataset.testStatus);
            return;
        }

        if (action === "bd-test-copy-report") {
            copyBattledomeAssistedTestReport();
            return;
        }

        if (action === "bd-test-reset") {
            resetBattledomeAssistedTestChecklist();
            return;
        }


        if (action === "trophy-set-filter") {
            const filter = button.dataset.trophyFilter || "all";
            root.querySelectorAll("[data-trophy-filter]").forEach(control => {
                control.classList.toggle("is-active", control.dataset.trophyFilter === filter);
                control.setAttribute("aria-pressed", control.dataset.trophyFilter === filter ? "true" : "false");
            });
            applyTrophyTrackerUiFilters();
            return;
        }

        if (action === "trophy-copy-summary") {
            copyTrophyTrackerSummary();
            return;
        }

        if (action === "trophy-open-popup") {
            openTrophyTrackerPopup();
            return;
        }

        if (action === "reset-visual") {
            resetVisualPreferences();
        }
    }

    function handleInput(event) {
        if (!root || !root.contains(event.target)) return;
        if (event.target.closest("[data-trophy-search]")) {
            applyTrophyTrackerUiFilters();
        }
    }

    function applyTrophyTrackerUiFilters() {
        if (!root) return;
        const board = root.querySelector("[data-trophy-board]");
        if (!board) return;
        const searchControl = root.querySelector("[data-trophy-search]");
        const query = normalizeTrophySearchText(searchControl ? searchControl.value : "");
        const activeFilter = root.querySelector("[data-trophy-filter].is-active")?.dataset?.trophyFilter || "all";
        const items = Array.from(board.querySelectorAll("[data-trophy-item]"));
        let visibleCount = 0;
        items.forEach(item => {
            const status = item.dataset.trophyStatus || "";
            const name = item.dataset.trophyName || "";
            const statusOk = activeFilter === "all" || status === activeFilter;
            const searchOk = !query || name.includes(query);
            const visible = statusOk && searchOk;
            item.hidden = !visible;
            if (visible) visibleCount += 1;
        });
        board.querySelectorAll("[data-trophy-group]").forEach(group => {
            const hasVisible = Boolean(group.querySelector("[data-trophy-item]:not([hidden])"));
            group.hidden = !hasVisible;
        });
        board.querySelectorAll("[data-trophy-category-card]").forEach(category => {
            const hasVisible = Boolean(category.querySelector("[data-trophy-item]:not([hidden])"));
            category.hidden = !hasVisible;
            if (hasVisible && query && !category.open) category.open = true;
        });
        const noResults = board.querySelector("[data-trophy-no-results]");
        if (noResults) noResults.hidden = visibleCount > 0;
        const summary = root.querySelector("[data-trophy-filter-summary]");
        if (summary) {
            const total = items.length;
            const label = activeFilter === "all" ? "todos" : (activeFilter === "missing" ? "faltando" : (activeFilter === "upgradable" ? "podem melhorar" : "nível máximo"));
            summary.textContent = `${visibleCount}/${total} visíveis · ${label}`;
        }
    }

    function handleChange(event) {
        if (root && root.contains(event.target)) {
            const bdSetting = event.target.closest("[data-bd-setting]");
            if (bdSetting) {
                setBattledomePrizeSetting(bdSetting.dataset.bdSetting, bdSetting.value);
                return;
            }
        }
        const control = event.target.closest("[data-action]");
        if (!control || !root || !root.contains(control)) return;
        const action = control.dataset.action;

        if (action === "select-team") {
            const teamId = normalizeTeamId(control.value);
            if (!teamId) return;
            commitState("select-team", () => ({ teamId }));
            return;
        }

        if (action === "select-theme") {
            const themeId = normalizeThemeId(control.value);
            if (!themeId) return;
            commitState("select-theme", () => ({ themeId }));
        }
    }

    function commitState(reason, updater, options) {
        const commitReason = reason || "state-change";
        const now = isoNow();
        const previous = hydrateStateForIdentity(state || {}, identity || detectIdentity(), now, "previous-snapshot");
        const patch = typeof updater === "function" ? updater(clonePlain(previous)) : (isObject(updater) ? updater : {});
        const next = hydrateStateForIdentity({
            ...previous,
            ...(isObject(patch) ? patch : {}),
            revision: normalizeRevision(previous.revision) + 1,
            sessionId: SESSION_ID,
            lastCommitReason: commitReason,
            updatedAt: now
        }, identity, now, commitReason);

        state = next;
        saveState(state, commitReason, options || {});
        render({ reason: commitReason, previous });
        publishExternalState(commitReason);
        return clonePlain(state);
    }

    function buildExternalState(reason) {
        const selectedAvatar = getAvatar(state?.avatarId);
        const selectedTeam = getTeam(state?.teamId);
        const selectedTheme = getTheme(state?.themeId);
        const now = isoNow();
        const publicWindow = getPublicWindow();
        const bridgeReady = Boolean(window[GLOBAL_BRIDGE_NAME] || publicWindow?.[GLOBAL_BRIDGE_NAME]);
        const pageMounted = Boolean(root);
        const stateId = `ncc-aaa:${VERSION}:${state?.playerId || identity?.playerId || "local"}:${Date.now()}`;
        return clonePlain({
            schema: EXTERNAL_STATE_SCHEMA,
            schemaVersion: 1,
            source: "ncc-aaa",
            sourceScript: "NCC AAA",
            sourceModule: "foundation",
            version: VERSION,
            reason: reason || "unknown",
            stateId,
            updatedAt: now,
            lastPublishedAt: lastPublishedAt || null,
            mountPage: MOUNT_PATH,
            entryPoint: ENTRY_POINT,
            status: "ready-kiss-the-mortog-module-install-active",
            ready: true,
            active: true,
            muted: false,
            mirrorOnly: false,
            targetBaseCategory: "Jogos/Arena",
            contract: COMMUNICATION_CONTRACT,
            transport: {
                storageKey: EXTERNAL_STATE_KEY,
                globalBridgeName: GLOBAL_BRIDGE_NAME,
                globalExternalStateName: GLOBAL_EXTERNAL_STATE_NAME,
                eventNames: [EXTERNAL_STATE_EVENT, STATE_EVENT, BASE_ENTRY_EVENT, TRAINING_EXTERNAL_STATE_EVENT, BATTLEDOME_EXTERNAL_STATE_EVENT],
                channels: {
                    localStorage: storageAvailable(),
                    windowGlobal: true,
                    unsafeWindowGlobal: Boolean(publicWindow && publicWindow !== window),
                    windowEvent: true,
                    unsafeWindowEvent: Boolean(publicWindow && publicWindow !== window),
                    documentEvent: true
                },
                bridgeReady,
                bridgeMethods: getBridgeMethodNames()
            },
            freshness: {
                policy: "foundation-state-long-lived",
                maxAgeMs: EXTERNAL_STATE_MAX_AGE_MS,
                maxAgeLabel: "7d",
                shouldRepublishOnPageLoad: true
            },
            player: {
                playerId: state?.playerId || identity?.playerId || "local",
                username: state?.username || identity?.username || "Neopiano",
                identitySource: identity?.source || "",
                identityConfidence: identity?.confidence || 0,
                identityIsFallback: Boolean(identity?.isFallback)
            },
            visual: {
                avatarId: selectedAvatar.id,
                avatarImageUrl: selectedAvatar.imageUrl,
                teamId: selectedTeam.id,
                teamName: selectedTeam.name,
                themeId: selectedTheme.id,
                themeLabel: selectedTheme.label,
                shell: "neoquest-icons-audit-v0.1.29"
            },
            render: buildRenderReport(),
            coreOrganization: buildCoreOrganizationReport(),
            progress: {
                level: 1,
                xp: 0,
                policy: "pending"
            },
            views: VIEWS.map(view => clonePlain(view)),
            arena: {
                schema: "ncc-aaa-arena-foundation-v1",
                status: "battledome-loadout-audit-ready",
                ready: true,
                runtimeActive: true,
                operationalEnabled: true,
                moduleCount: ARENA_MODULE_REGISTRY.length,
                modules: ARENA_MODULE_REGISTRY.map(module => clonePlain(module)),
                slotCount: PLANNED_ARENA_SLOTS.length,
                slots: PLANNED_ARENA_SLOTS.map(slot => clonePlain(slot)),
                battledomeFoundationInstalled: true,
                battledomeOpponentFavoritesEnabled: true,
                battledomePrizeLoggerEnabled: true,
                battledomeLoadoutEnabled: true
            },
            training: buildTrainingExternalState("external-state"),
            battledome: buildBattledomeExternalState("external-state"),
            trophyTracker: buildTrophyTrackerReport("external-state"),
            modules: GAME_MODULE_REGISTRY.map(module => clonePlain(module)),
            moduleReports: {
                cellblock: buildCellblockReport(),
                kikoPop: buildKikoPopReport(),
                neoQuestOne: buildNeoQuestReport(),
                neoQuestTwo: buildNeoQuestTwoReport(),
                tyranuEvavu: buildTyranuEvavuReport("module-report"),
                kissTheMortog: buildKissTheMortogReport("module-report"),
                godori: buildGodoriReport("module-report"),
                battledomeTracker: buildBattledomeTrackerReport("module-report"),
                trophyTracker: buildTrophyTrackerReport("module-report")
            },
            alerts: [],
            capabilities: [],
            summary: {
                totalModules: GAME_MODULE_REGISTRY.length,
                activeModules: GAME_MODULE_REGISTRY.length,
                totalArenaModules: ARENA_MODULE_REGISTRY.length,
                activeArenaModules: ARENA_MODULE_REGISTRY.filter(module => module.operationalEnabled).length,
                trophyTrackerStatus: buildTrophyTrackerReport("summary").status,
                totalViews: VIEWS.length,
                arenaStatus: "training-active-battledome-opponent-favorites-ready",
                trainingStatus: "course-actions-active",
                alertCount: 0,
                migratedGameModules: GAME_MODULE_REGISTRY.map(module => module.id)
            },
            storage: {
                namespace: "ncc.aaa",
                perUserState: true,
                immutableCommits: true,
                revision: normalizeRevision(state?.revision),
                sessionId: state?.sessionId || SESSION_ID,
                localSessionId: SESSION_ID,
                lastCommitReason: state?.lastCommitReason || "",
                lastStorageSyncAt,
                secondaryWritesDebounced: true,
                storageKey: storageKey || "",
                visualPreferencesKey: visualPreferencesKey || "",
                manifestKey: STORAGE_MANIFEST_KEY,
                auditKey: STORAGE_AUDIT_KEY,
                externalStateKey: EXTERNAL_STATE_KEY,
                touchesNccBaseKeys: false,
                touchesNccRpgKeys: false
            },
            safety: {
                class: "mixed_visual_confirmed_write_and_session_toggle_autoplayer",
                gameActionPolicy: "tyranu-evavu-only-after-session-toggle",
                automaticOfficialAction: false,
                automaticGameAction: "conditional-user-toggle",
                automaticGameActionWithoutUserToggle: false,
                clickAutomation: "tyranu-evavu-session-toggle-only",
                keyboardAutomation: "neoquest-map-shortcuts-only",
                formSubmit: "tyranu-evavu-session-toggle-only",
                formSubmitWithoutUserToggle: false,
                networkAccess: "kiko-pop-page-jquery-after-click-and-neoquest2-keyh-xhr-only",
                storageDeletion: false
            },
            diagnostics: {
                bridge: bridgeReady,
                storage: storageAvailable() ? "available" : "unavailable",
                pageMounted,
                targetFound: Boolean(mountedTarget),
                renderStatus: lastRenderError ? "error" : "ok",
                render: buildRenderReport(),
                visualShell: "phase-16-kiko-pop-minimal",
                storagePolicy: "per-user-isolated-visual-preferences",
                communicationPolicy: "localStorage-window-unsafeWindow-document-events",
                lastRenderError: lastRenderError || "",
                bootErrors: bootErrors.slice(),
                health: buildHealthSummary(),
                selfTest: buildSelfTest("external-state"),
                cssSafety: buildCssSafetyReport()
            }
        });
    }

    function bootTrainingStateReader(reason) {
        try {
            const processReport = captureTrainingProcessCompletion();
            const statusReport = captureTrainingStatusFromPage();
            const state = loadTrainingReaderState();
            const report = {
                schema: TRAINING_READER_REPORT_KEY,
                version: VERSION,
                source: "ncc-aaa",
                sourceModule: "training-state-reader",
                reason: reason || "boot",
                status: statusReport?.status || processReport?.status || (isTrainingPageFamily() ? "training-page-no-capture" : "idle"),
                currentSchoolId: getTrainingSchoolFromLocation()?.id || getProcessTrainingSchoolFromLocation()?.id || "",
                capturedStatus: Boolean(statusReport?.captured),
                capturedProcessCompletion: Boolean(processReport?.captured),
                activeCount: countTrainingEntries(state, false),
                completeCount: countTrainingEntries(state, true),
                updatedAt: Date.now(),
                updatedAtIso: isoNow(),
                safety: {
                    readOnly: true,
                    networkAccessAdded: false,
                    officialActionAvailable: false,
                    panelInjectionEnabled: true,
                    neutralPanelOnly: false,
                    sdbOperationsEnabled: true,
                    courseActionsEnabled: true
                }
            };
            lastTrainingReaderReport = clonePlain(report);
            writeJson(TRAINING_READER_REPORT_KEY, report);
            return report;
        } catch (error) {
            const message = `training-reader:${errorToMessage(error)}`;
            bootErrors.push(message);
            lastTrainingReaderReport = {
                schema: TRAINING_READER_REPORT_KEY,
                version: VERSION,
                source: "ncc-aaa",
                sourceModule: "training-state-reader",
                status: "error",
                message,
                updatedAt: Date.now(),
                updatedAtIso: isoNow()
            };
            writeJson(TRAINING_READER_REPORT_KEY, lastTrainingReaderReport);
            return lastTrainingReaderReport;
        }
    }

    function getDefaultTrainingReaderState() {
        return {
            schema: "ncc-aaa-training-reader-state-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "training-state-reader",
            schools: { pirate: [], mystery: [], ninja: [] },
            lastStatus: "Training State Reader pronto.",
            lastUpdatedAt: 0,
            lastCapturedAt: 0,
            lastCapturedSchoolId: "",
            lastProcessCompletionAt: 0,
            lastProcessPetName: "",
            logs: []
        };
    }

    function loadTrainingReaderState() {
        const defaults = getDefaultTrainingReaderState();
        const raw = readJson(TRAINING_LOCAL_STATE_KEY, defaults);
        const next = Object.assign({}, defaults, isObject(raw) ? raw : {});
        if (!isObject(next.schools)) next.schools = { pirate: [], mystery: [], ninja: [] };
        Object.keys(TRAINING_SCHOOLS).forEach(key => {
            const items = Array.isArray(next.schools[key]) ? next.schools[key] : [];
            next.schools[key] = items
                .filter(item => item && item.name)
                .map(item => normalizeTrainingEntry(item, key));
        });
        next.lastStatus = String(next.lastStatus || "");
        next.lastUpdatedAt = safeTrainingNumber(next.lastUpdatedAt, 0);
        next.lastCapturedAt = safeTrainingNumber(next.lastCapturedAt, 0);
        next.lastCapturedSchoolId = String(next.lastCapturedSchoolId || "");
        next.lastProcessCompletionAt = safeTrainingNumber(next.lastProcessCompletionAt, 0);
        next.lastProcessPetName = String(next.lastProcessPetName || "");
        next.logs = Array.isArray(next.logs) ? next.logs.slice(-50) : [];
        return next;
    }

    function saveTrainingReaderState(nextState, message) {
        const state = Object.assign(getDefaultTrainingReaderState(), nextState || {});
        if (message) {
            state.lastStatus = String(message || "");
            state.lastUpdatedAt = Date.now();
            state.logs = Array.isArray(state.logs) ? state.logs : [];
            state.logs.push({ time: state.lastUpdatedAt, message: state.lastStatus });
            state.logs = state.logs.slice(-50);
        }
        writeJson(TRAINING_LOCAL_STATE_KEY, state);
        return state;
    }

    function normalizeTrainingEntry(item, fallbackSchoolId) {
        const endTime = safeTrainingNumber(item.endTime, 0);
        const now = Date.now();
        return {
            name: normalizeTrainingText(item.name || ""),
            school: String(item.school || fallbackSchoolId || ""),
            schoolId: String(item.schoolId || item.school || fallbackSchoolId || ""),
            endTime,
            complete: item.complete === true || (endTime > 0 && endTime <= now),
            rawTime: String(item.rawTime || ""),
            capturedAt: safeTrainingNumber(item.capturedAt, 0),
            source: String(item.source || "status-page")
        };
    }

    function safeTrainingNumber(value, fallback) {
        const number = Number(value);
        return Number.isFinite(number) ? number : Number(fallback || 0);
    }

    function normalizeNumber(value, fallback, min) {
        const number = Number(value);
        const fallbackNumber = Number(fallback);
        const normalized = Number.isFinite(number) ? number : (Number.isFinite(fallbackNumber) ? fallbackNumber : 0);
        if (min !== undefined) {
            const minimum = Number(min);
            return Number.isFinite(minimum) ? Math.max(minimum, normalized) : normalized;
        }
        return normalized;
    }

    function normalizeTrainingText(text) {
        return String(text || "").replace(/\s+/g, " ").trim();
    }

    function isTrainingStatusPage() {
        return Boolean(getTrainingSchoolFromLocation());
    }

    function isTrainingProcessPage() {
        return Boolean(getProcessTrainingSchoolFromLocation());
    }

    function isBattledomePage() {
        return location.pathname === BATTLEDOME_HOME_PATH || location.pathname === "/dome" || location.pathname.startsWith(BATTLEDOME_HOME_PATH);
    }

    function isBattledomeFightPage() {
        return location.pathname === BATTLEDOME_FIGHT_PATH;
    }

    function isBattledomeArenaPage() {
        return location.pathname === BATTLEDOME_ARENA_PATH;
    }

    function isTrainingPageFamily() {
        return isTrainingStatusPage() || isTrainingProcessPage();
    }

    function getTrainingSchoolFromLocation() {
        const path = String(location.pathname || "").replace(/\/+$/, "") || "/";
        const params = new URLSearchParams(String(location.search || ""));
        return Object.values(TRAINING_SCHOOLS).find(school => {
            const schoolPath = String(school.statusPath || "").replace(/\/+$/, "");
            if (schoolPath !== path) return false;
            const type = String(params.get("type") || "").toLowerCase();
            return type === "status" || !params.has("type");
        }) || null;
    }

    function getProcessTrainingSchoolFromLocation() {
        const path = String(location.pathname || "").replace(/\/+$/, "") || "/";
        return Object.values(TRAINING_SCHOOLS).find(school => {
            const processPath = String(school.processPath || "").replace(/\/+$/, "");
            return processPath === path;
        }) || null;
    }

    function getTrainingBlocks(rootNode) {
        const root = rootNode || document;
        const seen = new Set();
        return Array.from(root.querySelectorAll("b"))
            .filter(el => /\s\(Level\s+\d+/i.test(el.textContent || ""))
            .map(header => {
                const name = normalizeTrainingText((header.textContent || "").split(" (Level")[0]);
                const key = name.toLowerCase();
                if (!name || seen.has(key)) return null;
                seen.add(key);
                const row = header.closest("tr");
                const rows = [];
                let current = row;
                for (let i = 0; i < 8 && current; i += 1) {
                    if (i > 0 && Array.from(current.querySelectorAll("b")).some(item => /\s\(Level\s+\d+/i.test(item.textContent || ""))) break;
                    rows.push(current);
                    current = current.nextElementSibling;
                }
                const table = header.closest("table");
                const text = normalizeTrainingText(rows.map(item => item.textContent || "").join(" ") || table?.textContent || header.parentElement?.textContent || "");
                return { name, header, row, rows, table, text };
            })
            .filter(Boolean);
    }

    function parseTrainingRemainingMs(text) {
        const compact = normalizeTrainingText(text);
        if (/Course Finished!/i.test(compact)) return -1;
        const line = compact.match(/Time till course finishes\s*:\s*([^\.]+?)(?:Status|$)/i) || compact.match(/Time till course finishes\s*:\s*(.*)$/i);
        if (!line) return 0;
        const nums = (line[1].match(/\d+/g) || []).map(Number);
        if (!nums.length) return 0;
        const hours = nums[0] || 0;
        const minutes = nums[1] || 0;
        const seconds = nums[2] || 0;
        return hours * 3600000 + minutes * 60000 + seconds * 1000;
    }

    function formatTrainingDuration(ms) {
        if (ms <= 0) return "Complete!";
        const totalSec = Math.floor(ms / 1000);
        const hrs = Math.floor(totalSec / 3600);
        const mins = Math.floor((totalSec % 3600) / 60);
        const secs = totalSec % 60;
        return `${hrs}h ${String(mins).padStart(2, "0")}m ${String(secs).padStart(2, "0")}s`;
    }

    function captureTrainingStatusFromPage() {
        const school = getTrainingSchoolFromLocation();
        if (!school) return { captured: false, status: "not-training-status-page" };
        const now = Date.now();
        const entries = [];
        getTrainingBlocks(document).forEach(block => {
            if (/is not on a course/i.test(block.text)) return;
            const remainingMs = parseTrainingRemainingMs(block.text);
            if (remainingMs === 0 && !/Course Finished!/i.test(block.text)) return;
            entries.push({
                name: block.name,
                school: school.id,
                schoolId: school.id,
                endTime: remainingMs < 0 ? now - 1000 : now + remainingMs,
                complete: remainingMs < 0,
                rawTime: remainingMs < 0 ? "Course Finished!" : formatTrainingDuration(remainingMs),
                capturedAt: now,
                source: "status-page"
            });
        });
        const state = loadTrainingReaderState();
        state.schools[school.id] = entries.map(entry => normalizeTrainingEntry(entry, school.id));
        state.lastCapturedAt = now;
        state.lastCapturedSchoolId = school.id;
        const message = entries.length ? `${school.shortName}: ${entries.length} curso(s) capturado(s).` : `${school.shortName}: nenhum curso em andamento encontrado.`;
        saveTrainingReaderState(state, message);
        return { captured: true, status: "status-captured", schoolId: school.id, count: entries.length, message };
    }

    function captureTrainingProcessCompletion() {
        const school = getProcessTrainingSchoolFromLocation();
        if (!school) return { captured: false, status: "not-training-process-page" };
        const html = String(document.body?.innerHTML || "");
        const text = normalizeTrainingText(document.body?.textContent || "");
        const match = html.match(/Congratulations!\s*<b>([^<]+)<\/b>/i) || text.match(/Congratulations!\s*([^<]+?)\s+has/i);
        if (!match) return { captured: false, status: "process-page-no-completion", schoolId: school.id };
        const petName = normalizeTrainingText(match[1]);
        if (!petName) return { captured: false, status: "process-page-empty-pet", schoolId: school.id };
        const state = loadTrainingReaderState();
        Object.keys(TRAINING_SCHOOLS).forEach(key => {
            state.schools[key] = (state.schools[key] || []).filter(entry => normalizeTrainingText(entry.name).toLowerCase() !== petName.toLowerCase());
        });
        state.lastProcessCompletionAt = Date.now();
        state.lastProcessPetName = petName;
        const message = `${petName}: curso concluído e removido do estado local de treino.`;
        saveTrainingReaderState(state, message);
        return { captured: true, status: "process-completion-captured", schoolId: school.id, petName, message };
    }

    function getTrainingReaderEntries(readerState) {
        const state = readerState || loadTrainingReaderState();
        return Object.keys(TRAINING_SCHOOLS).flatMap(key => (state.schools[key] || []).map(entry => normalizeTrainingEntry(entry, key)));
    }

    function countTrainingEntries(readerState, completeOnly) {
        const now = Date.now();
        return getTrainingReaderEntries(readerState).filter(entry => {
            const complete = entry.complete || (entry.endTime && entry.endTime <= now);
            return completeOnly ? complete : !complete;
        }).length;
    }

    function getNextTrainingCourse(readerState) {
        const now = Date.now();
        const next = getTrainingReaderEntries(readerState)
            .filter(entry => entry.endTime && entry.endTime > now)
            .sort((a, b) => a.endTime - b.endTime)[0];
        if (!next) return null;
        const school = TRAINING_SCHOOLS[next.schoolId || next.school] || null;
        return {
            name: next.name,
            school: next.schoolId || next.school,
            schoolName: school?.name || next.school || "",
            schoolShortName: school?.shortName || next.school || "",
            endTime: next.endTime,
            endTimeIso: new Date(next.endTime).toISOString(),
            remainingMs: Math.max(0, next.endTime - now),
            remainingLabel: formatTrainingDuration(next.endTime - now)
        };
    }

    function buildTrainingExternalState(reason) {
        const currentSchool = getTrainingSchoolFromLocation();
        const processSchool = getProcessTrainingSchoolFromLocation();
        const readerState = loadTrainingReaderState();
        const now = Date.now();
        const schools = Object.fromEntries(Object.entries(TRAINING_SCHOOLS).map(([key, school]) => {
            const entries = (readerState.schools[key] || []).map(entry => normalizeTrainingEntry(entry, key));
            const activeEntries = entries.filter(entry => entry.endTime && entry.endTime > now);
            const completeEntries = entries.filter(entry => entry.complete || (entry.endTime && entry.endTime <= now));
            return [key, {
                id: school.id,
                name: school.name,
                shortName: school.shortName,
                statusPath: school.statusPath,
                processPath: school.processPath,
                href: school.href,
                currency: school.currency,
                icon: school.icon,
                maxLevel: Number.isFinite(school.maxLevel) ? school.maxLevel : null,
                sdbMode: school.sdbMode,
                operationalEnabled: school.id !== "pirate",
                stateReaderEnabled: true,
                entries,
                activeCount: activeEntries.length,
                completeCount: completeEntries.length,
                lastCapturedAt: key === readerState.lastCapturedSchoolId ? readerState.lastCapturedAt : 0,
                lastCapturedAtIso: key === readerState.lastCapturedSchoolId && readerState.lastCapturedAt ? new Date(readerState.lastCapturedAt).toISOString() : ""
            }];
        }));
        const allEntries = getTrainingReaderEntries(readerState);
        const completeEntries = allEntries.filter(entry => entry.complete || (entry.endTime && entry.endTime <= now));
        const activeEntries = allEntries.filter(entry => entry.endTime && entry.endTime > now);
        const nextCourse = getNextTrainingCourse(readerState);
        return clonePlain({
            schema: TRAINING_STATE_SCHEMA,
            schemaVersion: 2,
            source: "ncc-aaa",
            sourceScript: "NCC AAA",
            sourceModule: "training-state-reader",
            version: VERSION,
            reason: reason || "unknown",
            updatedAt: now,
            updatedAtIso: isoNow(),
            status: "course-actions-audited",
            ready: true,
            stateOnly: false,
            stateReaderEnabled: true,
            pagePanelEnabled: true,
            operationalAvailable: true,
            officialActionAvailable: true,
            panelInjectionEnabled: true,
            neutralPanelOnly: false,
            sdbOperationsEnabled: true,
            courseActionsEnabled: true,
            currentPage: {
                pathname: location.pathname,
                search: location.search,
                isTrainingStatusPage: Boolean(currentSchool),
                isTrainingProcessPage: Boolean(processSchool),
                schoolId: currentSchool?.id || processSchool?.id || "",
                schoolName: currentSchool?.name || processSchool?.name || ""
            },
            schools,
            completeCount: completeEntries.length,
            activeCount: activeEntries.length,
            totalEntries: allEntries.length,
            nextCourse,
            alerts: completeEntries.map(entry => {
                const school = TRAINING_SCHOOLS[entry.schoolId || entry.school] || null;
                return {
                    type: "training-complete",
                    petName: entry.name,
                    school: entry.schoolId || entry.school,
                    schoolName: school?.name || "",
                    schoolShortName: school?.shortName || "",
                    endTime: entry.endTime,
                    endTimeIso: entry.endTime ? new Date(entry.endTime).toISOString() : ""
                };
            }),
            reader: {
                enabled: true,
                localStateKey: TRAINING_LOCAL_STATE_KEY,
                lastStatus: readerState.lastStatus || "",
                lastUpdatedAt: readerState.lastUpdatedAt || 0,
                lastUpdatedAtIso: readerState.lastUpdatedAt ? new Date(readerState.lastUpdatedAt).toISOString() : "",
                lastCapturedAt: readerState.lastCapturedAt || 0,
                lastCapturedAtIso: readerState.lastCapturedAt ? new Date(readerState.lastCapturedAt).toISOString() : "",
                lastCapturedSchoolId: readerState.lastCapturedSchoolId || "",
                lastProcessCompletionAt: readerState.lastProcessCompletionAt || 0,
                lastProcessPetName: readerState.lastProcessPetName || "",
                lastReport: lastTrainingReaderReport || readJson(TRAINING_READER_REPORT_KEY, null)
            },
            safety: {
                safetyClass: "confirmed-write",
                automaticOfficialAction: false,
                withdrawSdbOnBoot: false,
                payCourseOnBoot: false,
                completeCourseOnBoot: false,
                startCourseOnBoot: false,
                requiresUserClickForOfficialActions: true,
                requiresConfirmationForOfficialActions: true,
                permanentPinStorage: false,
                networkAccessAdded: "only-after-user-click-confirmation-for-sdb-and-course-actions",
                officialEndpointsCalled: "sdb-and-course-actions-only-after-user-confirmation",
                neutralPanelOnly: false,
                pagePanelReportKey: TRAINING_PAGE_PANEL_REPORT_KEY,
                sdbReportKey: TRAINING_SDB_REPORT_KEY,
                auditReportKey: TRAINING_EQUIVALENCE_AUDIT_KEY
            },
            audit: buildTrainingEquivalenceAuditReport("external-state"),
            pagePanel: buildTrainingPagePanelReport("external-state"),
            sdb: getTrainingSdbReportFallback(),
            courseActions: getTrainingCourseActionReportFallback(),
            nextStage: "manual-neopets-page-testing"
        });
    }

    function buildTrainingEquivalenceAuditReport(reason) {
        const report = clonePlain(Object.assign({}, TRAINING_EQUIVALENCE_AUDIT, {
            version: VERSION,
            reason: reason || "audit",
            updatedAt: Date.now(),
            updatedAtIso: isoNow(),
            currentPage: {
                pathname: location.pathname,
                search: location.search,
                isTrainingStatusPage: Boolean(getTrainingSchoolFromLocation()),
                isTrainingProcessPage: Boolean(getProcessTrainingSchoolFromLocation())
            },
            liveReports: {
                reader: lastTrainingReaderReport || readJson(TRAINING_READER_REPORT_KEY, null),
                pagePanel: lastTrainingPagePanelReport || readJson(TRAINING_PAGE_PANEL_REPORT_KEY, null),
                sdb: getTrainingSdbReportFallback(),
                courseActions: getTrainingCourseActionReportFallback()
            },
            storageKeys: {
                trainingExternalState: TRAINING_EXTERNAL_STATE_KEY,
                trainingReaderState: TRAINING_LOCAL_STATE_KEY,
                pagePanelReport: TRAINING_PAGE_PANEL_REPORT_KEY,
                sdbReport: TRAINING_SDB_REPORT_KEY,
                courseActionReport: TRAINING_COURSE_ACTION_REPORT_KEY,
                batchSummary: TRAINING_BATCH_SUMMARY_KEY,
                auditReport: TRAINING_EQUIVALENCE_AUDIT_KEY
            }
        }));
        writeJson(TRAINING_EQUIVALENCE_AUDIT_KEY, report);
        return report;
    }

    function publishTrainingExternalState(reason) {
        try {
            const trainingState = buildTrainingExternalState(reason);
            writeTrainingExternalStateToStorage(trainingState);
            publishTrainingExternalStateToGlobals(trainingState);
            dispatchTrainingExternalState(trainingState);
            return trainingState;
        } catch (error) {
            bootErrors.push(`training-state:${errorToMessage(error)}`);
            console.warn("[NCC AAA] Falha ao publicar Training State.", error);
            return null;
        }
    }

    function writeTrainingExternalStateToStorage(trainingState) {
        try {
            localStorage.setItem(TRAINING_EXTERNAL_STATE_KEY, JSON.stringify(trainingState));
        } catch (error) {
            bootErrors.push(`training-storage:${errorToMessage(error)}`);
        }
    }

    function publishTrainingExternalStateToGlobals(trainingState) {
        const publicWindow = getPublicWindow();
        try { window[GLOBAL_TRAINING_STATE_NAME] = trainingState; } catch (_) {}
        try { window.NCC_AAA_LAST_TRAINING_STATE_AT = trainingState.updatedAtIso; } catch (_) {}
        if (publicWindow && publicWindow !== window) {
            try { publicWindow[GLOBAL_TRAINING_STATE_NAME] = trainingState; } catch (_) {}
            try { publicWindow.NCC_AAA_LAST_TRAINING_STATE_AT = trainingState.updatedAtIso; } catch (_) {}
        }
    }

    function dispatchTrainingExternalState(trainingState) {
        dispatchStateEvent(window, TRAINING_EXTERNAL_STATE_EVENT, trainingState);
        dispatchStateEvent(document, TRAINING_EXTERNAL_STATE_EVENT, trainingState);
        const publicWindow = getPublicWindow();
        if (publicWindow && publicWindow !== window) dispatchStateEvent(publicWindow, TRAINING_EXTERNAL_STATE_EVENT, trainingState);
    }


    function getDefaultBattledomeState() {
        return {
            prizes: [],
            opponents: [],
            favoriteOpponentIds: [],
            paused: false,
            filterMode: "today",
            filterStart: "",
            filterEnd: "",
            format: "lines",
            lastBatchSignature: "",
            lastBatchAt: 0,
            prizeLogLimit: 500,
            loadoutEnabled: true,
            loadoutWeapons: [],
            loadoutLastAppliedAt: 0,
            lastStatus: "Battledome Tracker ativo: favoritos de oponentes e logger local de recompensas visíveis instalados."
        };
    }

    function loadBattledomeState() {
        return normalizeBattledomeState(readJson(BATTLEDOME_STORAGE_KEY, getDefaultBattledomeState()));
    }

    function saveBattledomeState(nextState) {
        const normalized = normalizeBattledomeState(nextState || getDefaultBattledomeState());
        writeJson(BATTLEDOME_STORAGE_KEY, normalized);
        return normalized;
    }

    function normalizeBattledomeState(data) {
        const defaults = getDefaultBattledomeState();
        const source = Object.assign({}, defaults, isObject(data) ? data : {});
        const limit = Math.max(100, Math.min(1000, safeTrainingNumber(source.prizeLogLimit, defaults.prizeLogLimit) || defaults.prizeLogLimit));
        const prizes = Array.isArray(source.prizes) ? source.prizes
            .filter(item => item && typeof item.item === "string")
            .map(item => ({
                time: safeTrainingNumber(item.time, Date.now()),
                item: normalizeTrainingText(item.item || ""),
                batch: String(item.batch || "")
            }))
            .filter(item => item.item)
            .slice(-limit) : [];
        const opponents = Array.isArray(source.opponents) ? source.opponents
            .filter(opponent => opponent && opponent.id !== undefined && opponent.name)
            .map(opponent => ({
                id: safeTrainingNumber(opponent.id, 0),
                name: normalizeTrainingText(opponent.name || ""),
                image: String(opponent.image || "").trim(),
                syncedAt: safeTrainingNumber(opponent.syncedAt, 0)
            }))
            .filter(opponent => Number.isFinite(opponent.id) && opponent.id > 0 && opponent.name) : [];
        const favoriteOpponentIds = Array.from(new Set(Array.isArray(source.favoriteOpponentIds) ? source.favoriteOpponentIds.map(id => safeTrainingNumber(id, 0)).filter(id => Number.isFinite(id) && id > 0) : [])).sort((a, b) => a - b);
        const loadoutWeapons = Array.isArray(source.loadoutWeapons) ? source.loadoutWeapons
            .filter(item => item && (item.name || item.image || item.src))
            .map(item => ({
                name: normalizeTrainingText(item.name || ""),
                image: String(item.image || "").split("/").pop().trim(),
                src: String(item.src || "").trim(),
                savedAt: safeTrainingNumber(item.savedAt, 0)
            }))
            .filter(item => item.name || item.image || item.src)
            .slice(0, 2) : [];
        return clonePlain({
            prizes,
            opponents,
            favoriteOpponentIds,
            paused: source.paused === true,
            filterMode: ["today", "seven", "all", "custom"].includes(source.filterMode) ? source.filterMode : defaults.filterMode,
            filterStart: String(source.filterStart || ""),
            filterEnd: String(source.filterEnd || ""),
            format: ["lines", "counts"].includes(source.format) ? source.format : defaults.format,
            lastBatchSignature: String(source.lastBatchSignature || ""),
            lastBatchAt: safeTrainingNumber(source.lastBatchAt, 0),
            prizeLogLimit: limit,
            loadoutEnabled: source.loadoutEnabled !== false,
            loadoutWeapons,
            loadoutLastAppliedAt: safeTrainingNumber(source.loadoutLastAppliedAt, 0),
            lastStatus: String(source.lastStatus || defaults.lastStatus)
        });
    }

    function setBattledomeStatus(message, patch) {
        const state = loadBattledomeState();
        const next = Object.assign({}, state, isObject(patch) ? patch : {});
        next.lastStatus = String(message || next.lastStatus || "");
        const saved = saveBattledomeState(next);
        publishBattledomeExternalState("battledome-status");
        if (root && isMountPage()) render({ reason: "battledome-status", forceFull: false });
        return saved;
    }

    function getBattledomeOpponentRows(rootNode) {
        const scope = rootNode || document;
        const rows = Array.from(scope.querySelectorAll("#npcTable tr.npcRow[data-oppid], #npcTable .npcRow[data-oppid], tr.npcRow[data-oppid], .npcRow[data-oppid]"));
        const seen = new Set();
        return rows.filter(row => {
            if (!row || seen.has(row)) return false;
            seen.add(row);
            const id = Number(row.dataset?.oppid || row.getAttribute?.("data-oppid"));
            return Number.isFinite(id) && id > 0;
        });
    }

    function normalizeBattledomeAbsoluteUrl(value) {
        const src = String(value || "").trim();
        if (!src) return "";
        try { return new URL(src, location.origin).href; } catch (_) { return src; }
    }

    function getBattledomeOpponentNameFromRow(row) {
        const nameNode = row?.querySelector?.("td.name") || row?.querySelector?.(".name") || row?.querySelector?.("td") || row;
        if (!nameNode) return "";
        let clone = null;
        try { clone = nameNode.cloneNode(true); } catch (_) { clone = null; }
        const source = clone || nameNode;
        try { source.querySelectorAll?.(".ncc-aaa-bd-row-fav-btn").forEach(button => button.remove()); } catch (_) {}
        return normalizeTrainingText(source.textContent || "")
            .replace(/Favoritar|Remover dos favoritos|Adicionar aos favoritos|NCC AAA/gi, "")
            .replace(/[★☆♥♡]/g, "")
            .replace(/\s+/g, " ")
            .trim();
    }

    function getBattledomeOpponentImageFromRow(row) {
        const img = row?.querySelector?.("img");
        const src = img?.getAttribute?.("src") || img?.src || "";
        if (src) return normalizeBattledomeAbsoluteUrl(src);
        const bgNode = row?.querySelector?.(".oppImage, [style*='background']");
        const bgValue = bgNode ? (bgNode.style?.backgroundImage || window.getComputedStyle(bgNode).backgroundImage || "") : "";
        const match = bgValue.match(/url\(["']?(.+?)["']?\)/i);
        return match?.[1] ? normalizeBattledomeAbsoluteUrl(match[1]) : "";
    }

    function mergeBattledomeOpponents(previousOpponents, nextOpponents) {
        const map = new Map();
        (Array.isArray(previousOpponents) ? previousOpponents : []).forEach(opponent => {
            if (opponent && Number.isFinite(Number(opponent.id))) map.set(Number(opponent.id), opponent);
        });
        (Array.isArray(nextOpponents) ? nextOpponents : []).forEach(opponent => {
            if (!opponent || !Number.isFinite(Number(opponent.id))) return;
            const id = Number(opponent.id);
            map.set(id, Object.assign({}, map.get(id) || {}, opponent, { id }));
        });
        return Array.from(map.values()).sort((a, b) => String(a.name || "").localeCompare(String(b.name || "")));
    }

    function syncBattledomeOpponentsFromPage(options) {
        const opts = Object.assign({ silent: false, apply: true }, options || {});
        if (!isBattledomeFightPage()) {
            if (!opts.silent) setBattledomeStatus("Abra a página de escolher oponente do Battledome para sincronizar favoritos.");
            return 0;
        }
        const rows = getBattledomeOpponentRows(document);
        if (!rows.length) {
            if (!opts.silent) setBattledomeStatus("Não encontrei oponentes nesta página. Entre na etapa de escolher oponente e tente novamente.");
            return 0;
        }
        const now = Date.now();
        const scanned = [];
        rows.forEach(row => {
            const id = Number(row.dataset?.oppid || row.getAttribute?.("data-oppid"));
            const name = getBattledomeOpponentNameFromRow(row);
            if (!Number.isFinite(id) || id <= 0 || !name) return;
            scanned.push({ id, name, image: getBattledomeOpponentImageFromRow(row), syncedAt: now });
        });
        const state = loadBattledomeState();
        const next = Object.assign({}, state, {
            opponents: mergeBattledomeOpponents(state.opponents, scanned),
            lastStatus: opts.silent && state.lastStatus ? state.lastStatus : `${scanned.length} oponente(s) sincronizado(s) nesta página.`
        });
        saveBattledomeState(next);
        if (opts.apply !== false) applyBattledomeFavoritesOnFightPage({ silent: true, skipSync: true });
        publishBattledomeExternalState(opts.silent ? "battledome-opponents-auto-sync" : "battledome-opponents-sync");
        if (root && isMountPage()) render({ reason: "battledome-opponents-sync", forceFull: false });
        return scanned.length;
    }

    function getBattledomeFavoriteOpponents(state) {
        const data = state || loadBattledomeState();
        const opponents = Array.isArray(data.opponents) ? data.opponents : [];
        const byId = new Map(opponents.map(opponent => [Number(opponent.id), opponent]));
        return (Array.isArray(data.favoriteOpponentIds) ? data.favoriteOpponentIds : []).map(id => {
            const numericId = Number(id);
            const saved = byId.get(numericId);
            return saved || { id: numericId, name: `Oponente #${numericId}`, image: "", syncedAt: 0, missing: true };
        }).filter(opponent => Number.isFinite(Number(opponent.id)) && Number(opponent.id) > 0);
    }

    function ensureBattledomeFightFavoriteStyle() {
        if (document.getElementById(BATTLEDOME_FIGHT_FAVORITE_STYLE_ID)) return;
        const style = document.createElement("style");
        style.id = BATTLEDOME_FIGHT_FAVORITE_STYLE_ID;
        style.textContent = `
            #npcTable tr.ncc-aaa-bd-fav-separator td {
                background: #fff1a8 !important;
                color: #4b3400 !important;
                font-weight: 800 !important;
                text-align: left !important;
                border-top: 2px solid #e5b900 !important;
                border-bottom: 2px solid #e5b900 !important;
                padding: 6px 10px !important;
                letter-spacing: .2px;
            }
            #npcTable tr.ncc-aaa-bd-fav-row td {
                background: #fff9cf !important;
                box-shadow: inset 0 0 0 1px rgba(241, 194, 50, .45);
            }
            #npcTable tr.ncc-aaa-bd-fav-row.selectedPveNpc td {
                background: #fff58c !important;
            }
            #npcTable .ncc-aaa-bd-row-fav-btn {
                appearance: none;
                border: 1px solid #d9b600;
                background: #fff7cf;
                color: #5c4300;
                border-radius: 999px;
                min-width: 24px;
                height: 22px;
                margin-left: 8px;
                padding: 0 5px;
                cursor: pointer;
                font: 800 13px/18px Arial, sans-serif;
                vertical-align: middle;
            }
            #npcTable .ncc-aaa-bd-row-fav-btn:hover,
            #npcTable .ncc-aaa-bd-row-fav-btn.active {
                background: #ffe37a;
                border-color: #bc9700;
            }
            #npcTable tr.ncc-aaa-bd-fav-row .ncc-aaa-bd-row-fav-btn {
                background: #ffd957;
                border-color: #b99100;
            }
        `;
        document.head.appendChild(style);
    }

    function ensureBattledomeRowFavoriteButton(row, favoriteSet) {
        const id = Number(row?.dataset?.oppid || row?.getAttribute?.("data-oppid"));
        if (!Number.isFinite(id) || id <= 0) return;
        const nameCell = row.querySelector("td.name") || row.querySelector(".name") || row.querySelector("td") || row;
        if (!nameCell) return;
        let button = nameCell.querySelector(":scope > .ncc-aaa-bd-row-fav-btn") || nameCell.querySelector(".ncc-aaa-bd-row-fav-btn");
        if (!button) {
            button = document.createElement("button");
            button.type = "button";
            button.className = "ncc-aaa-bd-row-fav-btn";
            button.dataset.nccAaaBdAction = "toggle-favorite";
            nameCell.appendChild(button);
        }
        const active = favoriteSet.has(id);
        button.dataset.oppId = String(id);
        button.textContent = active ? "★" : "☆";
        button.title = active ? "Remover dos favoritos do NCC AAA" : "Adicionar aos favoritos do NCC AAA";
        button.setAttribute("aria-label", button.title);
        button.classList.toggle("active", active);
    }

    function removeBattledomeFavoriteSeparators() {
        document.querySelectorAll("#npcTable tr.ncc-aaa-bd-fav-separator").forEach(row => row.remove());
    }

    function makeBattledomeFavoriteSeparator(text) {
        const tr = document.createElement("tr");
        tr.className = "ncc-aaa-bd-fav-separator";
        const td = document.createElement("td");
        td.colSpan = 4;
        td.textContent = text;
        tr.appendChild(td);
        return tr;
    }

    function applyBattledomeFavoritesOnFightPage(options) {
        const opts = Object.assign({ silent: false, skipSync: false }, options || {});
        if (!isBattledomeFightPage()) {
            if (!opts.silent) setBattledomeStatus("Abra a página de escolher oponente do Battledome para aplicar favoritos.");
            return 0;
        }
        if (!opts.skipSync) syncBattledomeOpponentsFromPage({ silent: true, apply: false });
        const table = document.querySelector("#npcTable");
        const rows = getBattledomeOpponentRows(document);
        if (!table || !rows.length) return 0;
        ensureBattledomeFightFavoriteStyle();
        const state = loadBattledomeState();
        const favoriteSet = new Set(state.favoriteOpponentIds.map(Number));
        rows.forEach((row, index) => {
            if (row.dataset.nccAaaBdOriginalIndex === undefined) row.dataset.nccAaaBdOriginalIndex = String(index);
            const id = Number(row.dataset?.oppid || row.getAttribute?.("data-oppid"));
            const active = favoriteSet.has(id);
            row.classList.toggle("ncc-aaa-bd-fav-row", active);
            row.dataset.nccAaaBdFavorite = active ? "1" : "0";
            ensureBattledomeRowFavoriteButton(row, favoriteSet);
        });
        removeBattledomeFavoriteSeparators();
        const favoriteRows = rows
            .filter(row => favoriteSet.has(Number(row.dataset?.oppid || row.getAttribute?.("data-oppid"))))
            .sort((a, b) => {
                const aid = Number(a.dataset?.oppid || a.getAttribute?.("data-oppid"));
                const bid = Number(b.dataset?.oppid || b.getAttribute?.("data-oppid"));
                const ai = state.favoriteOpponentIds.indexOf(aid);
                const bi = state.favoriteOpponentIds.indexOf(bid);
                if (ai !== bi) return ai - bi;
                return getBattledomeOpponentNameFromRow(a).localeCompare(getBattledomeOpponentNameFromRow(b));
            });
        if (favoriteRows.length) {
            const parent = rows[0].parentElement || table.tBodies?.[0] || table;
            let anchor = table.querySelector("tr.headerSeparator") || table.querySelector("tr.npcHeader") || null;
            let reference = anchor ? anchor.nextSibling : rows.find(row => row.parentElement === parent) || null;
            favoriteRows.forEach(row => {
                if (row.parentElement === parent) parent.removeChild(row);
            });
            if (reference && reference.parentElement !== parent) {
                reference = rows.find(row => row.parentElement === parent && !favoriteSet.has(Number(row.dataset?.oppid || row.getAttribute?.("data-oppid")))) || null;
            }
            parent.insertBefore(makeBattledomeFavoriteSeparator(`Favoritos do NCC AAA (${favoriteRows.length})`), reference);
            favoriteRows.forEach(row => parent.insertBefore(row, reference));
            parent.insertBefore(makeBattledomeFavoriteSeparator("Todos os oponentes"), reference);
        }
        if (!opts.silent) {
            setBattledomeStatus(favoriteRows.length ? `${favoriteRows.length} favorito(s) destacado(s) no topo da lista.` : "Nenhum favorito marcado para destacar na lista.");
        } else {
            publishBattledomeExternalState("battledome-favorites-apply-silent");
        }
        return favoriteRows.length;
    }

    function toggleBattledomeFavoriteOpponent(id) {
        const opponentId = Number(id);
        if (!Number.isFinite(opponentId) || opponentId <= 0) return false;
        const state = loadBattledomeState();
        const set = new Set(state.favoriteOpponentIds.map(Number));
        const willFavorite = !set.has(opponentId);
        if (willFavorite) set.add(opponentId);
        else set.delete(opponentId);
        const opponent = (state.opponents || []).find(item => Number(item.id) === opponentId);
        state.favoriteOpponentIds = Array.from(set).sort((a, b) => a - b);
        state.lastStatus = opponent
            ? `${willFavorite ? "Favoritado" : "Removido dos favoritos"}: ${opponent.name}.`
            : "Favoritos do Battledome atualizados.";
        saveBattledomeState(state);
        if (isBattledomeFightPage()) applyBattledomeFavoritesOnFightPage({ silent: true, skipSync: true });
        publishBattledomeExternalState("battledome-toggle-favorite");
        if (root && isMountPage()) render({ reason: "battledome-toggle-favorite", forceFull: false });
        return true;
    }

    function bindBattledomeFightFavoriteClicks() {
        if (battledomeFightFavoriteClickBound) return;
        battledomeFightFavoriteClickBound = true;
        document.addEventListener("click", event => {
            const button = event.target?.closest?.(".ncc-aaa-bd-row-fav-btn[data-ncc-aaa-bd-action='toggle-favorite']");
            if (!button || !isBattledomeFightPage()) return;
            event.preventDefault();
            event.stopPropagation();
            toggleBattledomeFavoriteOpponent(button.dataset.oppId);
        }, true);
    }

    function bootBattledomeOpponentFavorites(reason) {
        if (!isBattledomeFightPage()) return false;
        ensureBattledomeFightFavoriteStyle();
        bindBattledomeFightFavoriteClicks();
        const run = () => {
            try {
                syncBattledomeOpponentsFromPage({ silent: true, apply: true });
                applyBattledomeFavoritesOnFightPage({ silent: true, skipSync: true });
                publishBattledomeExternalState(reason || "boot-battledome-opponent-favorites");
            } catch (error) {
                bootErrors.push(`battledome-favorites:${errorToMessage(error)}`);
                console.warn("[NCC AAA] Falha ao aplicar favoritos do Battledome.", error);
            }
        };
        if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", () => window.setTimeout(run, 700), { once: true });
        } else {
            window.setTimeout(run, 700);
        }
        return true;
    }

    function clearBattledomeOpponentsAndFavorites() {
        if (!window.confirm("Limpar a lista local de oponentes e favoritos do Battledome no NCC AAA?")) return false;
        const state = loadBattledomeState();
        state.opponents = [];
        state.favoriteOpponentIds = [];
        state.lastStatus = "Lista local de oponentes e favoritos do Battledome limpa.";
        saveBattledomeState(state);
        publishBattledomeExternalState("battledome-clear-opponents");
        if (root && isMountPage()) render({ reason: "battledome-clear-opponents", forceFull: true });
        return true;
    }

    function getBattledomeNSTDayKey(date) {
        try {
            const parts = new Intl.DateTimeFormat("en-CA", {
                timeZone: "America/Los_Angeles",
                year: "numeric",
                month: "2-digit",
                day: "2-digit"
            }).formatToParts(date instanceof Date ? date : new Date(date));
            const map = Object.fromEntries(parts.map(part => [part.type, part.value]));
            return `${map.year || "0000"}-${map.month || "00"}-${map.day || "00"}`;
        } catch (_) {
            const d = date instanceof Date ? date : new Date(date);
            return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
        }
    }

    function formatBattledomePrizeTime(time) {
        try {
            return new Date(time).toLocaleString("pt-BR", {
                timeZone: "America/Los_Angeles",
                month: "short",
                day: "2-digit",
                hour: "2-digit",
                minute: "2-digit"
            }).replace(",", "") + " NST";
        } catch (_) {
            return new Date(time).toLocaleString("pt-BR");
        }
    }

    function getBattledomeFilteredPrizes(state) {
        const data = state || loadBattledomeState();
        const now = Date.now();
        let items = Array.isArray(data.prizes) ? data.prizes.slice() : [];
        if (data.filterMode === "today") {
            const today = getBattledomeNSTDayKey(new Date());
            items = items.filter(item => getBattledomeNSTDayKey(new Date(item.time)) === today);
        } else if (data.filterMode === "seven") {
            items = items.filter(item => Number(item.time || 0) >= now - 7 * 24 * 60 * 60 * 1000);
        } else if (data.filterMode === "custom") {
            const start = data.filterStart ? new Date(data.filterStart).getTime() : 0;
            const end = data.filterEnd ? new Date(data.filterEnd).getTime() : 0;
            if (Number.isFinite(start) && start > 0) items = items.filter(item => Number(item.time || 0) >= start);
            if (Number.isFinite(end) && end > 0) items = items.filter(item => Number(item.time || 0) < end);
        }
        return items.sort((a, b) => Number(a.time || 0) - Number(b.time || 0));
    }

    function formatBattledomePrizeLog(items, format) {
        const rows = Array.isArray(items) ? items : [];
        if (!rows.length) return "";
        if (format === "counts") {
            const counts = new Map();
            rows.forEach(entry => {
                const item = normalizeTrainingText(entry.item || "");
                if (item) counts.set(item, (counts.get(item) || 0) + 1);
            });
            return Array.from(counts.entries())
                .sort((a, b) => a[0].localeCompare(b[0]))
                .map(([item, count]) => `${item} x${count}`)
                .join("\n");
        }
        return rows.map(entry => `${formatBattledomePrizeTime(entry.time)} - ${normalizeTrainingText(entry.item || "")}`).join("\n");
    }

    function setBattledomePrizeSetting(name, value) {
        const state = loadBattledomeState();
        if (name === "filterMode") state.filterMode = ["today", "seven", "all", "custom"].includes(value) ? value : "today";
        if (name === "format") state.format = ["lines", "counts"].includes(value) ? value : "lines";
        if (name === "filterStart") state.filterStart = String(value || "");
        if (name === "filterEnd") state.filterEnd = String(value || "");
        state.lastStatus = "Filtro do log do Battledome atualizado.";
        saveBattledomeState(state);
        publishBattledomeExternalState("battledome-prize-filter");
        if (root && isMountPage()) render({ reason: "battledome-prize-filter", forceFull: false });
        return state;
    }

    function copyBattledomeCurrentLog() {
        const state = loadBattledomeState();
        const items = getBattledomeFilteredPrizes(state);
        const text = formatBattledomePrizeLog(items, state.format);
        if (!text.trim()) {
            setBattledomeStatus("Não há itens no filtro atual para copiar.");
            return false;
        }
        copyText(text, `${items.length} registro(s) do Battledome copiado(s).`);
        setBattledomeStatus(`${items.length} registro(s) do Battledome preparado(s) para cópia.`);
        return true;
    }

    function clearBattledomePrizes() {
        if (!window.confirm("Zerar o log local de recompensas do Battledome no NCC AAA?")) return false;
        const state = loadBattledomeState();
        state.prizes = [];
        state.lastBatchSignature = "";
        state.lastBatchAt = 0;
        state.lastStatus = "Log local de recompensas do Battledome zerado.";
        saveBattledomeState(state);
        publishBattledomeExternalState("battledome-clear-prizes");
        if (root && isMountPage()) render({ reason: "battledome-clear-prizes", forceFull: true });
        return true;
    }

    function toggleBattledomePrizePaused() {
        const state = loadBattledomeState();
        state.paused = !state.paused;
        state.lastStatus = state.paused ? "Logger de recompensas do Battledome pausado." : "Logger de recompensas do Battledome retomado.";
        saveBattledomeState(state);
        publishBattledomeExternalState("battledome-toggle-prize-paused");
        if (root && isMountPage()) render({ reason: "battledome-toggle-prize-paused", forceFull: false });
        return state.paused;
    }

    function shouldIgnoreBattledomePrize(text) {
        const value = normalizeTrainingText(text || "");
        if (!value) return true;
        if (/\d+[\d,.]*\s+Neopoints/i.test(value)) return true;
        if (/\d+[\d,.]*\s+Plot Points/i.test(value)) return true;
        if (/^inventory$/i.test(value)) return true;
        return false;
    }

    function getBattledomeVisiblePrizeNames(rootNode) {
        const scope = rootNode || document;
        return Array.from(scope.querySelectorAll(".prizname"))
            .map(node => normalizeTrainingText(node.textContent || ""))
            .filter(text => !shouldIgnoreBattledomePrize(text));
    }

    function grabBattledomeItemsFromArena() {
        if (!isBattledomeArenaPage()) return 0;
        const state = loadBattledomeState();
        if (state.paused) return 0;
        const names = getBattledomeVisiblePrizeNames(document);
        if (!names.length) return 0;
        const batchSignature = names.join(" | ");
        const now = Date.now();
        if (state.lastBatchSignature === batchSignature && now - Number(state.lastBatchAt || 0) < 9000) return 0;
        const batchId = `${now}-${Math.random().toString(36).slice(2, 8)}`;
        names.forEach(name => state.prizes.push({ time: now, item: name, batch: batchId }));
        state.lastBatchSignature = batchSignature;
        state.lastBatchAt = now;
        state.lastStatus = `${names.length} item(ns) registrado(s) pelo logger: ${names.join(", ")}.`;
        saveBattledomeState(state);
        publishBattledomeExternalState("battledome-prizes-grabbed");
        if (root && isMountPage()) render({ reason: "battledome-prizes-grabbed", forceFull: false });
        return names.length;
    }

    function startBattledomePrizeObserver() {
        if (!isBattledomeArenaPage()) return false;
        if (battledomePrizeObserverStarted) return true;
        const target = document.getElementById(BATTLEDOME_PRIZE_OBSERVER_TARGET_ID);
        if (!target) {
            battledomePrizeObserverRetryCount += 1;
            if (battledomePrizeObserverRetryCount <= 8) {
                window.setTimeout(() => startBattledomePrizeObserver(), 1500);
            } else {
                setBattledomeStatus("Arena detectada, mas a área de recompensas ainda não apareceu para o logger local.");
            }
            return false;
        }
        battledomePrizeObserverRetryCount = 0;
        battledomePrizeObserverStarted = true;
        try {
            battledomePrizeObserver = new MutationObserver(() => {
                window.setTimeout(() => grabBattledomeItemsFromArena(), 120);
            });
            battledomePrizeObserver.observe(target, { childList: true, subtree: true });
            grabBattledomeItemsFromArena();
            const state = loadBattledomeState();
            if (!state.paused) setBattledomeStatus("Logger local ativo nesta arena. Aguardando recompensas visíveis após batalhas.");
            return true;
        } catch (error) {
            bootErrors.push(`battledome-prize-observer:${errorToMessage(error)}`);
            console.warn("[NCC AAA] Falha no observer local de recompensas do Battledome.", error);
            return false;
        }
    }

    function bootBattledomePrizeLogger(reason) {
        if (!isBattledomeArenaPage()) return false;
        const run = () => {
            try {
                startBattledomePrizeObserver();
                publishBattledomeExternalState(reason || "boot-battledome-prize-logger");
            } catch (error) {
                bootErrors.push(`battledome-prize-logger:${errorToMessage(error)}`);
                console.warn("[NCC AAA] Falha ao iniciar logger local de recompensas do Battledome.", error);
            }
        };
        if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", () => window.setTimeout(run, 700), { once: true });
        } else {
            window.setTimeout(run, 700);
        }
        return true;
    }


    function normalizeBattledomeWeaponImage(src) {
        try {
            const url = new URL(String(src || ""), location.origin);
            return url.pathname.split("/").pop().toLowerCase();
        } catch (_) {
            return String(src || "").split("/").pop().toLowerCase();
        }
    }

    function getBattledomeWeaponDataFromImage(img) {
        if (!img) return null;
        const src = img.getAttribute("src") || img.src || "";
        const name = normalizeTrainingText(img.getAttribute("alt") || img.title || "");
        const image = normalizeBattledomeWeaponImage(src);
        const id = String(img.id || "").trim();
        if (!name && !image) return null;
        return {
            id,
            name,
            image,
            src: normalizeBattledomeAbsoluteUrl(src)
        };
    }

    function battledomeWeaponsMatch(a, b) {
        if (!a || !b) return false;
        const ai = String(a.image || "").toLowerCase();
        const bi = String(b.image || "").toLowerCase();
        const an = String(a.name || "").toLowerCase();
        const bn = String(b.name || "").toLowerCase();
        if (ai && bi && ai === bi) return true;
        if (an && bn && an === bn) return true;
        return false;
    }

    function getBattledomeArenaWeaponImages(rootNode) {
        const scope = rootNode || document;
        return Array.from(scope.querySelectorAll("#p1equipment img.item")).filter(img => {
            const data = getBattledomeWeaponDataFromImage(img);
            return Boolean(data && data.id && (data.name || data.image));
        });
    }

    function ensureBattledomeLoadoutStyle() {
        if (document.getElementById(BATTLEDOME_LOADOUT_STYLE_ID)) return;
        const style = document.createElement("style");
        style.id = BATTLEDOME_LOADOUT_STYLE_ID;
        style.textContent = `
            #p1equipment li.ncc-aaa-bd-loadout-li { position: relative !important; }
            #p1equipment .ncc-aaa-bd-loadout-heart {
                position: absolute; top: -7px; right: -7px; z-index: 9999;
                width: 20px; height: 20px; border-radius: 999px;
                border: 1px solid #7b4fd9; background: rgba(255,255,255,.97); color: #7b4fd9;
                box-shadow: 0 1px 5px rgba(0,0,0,.25); font: 900 14px/17px Arial, sans-serif;
                text-align: center; padding: 0; cursor: pointer;
            }
            #p1equipment .ncc-aaa-bd-loadout-heart.active { background: #fff2b7; color: #d32f6a; border-color: #e5ad2d; }
            #p1equipment .ncc-aaa-bd-loadout-heart:hover { transform: scale(1.08); filter: brightness(1.05); }
            .ncc-aaa-bd-loadout-toast {
                position: fixed; left: 50%; top: 18px; transform: translateX(-50%); z-index: 2147483647;
                max-width: 560px; border-radius: 999px; padding: 8px 14px;
                background: #3d247f; color: #fff; border: 2px solid #b99cff;
                box-shadow: 0 4px 16px rgba(0,0,0,.28); font: 900 12px Arial, sans-serif; text-align: center;
            }
            .ncc-aaa-bd-loadout-toast.warn { background: #6b3b00; border-color: #ffd066; }
        `;
        document.head.appendChild(style);
    }

    function showBattledomeLoadoutToast(message, tone) {
        document.getElementById(BATTLEDOME_LOADOUT_TOAST_ID)?.remove();
        const toast = document.createElement("div");
        toast.id = BATTLEDOME_LOADOUT_TOAST_ID;
        toast.className = `ncc-aaa-bd-loadout-toast ${tone === "warn" ? "warn" : ""}`;
        toast.textContent = String(message || "");
        document.body.appendChild(toast);
        window.setTimeout(() => toast.remove(), 3400);
    }

    function decorateBattledomeLoadoutWeaponsWhenReady(attempt) {
        if (!isBattledomeArenaPage()) return 0;
        const tries = Number(attempt || 0);
        const weapons = getBattledomeArenaWeaponImages(document);
        if (!weapons.length) {
            if (tries < 30) window.setTimeout(() => decorateBattledomeLoadoutWeaponsWhenReady(tries + 1), 500);
            return 0;
        }
        return decorateBattledomeLoadoutWeapons();
    }

    function decorateBattledomeLoadoutWeapons() {
        if (!isBattledomeArenaPage()) return 0;
        const weapons = getBattledomeArenaWeaponImages(document);
        if (!weapons.length) return 0;
        ensureBattledomeLoadoutStyle();
        const state = loadBattledomeState();
        const favorites = Array.isArray(state.loadoutWeapons) ? state.loadoutWeapons : [];
        weapons.forEach(img => {
            const data = getBattledomeWeaponDataFromImage(img);
            if (!data) return;
            const holder = img.closest("li") || img.parentElement;
            if (!holder) return;
            holder.classList.add("ncc-aaa-bd-loadout-li");
            let heart = holder.querySelector(":scope > .ncc-aaa-bd-loadout-heart") || holder.querySelector(".ncc-aaa-bd-loadout-heart");
            if (!heart) {
                heart = document.createElement("button");
                heart.type = "button";
                heart.className = "ncc-aaa-bd-loadout-heart";
                heart.dataset.nccAaaBdLoadoutAction = "toggle-loadout-weapon";
                holder.appendChild(heart);
            }
            const active = favorites.some(fav => battledomeWeaponsMatch(fav, data));
            heart.textContent = active ? "♥" : "♡";
            heart.classList.toggle("active", active);
            heart.title = active ? `Remover ${data.name || data.image} do loadout favorito` : `Favoritar ${data.name || data.image} para o loadout`;
            heart.setAttribute("aria-label", heart.title);
        });
        return weapons.length;
    }

    function toggleBattledomeLoadoutWeaponFromImage(img) {
        const weapon = getBattledomeWeaponDataFromImage(img);
        if (!weapon) return false;
        const state = loadBattledomeState();
        const weapons = Array.isArray(state.loadoutWeapons) ? state.loadoutWeapons.slice(0, 2) : [];
        const index = weapons.findIndex(item => battledomeWeaponsMatch(item, weapon));
        if (index >= 0) {
            const removed = weapons.splice(index, 1)[0];
            state.lastStatus = `Arma removida do loadout: ${removed.name || removed.image}.`;
            showBattledomeLoadoutToast(state.lastStatus);
        } else {
            if (weapons.length >= 2) {
                state.lastStatus = "Você já tem 2 armas favoritas. Remova uma antes de marcar outra.";
                saveBattledomeState(state);
                showBattledomeLoadoutToast(state.lastStatus, "warn");
                publishBattledomeExternalState("battledome-loadout-limit");
                if (root && isMountPage()) render({ reason: "battledome-loadout-limit", forceFull: false });
                return false;
            }
            weapons.push({ name: weapon.name, image: weapon.image, src: weapon.src, savedAt: Date.now() });
            state.lastStatus = `Arma adicionada ao loadout: ${weapon.name || weapon.image}.`;
            showBattledomeLoadoutToast(state.lastStatus);
        }
        state.loadoutWeapons = weapons;
        saveBattledomeState(state);
        decorateBattledomeLoadoutWeapons();
        publishBattledomeExternalState("battledome-toggle-loadout-weapon");
        if (root && isMountPage()) render({ reason: "battledome-toggle-loadout-weapon", forceFull: false });
        return true;
    }

    function resetBattledomeArenaWeaponVisibility() {
        getBattledomeArenaWeaponImages(document).forEach(img => {
            const holder = img.closest("li") || img.parentElement;
            if (holder) holder.style.display = "";
            img.classList.remove("hide", "ncc-aaa-bd-loadout-hidden");
        });
    }

    function selectBattledomeArenaWeaponForSlot(img, slotIndex) {
        const data = getBattledomeWeaponDataFromImage(img);
        if (!data || !data.id) return false;
        const input = slotIndex === 0 ? document.getElementById("p1e1") : document.getElementById("p1e2");
        const menu = slotIndex === 0 ? document.getElementById("p1e1m") : document.getElementById("p1e2m");
        const menuDiv = menu?.querySelector?.("div");
        if (!input || !menu || !menuDiv) return false;
        input.value = data.id;
        menu.classList.add("selected");
        menuDiv.style.backgroundImage = `url("${data.src}")`;
        menuDiv.style.backgroundSize = "60px 60px";
        menuDiv.style.backgroundPosition = "0px 0px";
        menuDiv.style.opacity = "1";
        const holder = img.closest("li") || img.parentElement;
        if (holder) holder.style.display = "none";
        img.classList.add("hide", "ncc-aaa-bd-loadout-hidden");
        return true;
    }

    function applyBattledomeLoadoutWhenReady(attempt, force) {
        if (!isBattledomeArenaPage()) return 0;
        const state = loadBattledomeState();
        if (!state.loadoutEnabled || !state.loadoutWeapons.length) return 0;
        if (battledomeLoadoutAppliedThisArena && !force) return 0;
        const tries = Number(attempt || 0);
        const weapons = getBattledomeArenaWeaponImages(document);
        if (!weapons.length || !document.getElementById("p1e1") || !document.getElementById("p1e2") || !document.getElementById("p1e1m") || !document.getElementById("p1e2m")) {
            if (tries < 60) window.setTimeout(() => applyBattledomeLoadoutWhenReady(tries + 1, Boolean(force)), 600);
            return 0;
        }
        const showIntro = String(document.getElementById("showintro")?.value || "") === "1";
        const introVisible = Boolean(document.getElementById("introdiv"));
        if (!force && (showIntro || introVisible)) {
            if (tries < 80) window.setTimeout(() => applyBattledomeLoadoutWhenReady(tries + 1, false), 600);
            return 0;
        }
        return applyBattledomeFavoriteLoadout({ force: Boolean(force) });
    }

    function applyBattledomeFavoriteLoadout(options) {
        const opts = Object.assign({ force: false }, options || {});
        if (!isBattledomeArenaPage()) {
            setBattledomeStatus("Abra uma luta ativa no Battledome para aplicar o loadout favorito.");
            return 0;
        }
        const state = loadBattledomeState();
        if (!state.loadoutEnabled) {
            setBattledomeStatus("Pré-seleção de armas do Battledome está desligada.");
            return 0;
        }
        const favorites = Array.isArray(state.loadoutWeapons) ? state.loadoutWeapons.slice(0, 2) : [];
        if (!favorites.length) {
            setBattledomeStatus("Nenhuma arma favorita marcada. Use os corações na lista de equipamentos da arena.");
            return 0;
        }
        const arenaWeapons = getBattledomeArenaWeaponImages(document);
        decorateBattledomeLoadoutWeapons();
        const matched = [];
        favorites.forEach(fav => {
            const found = arenaWeapons.find(img => {
                if (matched.some(item => item.img === img)) return false;
                return battledomeWeaponsMatch(fav, getBattledomeWeaponDataFromImage(img));
            });
            if (found) matched.push({ favorite: fav, img: found });
        });
        if (!matched.length) {
            setBattledomeStatus("Nenhuma arma favorita foi encontrada nos equipamentos desta luta.");
            showBattledomeLoadoutToast("Nenhuma arma favorita encontrada nesta arena.", "warn");
            return 0;
        }
        resetBattledomeArenaWeaponVisibility();
        const appliedNames = [];
        matched.slice(0, 2).forEach((entry, index) => {
            if (selectBattledomeArenaWeaponForSlot(entry.img, index)) {
                const data = getBattledomeWeaponDataFromImage(entry.img);
                appliedNames.push(data?.name || entry.favorite.name || entry.favorite.image);
            }
        });
        if (!appliedNames.length) {
            setBattledomeStatus("Não consegui preencher os campos visuais do loadout nesta arena.");
            return 0;
        }
        state.loadoutLastAppliedAt = Date.now();
        state.lastStatus = `Loadout pré-selecionado visualmente: ${appliedNames.join(" + ")}. Confira e clique em Fight manualmente se desejar.`;
        battledomeLoadoutAppliedThisArena = true;
        saveBattledomeState(state);
        showBattledomeLoadoutToast(state.lastStatus);
        publishBattledomeExternalState(opts.force ? "battledome-loadout-manual-apply" : "battledome-loadout-auto-apply");
        if (root && isMountPage()) render({ reason: "battledome-loadout-apply", forceFull: false });
        return appliedNames.length;
    }

    function toggleBattledomeLoadoutEnabled() {
        const state = loadBattledomeState();
        state.loadoutEnabled = !state.loadoutEnabled;
        state.lastStatus = state.loadoutEnabled ? "Pré-seleção de armas ligada." : "Pré-seleção de armas desligada.";
        saveBattledomeState(state);
        if (isBattledomeArenaPage()) {
            decorateBattledomeLoadoutWeapons();
            if (state.loadoutEnabled) applyBattledomeLoadoutWhenReady(0, true);
        }
        publishBattledomeExternalState("battledome-toggle-loadout-enabled");
        if (root && isMountPage()) render({ reason: "battledome-toggle-loadout-enabled", forceFull: false });
        return state.loadoutEnabled;
    }

    function clearBattledomeLoadoutWeapons() {
        if (!window.confirm("Limpar as armas favoritas do Battledome no NCC AAA?")) return false;
        const state = loadBattledomeState();
        state.loadoutWeapons = [];
        state.loadoutLastAppliedAt = 0;
        state.lastStatus = "Loadout favorito de armas limpo.";
        saveBattledomeState(state);
        if (isBattledomeArenaPage()) decorateBattledomeLoadoutWeapons();
        publishBattledomeExternalState("battledome-clear-loadout");
        if (root && isMountPage()) render({ reason: "battledome-clear-loadout", forceFull: true });
        return true;
    }

    function bindBattledomeLoadoutClicks() {
        if (battledomeLoadoutClickBound) return;
        battledomeLoadoutClickBound = true;
        document.addEventListener("click", event => {
            const button = event.target?.closest?.(".ncc-aaa-bd-loadout-heart[data-ncc-aaa-bd-loadout-action='toggle-loadout-weapon']");
            if (!button || !isBattledomeArenaPage()) return;
            event.preventDefault();
            event.stopPropagation();
            const holder = button.closest("li") || button.parentElement;
            const img = holder?.querySelector?.("img.item");
            toggleBattledomeLoadoutWeaponFromImage(img);
        }, true);
    }

    function bootBattledomeLoadout(reason) {
        if (!isBattledomeArenaPage()) return false;
        ensureBattledomeLoadoutStyle();
        bindBattledomeLoadoutClicks();
        const run = () => {
            try {
                decorateBattledomeLoadoutWeaponsWhenReady(0);
                applyBattledomeLoadoutWhenReady(0, false);
                buildBattledomeTrackerAuditReport(reason || "boot-battledome-loadout");
                publishBattledomeExternalState(reason || "boot-battledome-loadout");
            } catch (error) {
                bootErrors.push(`battledome-loadout:${errorToMessage(error)}`);
                console.warn("[NCC AAA] Falha no loadout visual do Battledome.", error);
            }
        };
        if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", () => window.setTimeout(run, 700), { once: true });
        } else {
            window.setTimeout(run, 700);
        }
        return true;
    }


    function getBattledomeAssistedTestDefaultState() {
        return { schema: "ncc-aaa-battledome-assisted-test-v1", version: VERSION, updatedAt: 0, updatedAtIso: "", lastStatus: "Checklist ainda não iniciado.", steps: {} };
    }

    function loadBattledomeAssistedTestState() {
        const fallback = getBattledomeAssistedTestDefaultState();
        const data = readJson(BATTLEDOME_ASSISTED_TEST_KEY, fallback) || fallback;
        if (!data || typeof data !== "object") return fallback;
        if (!data.steps || typeof data.steps !== "object" || Array.isArray(data.steps)) data.steps = {};
        data.schema = data.schema || fallback.schema;
        data.version = VERSION;
        data.updatedAt = normalizeNumber(data.updatedAt, 0, 0);
        data.updatedAtIso = String(data.updatedAtIso || "");
        data.lastStatus = String(data.lastStatus || fallback.lastStatus);
        return data;
    }

    function saveBattledomeAssistedTestState(data) {
        const state = Object.assign(getBattledomeAssistedTestDefaultState(), data || {});
        if (!state.steps || typeof state.steps !== "object" || Array.isArray(state.steps)) state.steps = {};
        state.version = VERSION;
        state.updatedAt = Date.now();
        state.updatedAtIso = isoNow();
        writeJson(BATTLEDOME_ASSISTED_TEST_KEY, state);
        return state;
    }

    function getBattledomeAssistedTestStepDefinitions() {
        return [
            { id: "preflight", title: "Pré-teste", description: "Valida versão, bridge e estado local antes de mexer nas páginas do Battledome.", steps: [
                { id: "preflight-version", label: "Versão instalada", expected: "NCC AAA v0.1.63 aparece instalado e a página principal abre normalmente." },
                { id: "preflight-bridge", label: "Bridge publicada", expected: "window.NCCAAA responde e getBattledomeTrackerAudit() existe no console." },
                { id: "preflight-state", label: "Estado Battledome publicado", expected: "window.NCC_AAA_BATTLEDOME_STATE existe após abrir o NCC AAA." }
            ]},
            { id: "opponents", title: "Oponentes favoritos", description: "Teste em /dome/fight.phtml; nenhum oponente pode ser escolhido automaticamente.", steps: [
                { id: "opp-open", label: "Abrir página de oponentes", expected: "A página /dome/fight.phtml carrega com linhas de oponentes visíveis." },
                { id: "opp-stars", label: "Estrelas inseridas", expected: "Cada oponente recebe botão ☆/★ sem quebrar a seleção original do Neopets." },
                { id: "opp-favorite", label: "Favoritar", expected: "Ao clicar em ☆, o oponente vira favorito e sobe para Favoritos do NCC AAA." },
                { id: "opp-unfavorite", label: "Desfavoritar", expected: "Ao clicar em ★, o oponente volta para a lista comum." },
                { id: "opp-no-autoselect", label: "Sem escolha automática", expected: "Nenhum oponente é selecionado automaticamente e nenhum formulário é enviado." }
            ]},
            { id: "logger", title: "Logger de recompensas", description: "Teste em /dome/arena.phtml depois que recompensas aparecerem visualmente.", steps: [
                { id: "log-open", label: "Abrir arena ativa", expected: "A página /dome/arena.phtml tem área #bd_rewardsloot disponível ou aparece após a luta." },
                { id: "log-visible-items", label: "Registrar itens visíveis", expected: "Itens em .prizname entram no log local após aparecerem na tela." },
                { id: "log-ignore-non-items", label: "Ignorar não-itens", expected: "Neopoints, Plot Points, Inventory e textos vazios não entram como item." },
                { id: "log-filters", label: "Filtros e formato", expected: "Hoje NST, últimos 7 dias, tudo, intervalo e contagem por item funcionam." },
                { id: "log-copy-pause", label: "Copiar/pausar", expected: "Copiar log funciona e pausar impede novos registros locais." }
            ]},
            { id: "loadout", title: "Loadout favorito visual", description: "Teste em arena ativa; o script só pode pré-selecionar campos de arma.", steps: [
                { id: "loadout-hearts", label: "Corações nas armas", expected: "As armas em #p1equipment img.item recebem corações ♥/♡." },
                { id: "loadout-two", label: "Limite de 2 armas", expected: "É possível marcar 1 ou 2 armas; a terceira é bloqueada com aviso." },
                { id: "loadout-apply", label: "Aplicar loadout", expected: "#p1e1/#p1e2 e #p1e1m/#p1e2m são preenchidos visualmente." },
                { id: "loadout-no-fight", label: "Sem Fight automático", expected: "O botão Fight não é clicado, inactive não é removido à força e nada é enviado." },
                { id: "loadout-clear", label: "Limpar loadout", expected: "Limpar remove apenas o storage local do NCC AAA." }
            ]},
            { id: "regression", title: "Regressão NCC AAA", description: "Confirma que Battledome não afetou Training, Avatar Modal ou a Arena principal.", steps: [
                { id: "reg-arena", label: "Arena abre limpa", expected: "Arena mostra Training Assistant e Battledome sem erro visual pesado." },
                { id: "reg-training", label: "Training preservado", expected: "SDB, pagar, completar e iniciar cursos continuam exigindo clique/confirm." },
                { id: "reg-avatar", label: "Avatar modal preservado", expected: "O lápis do avatar abre o popup de escolha normalmente." },
                { id: "reg-no-base", label: "NCC Base intocado", expected: "Nada desta versão altera o NCC Base." }
            ]}
        ];
    }

    function buildBattledomeAssistedTestChecklist(reason) {
        const saved = loadBattledomeAssistedTestState();
        const bdState = loadBattledomeState();
        const allSteps = [];
        const sections = getBattledomeAssistedTestStepDefinitions().map(section => {
            const steps = section.steps.map(step => {
                const savedStep = saved.steps?.[step.id] || {};
                const status = ["pending", "pass", "fail", "skip"].includes(savedStep.status) ? savedStep.status : "pending";
                const view = Object.assign({}, step, { status, checkedAt: normalizeNumber(savedStep.checkedAt, 0, 0), checkedAtIso: String(savedStep.checkedAtIso || ""), checkedPath: String(savedStep.checkedPath || "") });
                allSteps.push(view);
                return view;
            });
            return Object.assign({}, section, { steps });
        });
        const progress = { total: allSteps.length, passed: allSteps.filter(s => s.status === "pass").length, failed: allSteps.filter(s => s.status === "fail").length, skipped: allSteps.filter(s => s.status === "skip").length, pending: allSteps.filter(s => s.status === "pending").length };
        const environment = {
            currentPath: location.pathname,
            isMountPage: isMountPage(),
            isBattledomeFightPage: isBattledomeFightPage(),
            isBattledomeArenaPage: isBattledomeArenaPage(),
            detectedOpponentRows: document.querySelectorAll?.(".npcRow[data-oppid], tr[data-oppid]")?.length || 0,
            detectedPrizeNames: document.querySelectorAll?.(".prizname")?.length || 0,
            detectedRewardArea: Boolean(document.getElementById("bd_rewardsloot")),
            detectedWeaponImages: document.querySelectorAll?.("#p1equipment img.item")?.length || 0,
            loadoutFields: { p1e1: Boolean(document.getElementById("p1e1")), p1e2: Boolean(document.getElementById("p1e2")), p1e1m: Boolean(document.getElementById("p1e1m")), p1e2m: Boolean(document.getElementById("p1e2m")) }
        };
        return clonePlain({ schema: "ncc-aaa-battledome-assisted-test-checklist-v1", version: VERSION, source: "ncc-aaa", sourceModule: "battledome-tracker-assisted-test-mode", reason: reason || "assisted-test-checklist", updatedAt: Date.now(), updatedAtIso: isoNow(), storageKey: BATTLEDOME_ASSISTED_TEST_KEY, moduleId: "battledomeTracker", safetyClass: BATTLEDOME_TRACKER_CONTRACT.safetyClass, environment, summary: { prizes: Array.isArray(bdState.prizes) ? bdState.prizes.length : 0, opponents: Array.isArray(bdState.opponents) ? bdState.opponents.length : 0, favoriteOpponents: Array.isArray(bdState.favoriteOpponentIds) ? bdState.favoriteOpponentIds.length : 0, loadoutWeapons: Array.isArray(bdState.loadoutWeapons) ? bdState.loadoutWeapons.length : 0, loadoutEnabled: bdState.loadoutEnabled !== false, loggerPaused: Boolean(bdState.paused), lastStatus: String(bdState.lastStatus || "") }, progress, sections });
    }

    function setBattledomeAssistedTestStepStatus(stepId, status) {
        const id = String(stepId || "").trim();
        const normalizedStatus = ["pending", "pass", "fail", "skip"].includes(status) ? status : "pending";
        const knownIds = new Set(getBattledomeAssistedTestStepDefinitions().flatMap(section => section.steps.map(step => step.id)));
        if (!id || !knownIds.has(id)) { showToast("Item de teste Battledome não reconhecido."); return false; }
        const state = loadBattledomeAssistedTestState();
        state.steps[id] = { status: normalizedStatus, checkedAt: Date.now(), checkedAtIso: isoNow(), checkedPath: location.pathname };
        state.lastStatus = `Teste ${id}: ${normalizedStatus}.`;
        saveBattledomeAssistedTestState(state);
        publishBattledomeExternalState("battledome-assisted-test-step");
        if (root && isMountPage()) render({ reason: "battledome-assisted-test-step", forceFull: false });
        return true;
    }

    function resetBattledomeAssistedTestChecklist() {
        if (!window.confirm("Resetar o checklist local do teste assistido do Battledome?")) return false;
        const state = getBattledomeAssistedTestDefaultState();
        state.lastStatus = "Checklist resetado.";
        saveBattledomeAssistedTestState(state);
        publishBattledomeExternalState("battledome-assisted-test-reset");
        if (root && isMountPage()) render({ reason: "battledome-assisted-test-reset", forceFull: true });
        return true;
    }

    function formatBattledomeAssistedTestReport(checklist) {
        const lines = [];
        lines.push(`NCC AAA Battledome Assisted Test — v${VERSION}`);
        lines.push(`Gerado em: ${checklist.updatedAtIso}`);
        lines.push(`Página atual: ${checklist.environment.currentPath}`);
        lines.push(`Progresso: ${checklist.progress.passed}/${checklist.progress.total} OK · ${checklist.progress.failed} falha(s) · ${checklist.progress.skipped} pulado(s) · ${checklist.progress.pending} pendente(s)`);
        lines.push("");
        lines.push("Resumo local:");
        lines.push(`- Prêmios salvos: ${checklist.summary.prizes}`);
        lines.push(`- Oponentes sincronizados: ${checklist.summary.opponents}`);
        lines.push(`- Favoritos: ${checklist.summary.favoriteOpponents}`);
        lines.push(`- Loadout: ${checklist.summary.loadoutWeapons}/2 arma(s), ${checklist.summary.loadoutEnabled ? "ligado" : "desligado"}`);
        lines.push(`- Logger: ${checklist.summary.loggerPaused ? "pausado" : "ativo"}`);
        lines.push("");
        checklist.sections.forEach(section => {
            lines.push(`[${section.title}]`);
            section.steps.forEach(step => lines.push(`- ${step.status.toUpperCase()} · ${step.label}: ${step.expected}${step.checkedAtIso ? ` · marcado em ${step.checkedAtIso} em ${step.checkedPath}` : ""}`));
            lines.push("");
        });
        lines.push("Critérios obrigatórios: sem Fight automático, sem escolha automática de oponente, sem habilidade automática, sem coleta automática, sem submit, sem fetch e sem POST.");
        return lines.join("\n");
    }

    function copyBattledomeAssistedTestReport() {
        const checklist = buildBattledomeAssistedTestChecklist("copy-assisted-test-report");
        const text = formatBattledomeAssistedTestReport(checklist);
        copyText(text, "Relatório do teste Battledome copiado.");
        return text;
    }

    function buildBattledomeManualTestPlan(reason) {
        const plan = clonePlain({
            schema: "ncc-aaa-battledome-manual-test-plan-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "battledome-tracker-assisted-test-mode",
            reason: reason || "manual-test-plan",
            updatedAt: Date.now(),
            updatedAtIso: isoNow(),
            moduleId: "battledomeTracker",
            safetyClass: BATTLEDOME_TRACKER_CONTRACT.safetyClass,
            prerequisite: "Instalar esta versão, abrir o NCC AAA em /~Yillcivi e manter o teste manual dentro das páginas oficiais do Battledome.",
            pages: clonePlain(BATTLEDOME_TRACKER_CONTRACT.pages),
            sections: [
                {
                    id: "opponents",
                    title: "Oponentes favoritos",
                    path: BATTLEDOME_FIGHT_PATH,
                    steps: [
                        "Abrir /dome/fight.phtml.",
                        "Confirmar que as estrelas aparecem nas linhas de oponentes.",
                        "Favoritar um oponente e confirmar que ele sobe para a área Favoritos do NCC AAA.",
                        "Desfavoritar o mesmo oponente e confirmar que a linha volta para a lista comum.",
                        "Voltar ao NCC AAA e conferir se os contadores de sincronizados/favoritos foram atualizados."
                    ],
                    mustRemainTrue: [
                        "Nenhum oponente deve ser escolhido automaticamente.",
                        "Nenhum botão Fight deve ser clicado.",
                        "Nenhum formulário oficial deve ser enviado."
                    ]
                },
                {
                    id: "prize-logger",
                    title: "Logger de recompensas visíveis",
                    path: BATTLEDOME_ARENA_PATH,
                    steps: [
                        "Abrir uma batalha real em /dome/arena.phtml.",
                        "Finalizar uma batalha manualmente pelo Neopets.",
                        "Confirmar que itens visíveis em #bd_rewardsloot/.prizname entram no log local.",
                        "Confirmar que Neopoints, Plot Points, Inventory e textos vazios não entram como item.",
                        "Testar filtros Hoje NST, Últimos 7 dias, Tudo e intervalo manual.",
                        "Copiar log e confirmar que o texto copiado bate com o filtro atual.",
                        "Pausar logger, concluir outra ação manual e confirmar que nada novo é salvo enquanto pausado."
                    ],
                    mustRemainTrue: [
                        "O logger apenas observa DOM visível.",
                        "Nenhuma recompensa deve ser coletada automaticamente.",
                        "Nenhuma requisição oficial deve ser feita pelo logger."
                    ]
                },
                {
                    id: "loadout",
                    title: "Loadout favorito visual",
                    path: BATTLEDOME_ARENA_PATH,
                    steps: [
                        "Abrir /dome/arena.phtml durante uma batalha ativa com equipamentos visíveis.",
                        "Confirmar que corações aparecem sobre armas em #p1equipment img.item.",
                        "Marcar uma arma favorita.",
                        "Marcar uma segunda arma favorita.",
                        "Tentar marcar uma terceira arma e confirmar que o script bloqueia pelo limite de 2.",
                        "Usar Aplicar loadout e conferir se #p1e1/#p1e2 e #p1e1m/#p1e2m foram preenchidos visualmente.",
                        "Conferir manualmente antes de clicar em Fight pelo próprio site."
                    ],
                    mustRemainTrue: [
                        "O script não deve clicar em Fight.",
                        "O script não deve remover inactive do botão Fight.",
                        "O script não deve escolher habilidade.",
                        "O script não deve enviar formulário."
                    ]
                },
                {
                    id: "regression",
                    title: "Regressão Arena/Training",
                    path: MOUNT_PATH,
                    steps: [
                        "Abrir o NCC AAA em /~Yillcivi.",
                        "Confirmar que a Arena carrega Training Assistant e Battledome Tracker.",
                        "Confirmar que o popup de avatar continua abrindo pelo lápis.",
                        "Confirmar que as ações de Training continuam exigindo clique e confirmação."
                    ],
                    mustRemainTrue: [
                        "Training Assistant, SDB, pagamento, conclusão e início de curso não devem ter comportamento alterado pelo Battledome.",
                        "O NCC Base não deve ser alterado por esta versão."
                    ]
                }
            ],
            finalPassCriteria: [
                "Favoritos sobem ao topo sem escolher oponente.",
                "Logger registra apenas recompensas já visíveis.",
                "Loadout pré-seleciona no máximo 2 armas sem clicar em Fight.",
                "NCC AAA publica window.NCC_AAA_BATTLEDOME_STATE e window.NCCAAA.getBattledomeTrackerAudit().",
                "Nenhuma ação oficial de batalha acontece sem o usuário agir manualmente no Neopets."
            ]
        });
        writeJson(BATTLEDOME_MANUAL_TEST_PLAN_KEY, plan);
        return plan;
    }

    function buildBattledomeTrackerAuditReport(reason) {
        const state = loadBattledomeState();
        const report = clonePlain({
            schema: "ncc-aaa-battledome-tracker-audit-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "battledome-tracker-assisted-test-mode",
            reason: reason || "unknown",
            updatedAt: Date.now(),
            updatedAtIso: isoNow(),
            moduleId: "battledomeTracker",
            safetyClass: BATTLEDOME_TRACKER_CONTRACT.safetyClass,
            allowedPaths: Array.from(BATTLEDOME_TRACKER_CONTRACT.allowedPaths || []),
            checks: {
                noFetch: true,
                noXmlHttpRequest: true,
                noGmXmlHttpRequest: true,
                noPost: true,
                noSubmit: true,
                noProgrammaticClick: true,
                noFightClick: true,
                noOpponentAutoChoice: true,
                noAbilityAutoUse: true,
                noPrizeCollect: true,
                clientLocalWeaponPreselection: true,
                conservativeFightButtonPolicy: "does-not-remove-inactive-and-does-not-click-fight"
            },
            runtime: {
                onBattledomePage: isBattledomePage(),
                onFightPage: isBattledomeFightPage(),
                onArenaPage: isBattledomeArenaPage(),
                opponentFavoritesEnabled: true,
                prizeLoggerEnabled: true,
                loadoutEnabled: state.loadoutEnabled !== false,
                loadoutWeapons: state.loadoutWeapons.length,
                observerActive: Boolean(battledomePrizeObserverStarted)
            },
            storageKey: BATTLEDOME_STORAGE_KEY,
            reportKey: BATTLEDOME_REPORT_KEY,
            externalStateKey: BATTLEDOME_EXTERNAL_STATE_KEY,
            testPlanKey: BATTLEDOME_MANUAL_TEST_PLAN_KEY,
            assistedTestKey: BATTLEDOME_ASSISTED_TEST_KEY,
            manualTestPlan: buildBattledomeManualTestPlan(reason || "audit-test-plan"),
            assistedTest: buildBattledomeAssistedTestChecklist(reason || "audit-assisted-test"),
            contract: BATTLEDOME_TRACKER_CONTRACT
        });
        writeJson(BATTLEDOME_AUDIT_KEY, report);
        return report;
    }

    function buildBattledomeTrackerReport(reason) {
        const data = loadBattledomeState();
        lastBattledomeTrackerReport = clonePlain({
            schema: "ncc-aaa-battledome-tracker-report-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "battledome-tracker-assisted-test-mode",
            moduleId: "battledomeTracker",
            reason: reason || "unknown",
            updatedAt: Date.now(),
            updatedAtIso: isoNow(),
            currentPath: location.pathname,
            onBattledomePage: isBattledomePage(),
            foundationInstalled: true,
            runtimeStage: "opponent-favorites-prize-logger-and-loadout",
            runtimeActive: isBattledomeFightPage() || isBattledomeArenaPage(),
            opponentFavoritesEnabled: true,
            prizeLoggerEnabled: true,
            loadoutEnabled: true,
            loadoutPolicy: "client-local-preselection-only-no-fight-click",
            observerActive: Boolean(battledomePrizeObserverStarted),
            networkAccess: "none",
            formSubmit: false,
            programmaticClick: false,
            officialActionAvailable: false,
            storageKey: BATTLEDOME_STORAGE_KEY,
            counts: {
                prizes: data.prizes.length,
                opponents: data.opponents.length,
                favoriteOpponents: data.favoriteOpponentIds.length,
                loadoutWeapons: data.loadoutWeapons.length
            },
            state: data,
            contract: BATTLEDOME_TRACKER_CONTRACT,
            audit: buildBattledomeTrackerAuditReport(reason || "module-report"),
            manualTestPlanKey: BATTLEDOME_MANUAL_TEST_PLAN_KEY,
            assistedTestKey: BATTLEDOME_ASSISTED_TEST_KEY,
            assistedTestProgress: buildBattledomeAssistedTestChecklist(reason || "module-report").progress,
            manualTestPlanSections: buildBattledomeManualTestPlan(reason || "module-report").sections.map(section => section.id)
        });
        writeJson(BATTLEDOME_REPORT_KEY, lastBattledomeTrackerReport);
        return lastBattledomeTrackerReport;
    }

    function buildBattledomeExternalState(reason) {
        const report = buildBattledomeTrackerReport(reason || "external-state");
        return clonePlain({
            schema: BATTLEDOME_STATE_SCHEMA,
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "battledome-tracker",
            moduleId: "battledomeTracker",
            reason: reason || "unknown",
            updatedAt: report.updatedAt,
            updatedAtIso: report.updatedAtIso,
            ready: true,
            active: isBattledomeFightPage() || isBattledomeArenaPage(),
            status: "opponent-favorites-prize-logger-and-loadout-active",
            safetyClass: BATTLEDOME_TRACKER_CONTRACT.safetyClass,
            allowedPaths: Array.from(BATTLEDOME_TRACKER_CONTRACT.allowedPaths || []),
            pages: clonePlain(BATTLEDOME_TRACKER_CONTRACT.pages),
            officialActions: clonePlain(BATTLEDOME_TRACKER_CONTRACT.officialActions),
            stage: clonePlain(BATTLEDOME_TRACKER_CONTRACT.stageOneRuntime),
            storageKey: BATTLEDOME_STORAGE_KEY,
            reportKey: BATTLEDOME_REPORT_KEY,
            auditKey: BATTLEDOME_AUDIT_KEY,
            manualTestPlanKey: BATTLEDOME_MANUAL_TEST_PLAN_KEY,
            assistedTestKey: BATTLEDOME_ASSISTED_TEST_KEY,
            assistedTestProgress: buildBattledomeAssistedTestChecklist(reason || "external-state").progress,
            eventName: BATTLEDOME_EXTERNAL_STATE_EVENT,
            globalName: GLOBAL_BATTLEDOME_STATE_NAME,
            report,
            summary: {
                prizes: report.counts.prizes,
                opponents: report.counts.opponents,
                favoriteOpponents: report.counts.favoriteOpponents,
                loadoutWeapons: report.counts.loadoutWeapons,
                loadoutEnabled: report.state.loadoutEnabled !== false,
                prizeLoggerPaused: report.state.paused === true,
                observerActive: report.observerActive === true,
                message: "Favoritos de oponentes, logger local e loadout visual de até 2 armas ativos sem ação oficial de batalha."
            }
        });
    }

    function publishBattledomeExternalState(reason) {
        try {
            const battledomeState = buildBattledomeExternalState(reason);
            writeJson(BATTLEDOME_EXTERNAL_STATE_KEY, battledomeState);
            publishBattledomeExternalStateToGlobals(battledomeState);
            dispatchBattledomeExternalState(battledomeState);
            return battledomeState;
        } catch (error) {
            bootErrors.push(`battledome-state:${errorToMessage(error)}`);
            console.warn("[NCC AAA] Falha ao publicar Battledome State.", error);
            return null;
        }
    }

    function publishBattledomeExternalStateToGlobals(battledomeState) {
        const publicWindow = getPublicWindow();
        try { window[GLOBAL_BATTLEDOME_STATE_NAME] = battledomeState; } catch (_) {}
        try { window.NCC_AAA_LAST_BATTLEDOME_STATE_AT = battledomeState.updatedAtIso; } catch (_) {}
        if (publicWindow && publicWindow !== window) {
            try { publicWindow[GLOBAL_BATTLEDOME_STATE_NAME] = battledomeState; } catch (_) {}
            try { publicWindow.NCC_AAA_LAST_BATTLEDOME_STATE_AT = battledomeState.updatedAtIso; } catch (_) {}
        }
    }

    function dispatchBattledomeExternalState(battledomeState) {
        dispatchStateEvent(window, BATTLEDOME_EXTERNAL_STATE_EVENT, battledomeState);
        dispatchStateEvent(document, BATTLEDOME_EXTERNAL_STATE_EVENT, battledomeState);
        const publicWindow = getPublicWindow();
        if (publicWindow && publicWindow !== window) dispatchStateEvent(publicWindow, BATTLEDOME_EXTERNAL_STATE_EVENT, battledomeState);
    }


    function buildTrainingPagePanelReport(reason) {
        const school = getTrainingSchoolFromLocation();
        const panel = document.getElementById(TRAINING_PAGE_PANEL_ID);
        const payTargets = school ? getTrainingPayTargets() : [];
        const completeTargets = school ? getTrainingCompleteTargets() : [];
        const activeCourses = school ? getTrainingActiveCourseTargets() : [];
        const trainablePets = school ? getTrainingTrainablePetNames() : [];
        return clonePlain({
            schema: "ncc-aaa-training-page-panel-report-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "training-page-panel",
            reason: reason || "unknown",
            status: school ? "available" : "not-training-status-page",
            mounted: Boolean(panel),
            panelId: TRAINING_PAGE_PANEL_ID,
            currentSchoolId: school?.id || "",
            currentSchoolName: school?.name || "",
            payTargetCount: payTargets.length,
            completeTargetCount: completeTargets.length,
            activeCourseCount: activeCourses.length,
            trainablePetCount: trainablePets.length,
            neutralActionsEnabled: true,
            officialActionsEnabled: true,
            sdbWithdrawEnabled: true,
            payCourseEnabled: true,
            completeCourseEnabled: true,
            startCourseEnabled: true,
            networkAccessAdded: "sdb-and-course-actions-after-click-confirmation",
            sdbReport: lastTrainingSdbReport || readJson(TRAINING_SDB_REPORT_KEY, null),
            courseActionReport: lastTrainingCourseActionReport || readJson(TRAINING_COURSE_ACTION_REPORT_KEY, null),
            updatedAt: Date.now(),
            updatedAtIso: isoNow()
        });
    }

    function injectTrainingPagePanel(reason) {
        try {
            const school = getTrainingSchoolFromLocation();
            if (!school) return false;
            if (document.getElementById(TRAINING_PAGE_PANEL_ID)) return true;
            ensureTrainingPagePanelStyle();
            const panel = document.createElement("div");
            panel.id = TRAINING_PAGE_PANEL_ID;
            panel.className = "ncc-aaa-training-page-panel";
            panel.innerHTML = renderTrainingPagePanelHtml(school);
            panel.addEventListener("click", handleTrainingPagePanelClick);
            panel.addEventListener("change", handleTrainingPagePanelChange);
            const host = Array.from(document.querySelectorAll("b")).find(item => /Current Course Status/i.test(item.textContent || ""));
            const target = host?.parentElement || document.querySelector(".content") || document.querySelector("#content") || document.querySelector("#container__2020") || document.body;
            if (host?.parentElement) target.insertAdjacentElement("afterend", panel);
            else target.insertAdjacentElement("afterbegin", panel);
            const report = buildTrainingPagePanelReport(reason || "inject");
            lastTrainingPagePanelReport = clonePlain(report);
            writeJson(TRAINING_PAGE_PANEL_REPORT_KEY, report);
            publishTrainingExternalState("training-page-panel-injected");
            return true;
        } catch (error) {
            const message = `training-panel:${errorToMessage(error)}`;
            bootErrors.push(message);
            lastTrainingPagePanelReport = {
                schema: "ncc-aaa-training-page-panel-report-v1",
                version: VERSION,
                source: "ncc-aaa",
                sourceModule: "training-page-panel",
                status: "error",
                message,
                updatedAt: Date.now(),
                updatedAtIso: isoNow()
            };
            writeJson(TRAINING_PAGE_PANEL_REPORT_KEY, lastTrainingPagePanelReport);
            console.warn("[NCC AAA] Falha ao injetar painel neutro de treinamento.", error);
            return false;
        }
    }

    function ensureTrainingPagePanelStyle() {
        if (document.getElementById(TRAINING_PAGE_PANEL_STYLE_ID)) return;
        const style = document.createElement("style");
        style.id = TRAINING_PAGE_PANEL_STYLE_ID;
        style.textContent = `
            #${TRAINING_PAGE_PANEL_ID}, #${TRAINING_PAGE_PANEL_ID} * { box-sizing: border-box; }
            #${TRAINING_PAGE_PANEL_ID} { width: min(780px, 96%); margin: 12px auto; padding: 12px; border: 2px solid #5b45a6; border-radius: 16px; background: linear-gradient(180deg, #fffefa, #f2f7ff); color: #172033; font-family: Arial, Helvetica, sans-serif; box-shadow: 0 10px 24px rgba(31,23,61,.18); text-align: left; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-head { display: flex; gap: 10px; align-items: center; margin-bottom: 8px; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-head img { width: 42px; height: 42px; object-fit: contain; border-radius: 12px; border: 1px solid rgba(91,69,166,.24); background: #fff; padding: 4px; }
            #${TRAINING_PAGE_PANEL_ID} h2, #${TRAINING_PAGE_PANEL_ID} h3 { margin: 0; color: #2f255f; }
            #${TRAINING_PAGE_PANEL_ID} h2 { font-size: 16px; }
            #${TRAINING_PAGE_PANEL_ID} h3 { font-size: 13px; margin-bottom: 6px; }
            #${TRAINING_PAGE_PANEL_ID} p { margin: 4px 0; line-height: 1.35; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-kicker { margin: 0; font-size: 10px; text-transform: uppercase; letter-spacing: .08em; font-weight: 900; color: #5b45a6; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 10px; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-box { border: 1px solid rgba(91,69,166,.22); border-radius: 12px; background: #fff; padding: 9px; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-box.wide { grid-column: 1 / -1; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-row { border-top: 1px solid rgba(91,69,166,.12); padding: 7px 0; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-row:first-child { border-top: 0; padding-top: 0; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-tags span, #${TRAINING_PAGE_PANEL_ID} .ncc-training-pill { display: inline-flex; align-items: center; border-radius: 999px; border: 1px solid rgba(66,167,221,.26); background: #eaf8ff; color: #154870; padding: 3px 7px; font-size: 11px; font-weight: 800; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-actions { display: inline-flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
            #${TRAINING_PAGE_PANEL_ID} button, #${TRAINING_PAGE_PANEL_ID} a.ncc-training-action { border-radius: 10px; border: 1px solid #5b45a6; background: #f1ecff; color: #172033; padding: 4px 7px; cursor: pointer; text-decoration: none; font-size: 11px; font-weight: 800; line-height: 1.2; }
            #${TRAINING_PAGE_PANEL_ID} button:hover, #${TRAINING_PAGE_PANEL_ID} a.ncc-training-action:hover { filter: brightness(.96); text-decoration: none; }
            #${TRAINING_PAGE_PANEL_ID} button[disabled] { opacity: .48; cursor: not-allowed; }
            #${TRAINING_PAGE_PANEL_ID} button.ncc-training-working { background: #fff1b8; border-color: #d1a600; color: #4f3b00; }
            #${TRAINING_PAGE_PANEL_ID} button.ncc-training-success { background: #dff8df; border-color: #25a525; color: #075b07; }
            #${TRAINING_PAGE_PANEL_ID} button.ncc-training-error { background: #ffe1e1; border-color: #ce3a3a; color: #7b0000; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-official-disabled { background: #ededf3; border-color: #b7b7c8; color: #555; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-safe-note { margin-top: 9px; padding: 8px; border-radius: 10px; background: #fff7d9; border: 1px solid #e7c75e; font-size: 11px; color: #4b311a; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-course-table { width: 100%; border-collapse: collapse; margin-top: 7px; font-size: 12px; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-course-table th, #${TRAINING_PAGE_PANEL_ID} .ncc-training-course-table td { border: 1px solid rgba(91,69,166,.22); padding: 4px; text-align: center; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-course-table th { background: #ede7ff; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-progress { margin: 9px 0; padding: 8px; border-radius: 10px; border: 1px solid rgba(91,69,166,.22); background: #fff; font-size: 12px; display: none; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-progress-row { display: grid; grid-template-columns: 24px minmax(120px,1fr) 88px minmax(120px,1fr); gap: 6px; align-items: center; padding: 4px 0; border-top: 1px solid rgba(91,69,166,.10); }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-progress-row:first-child { border-top: 0; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-progress-row.success { color: #075b07; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-progress-row.error { color: #7b0000; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-progress-row.loading { color: #5b4300; }
            #${TRAINING_PAGE_PANEL_ID} .ncc-training-status { min-height: 18px; margin-top: 7px; color: #2f255f; font-weight: 800; }
            @media (max-width: 700px) { #${TRAINING_PAGE_PANEL_ID} .ncc-training-grid { grid-template-columns: 1fr; } }
        `;
        document.head.appendChild(style);
    }

    function renderTrainingPagePanelHtml(school) {
        const payTargets = getTrainingPayTargets();
        const completeTargets = getTrainingCompleteTargets();
        const activeCourses = getTrainingActiveCourseTargets();
        const stats = getTrainingPetStats();
        const trainableNames = getTrainingTrainablePetNames(stats);
        const pendingCounts = getTrainingPendingRequiredItemCounts(payTargets);
        return `
            <div class="ncc-training-head">
                <img src="${escapeAttribute(school.icon)}" alt="" loading="lazy" decoding="async">
                <div>
                    <p class="ncc-training-kicker">NCC AAA · Arena · painel operacional</p>
                    <h2>${escapeHtml(school.name)}</h2>
                    <p>${escapeHtml(school.currency)} · ${escapeHtml(Number.isFinite(school.maxLevel) ? `até nível ${school.maxLevel}` : "sem teto")}</p>
                </div>
            </div>
            <div class="ncc-training-tags">
                <span>${escapeHtml(payTargets.length)} aguardando pagamento</span>
                <span>${escapeHtml(completeTargets.length)} completo(s)</span>
                <span>${escapeHtml(activeCourses.length)} em andamento</span>
                <span>${escapeHtml(trainableNames.length)} treinável(is)</span>
                <span>SDB ativo com confirmação</span><span>ações oficiais ativas</span>
            </div>
            <div class="ncc-training-grid">
                <section class="ncc-training-box">
                    <h3>Cursos em andamento</h3>
                    ${activeCourses.length ? activeCourses.map(renderTrainingActiveCourseRow).join("") : `<p>Nenhum curso em andamento detectado neste momento.</p>`}
                </section>
                <section class="ncc-training-box">
                    <h3>Cursos completos</h3>
                    ${completeTargets.length ? completeTargets.map(target => `<div class="ncc-training-row"><b>${escapeHtml(target.petName)}</b><br><span class="ncc-training-pill">pronto para completar</span><div class="ncc-training-actions"><button type="button" data-training-panel-action="complete-course" data-pet-name="${escapeAttribute(target.petName)}">Completar curso</button></div></div>`).join("") + `<div class="ncc-training-actions"><button type="button" data-training-panel-action="complete-all-courses">Completar todos</button></div>` : `<p>Nenhum curso completo detectado.</p>`}
                </section>
                <section class="ncc-training-box wide">
                    <h3>Aguardando pagamento / itens exigidos</h3>
                    ${payTargets.length ? payTargets.map(target => renderTrainingPayTargetRow(target, school)).join("") : `<p>Nenhum curso aguardando pagamento detectado.</p>`}
                    ${Object.keys(pendingCounts).length ? renderTrainingPendingItemsActions(school) : ``}
                </section>
                <section class="ncc-training-box wide">
                    <h3>Iniciar cursos selecionados</h3>
                    ${trainableNames.length ? renderTrainingCourseSelectionTable(trainableNames, stats) : `<p>Nenhum pet disponível para iniciar curso nesta academia ou todos já estão treinando.</p>`}
                    <div class="ncc-training-actions">
                        <button type="button" data-training-panel-action="clear-selected-courses">Limpar seleção</button>
                        <button type="button" data-training-panel-action="start-selected-courses">Iniciar selecionados</button>
                    </div>
                </section>
            </div>
            <div class="ncc-training-safe-note">Etapa 5 ativa ações oficiais apenas por clique real e confirmação: SDB, pagamento, conclusão e início de cursos selecionados. Nada roda no boot e o PIN do SDB continua apenas em sessão.</div>
            <div class="ncc-training-progress" aria-live="polite"></div>
            <div class="ncc-training-status" aria-live="polite"></div>
        `;
    }

    function renderTrainingActiveCourseRow(item) {
        const label = item.complete ? "Course Finished!" : `Termina em ${formatTrainingDuration(item.remainingMs)}`;
        return `<div class="ncc-training-row"><b>${escapeHtml(item.petName)}</b><br>${escapeHtml(item.course)}<br><span class="ncc-training-pill">${escapeHtml(label)}</span></div>`;
    }

    function renderTrainingPayTargetRow(target, school) {
        const items = getTrainingRequiredItemsForPet(target.petName);
        const itemHtml = renderTrainingRequiredItemsHtml(items, school.id === "pirate");
        const sdbAction = school.id === "pirate"
            ? `<button type="button" data-training-panel-action="open-pet-sdb-search" data-pet-name="${escapeAttribute(target.petName)}">Buscar Dubloon no SDB</button>`
            : `<button type="button" data-training-panel-action="get-pet-sdb-items" data-pet-name="${escapeAttribute(target.petName)}">Pegar itens do SDB</button>`;
        return `<div class="ncc-training-row"><b>${escapeHtml(target.petName)}</b><br>${itemHtml}<div class="ncc-training-actions">${sdbAction}<button type="button" data-training-panel-action="pay-course" data-pet-name="${escapeAttribute(target.petName)}">Pagar curso</button></div></div>`;
    }

    function renderTrainingPendingItemsActions(school) {
        const isPirate = school?.id === "pirate";
        return `<div class="ncc-training-actions"><button type="button" data-training-panel-action="copy-pending-items">Copiar lista de itens pendentes</button><button type="button" data-training-panel-action="open-first-pending-sdb">Abrir primeiro item no SDB</button>${isPirate ? `` : `<button type="button" data-training-panel-action="get-all-sdb-items">Pegar todos os itens do SDB</button>`}<button type="button" data-training-panel-action="pay-all-courses">Pagar todos</button></div>`;
    }

    function renderTrainingRequiredItemsHtml(items, pirateManual) {
        const counts = {};
        (items || []).map(normalizeTrainingText).filter(Boolean).forEach(name => { counts[name] = (counts[name] || 0) + 1; });
        const entries = Object.entries(counts);
        if (!entries.length) return `<span class="ncc-training-pill">Itens não detectados</span>`;
        return entries.map(([name, count]) => `<div class="ncc-training-row"><span><b>${escapeHtml(name)}${count > 1 ? ` x${escapeHtml(String(count))}` : ""}</b></span>${renderTrainingItemActionSet(name, pirateManual)}</div>`).join("");
    }

    function renderTrainingItemActionSet(itemName, pirateManual) {
        const name = normalizeTrainingText(itemName);
        const q = encodeURIComponent(name);
        return `<div class="ncc-training-actions" data-training-item-actions="${escapeAttribute(name)}">
            <button type="button" data-training-panel-action="open-sdb-item" data-item-name="${escapeAttribute(name)}">${pirateManual ? "Buscar Dubloon no SDB" : "Buscar no SDB"}</button>
            <button type="button" data-training-panel-action="open-ssw-item" data-item-name="${escapeAttribute(name)}">SSW</button>
            <a class="ncc-training-action" href="/shops/wizard.phtml?string=${escapeAttribute(q)}" target="_blank" rel="noopener noreferrer">SW</a>
            <a class="ncc-training-action" href="/island/tradingpost.phtml?type=browse&criteria=item_exact&search_string=${escapeAttribute(q)}" target="_blank" rel="noopener noreferrer">TP</a>
            <button type="button" data-training-panel-action="copy-item-name" data-item-name="${escapeAttribute(name)}">Copiar</button>
        </div>`;
    }

    function renderTrainingCourseSelectionTable(names, stats) {
        const statLabels = [["Level", "Lvl"], ["Strength", "Str"], ["Defence", "Def"], ["Agility", "Mov"], ["Endurance", "Hp"]];
        return `<table class="ncc-training-course-table"><thead><tr><th>Pet</th>${statLabels.map(([label]) => `<th>${escapeHtml(label)}</th>`).join("")}</tr></thead><tbody>${names.map(name => `<tr><td><b>${escapeHtml(name)}</b></td>${statLabels.map(([label, key]) => `<td><input type="radio" name="ncc-aaa-training-${escapeAttribute(name)}" data-training-course="1" data-pet-name="${escapeAttribute(name)}" value="${escapeAttribute(label)}"><br>${escapeHtml(stats[name]?.[key] || 0)}</td>`).join("")}</tr>`).join("")}</tbody></table>`;
    }

    function getTrainingPetStats() {
        const stats = {};
        Array.from(document.querySelectorAll("b")).filter(el => /\s\(Level\s+\d+/i.test(el.textContent || "")).forEach(el => {
            const petName = normalizeTrainingText((el.textContent || "").split(" (Level")[0]);
            if (!petName || stats[petName]) return;
            const values = [];
            let statRow = el.closest("tr")?.nextElementSibling || null;
            for (let i = 0; i < 3 && statRow && values.length < 5; i += 1) {
                Array.from(statRow.querySelectorAll("b")).forEach(b => {
                    const numberText = normalizeTrainingText(b.textContent || "");
                    if (/^\d+(?:\s*\/\s*\d+)?$/.test(numberText)) values.push(numberText);
                });
                statRow = statRow.nextElementSibling;
            }
            if (values.length >= 5) {
                stats[petName] = {
                    Lvl: parseInt(values[0], 10) || 0,
                    Str: parseInt(values[1], 10) || 0,
                    Def: parseInt(values[2], 10) || 0,
                    Mov: parseInt(values[3], 10) || 0,
                    Hp: parseInt(String(values[4]).split("/").pop(), 10) || 0
                };
            }
        });
        return stats;
    }

    function getTrainingTrainablePetNames(statsInput) {
        const school = getTrainingSchoolFromLocation();
        const stats = statsInput || getTrainingPetStats();
        if (!school) return [];
        return Object.keys(stats).filter(name => {
            if (Number.isFinite(school.maxLevel) && Number(stats[name]?.Lvl || 0) > school.maxLevel) return false;
            const block = getTrainingBlocks(document).find(item => item.name === name);
            if (block && !/is not on a course/i.test(block.text)) return false;
            return true;
        });
    }

    function getTrainingRequiredItemsForPet(petName) {
        const names = [];
        const wanted = normalizeTrainingText(petName).toLowerCase();
        getTrainingBlocks(document)
            .filter(block => normalizeTrainingText(block.name).toLowerCase() === wanted)
            .forEach(block => {
                Array.from(new Set(block.rows || [])).filter(Boolean).forEach(row => {
                    Array.from(row.querySelectorAll("b")).forEach(b => {
                        if (b.closest?.(`#${TRAINING_PAGE_PANEL_ID}`)) return;
                        const name = normalizeTrainingText(b.textContent || "");
                        if (/\b(Codestone|Dubloon Coin)\b/i.test(name) && !/^Status$/i.test(name)) names.push(name);
                    });
                });
            });
        return names;
    }

    function getTrainingPayTargets() {
        const targets = [];
        Array.from(document.querySelectorAll('form[action*="process_training"], form[action*="process_fight_training"], form[action*="process_academy"]')).forEach(form => {
            if (form.closest?.(`#${TRAINING_PAGE_PANEL_ID}`)) return;
            const hasPay = Boolean(form.querySelector('input[name="type"][value="pay"]')) || /type=pay/i.test(form.action || "");
            if (!hasPay) return;
            const petName = form.querySelector('input[name="pet_name"]')?.value || "";
            if (petName && !targets.some(item => item.petName === petName)) targets.push({ petName, el: form, isLink: false });
        });
        Array.from(document.querySelectorAll('a[href*="type=pay"]')).forEach(a => {
            if (a.closest?.(`#${TRAINING_PAGE_PANEL_ID}`)) return;
            const href = a.getAttribute("href") || "";
            if (!/process_(training|fight_training|academy)\.phtml/i.test(href)) return;
            const match = href.match(/pet_name=([^&]+)/i);
            const petName = match ? decodeURIComponent(match[1].replace(/\+/g, " ")) : "";
            if (petName && !targets.some(item => item.petName === petName)) targets.push({ petName, el: a, isLink: true });
        });
        return targets;
    }

    function getTrainingCompleteTargets() {
        const targets = [];
        Array.from(document.querySelectorAll('form[action*="process_training"], form[action*="process_fight_training"], form[action*="process_academy"]')).forEach(form => {
            if (form.closest?.(`#${TRAINING_PAGE_PANEL_ID}`)) return;
            const submit = form.querySelector('input[type="submit"][value*="Complete Course"], button[type="submit"]');
            const petName = form.querySelector('input[name="pet_name"]')?.value || "";
            if (submit && petName && !targets.some(item => item.petName === petName)) targets.push({ petName, el: form });
        });
        return targets;
    }

    function getTrainingActiveCourseTargets() {
        const school = getTrainingSchoolFromLocation();
        if (!school) return [];
        const completeNames = new Set(getTrainingCompleteTargets().map(item => normalizeTrainingText(item.petName).toLowerCase()));
        const payNames = new Set(getTrainingPayTargets().map(item => normalizeTrainingText(item.petName).toLowerCase()));
        return getTrainingBlocks(document).map(block => {
            const text = normalizeTrainingText(block?.text || "");
            const petName = normalizeTrainingText(block?.name || "");
            if (!petName || !text) return null;
            const key = petName.toLowerCase();
            if (/is not on a course/i.test(text)) return null;
            if (completeNames.has(key) || payNames.has(key)) return null;
            const remainingMsRaw = parseTrainingRemainingMs(text);
            const complete = remainingMsRaw < 0 || /Course Finished!/i.test(text);
            if (!complete && remainingMsRaw === 0 && !/currently studying/i.test(text)) return null;
            const courseMatch = text.match(/currently studying\s+([A-Za-z ]+?)(?:\s+Time till course finishes|\s+Lvl\s*:|\s+Str\s*:|\s+Def\s*:|\s+Mov\s*:|\s+Hp\s*:|$)/i);
            return { petName, school: school.id, course: normalizeTrainingText(courseMatch?.[1] || "Curso em andamento"), remainingMs: remainingMsRaw < 0 ? 0 : remainingMsRaw, complete, text };
        }).filter(Boolean);
    }

    function getTrainingPendingRequiredItemCounts(payTargetsInput) {
        const counts = {};
        const targets = payTargetsInput || getTrainingPayTargets();
        targets.forEach(target => {
            getTrainingRequiredItemsForPet(target.petName).forEach(item => {
                const name = normalizeTrainingText(item);
                if (name) counts[name] = (counts[name] || 0) + 1;
            });
        });
        return counts;
    }

    function formatTrainingItemCounts(counts) {
        return Object.entries(counts || {}).map(([name, count]) => `${name} x${count}`).join("\n");
    }

    function isTrainingPirateSchool() {
        return getTrainingSchoolFromLocation()?.id === "pirate";
    }

    function trainingConfirmAction(message) {
        return window.confirm(String(message || "Confirmar ação do NCC AAA?"));
    }

    function getTrainingPinForRequest() {
        if (trainingSessionPin) return trainingSessionPin;
        const value = window.prompt("Se o SDB pedir PIN, digite agora. Deixe em branco para tentar sem PIN. O PIN não será salvo permanentemente.");
        if (value === null) return "";
        trainingSessionPin = String(value || "").trim();
        return trainingSessionPin;
    }

    function getTrainingSdbReportFallback() {
        return clonePlain(lastTrainingSdbReport || readJson(TRAINING_SDB_REPORT_KEY, null) || {
            schema: "ncc-aaa-training-sdb-report-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "training-sdb-operations",
            status: "idle",
            message: "Nenhuma operação de SDB executada nesta sessão.",
            updatedAt: 0,
            updatedAtIso: ""
        });
    }

    function saveTrainingSdbReport(status, message, extra) {
        const report = clonePlain(Object.assign({
            schema: "ncc-aaa-training-sdb-report-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "training-sdb-operations",
            status: status || "unknown",
            message: String(message || ""),
            safetyClass: "confirmed-write",
            userClickRequired: true,
            confirmationRequired: true,
            pinPersistence: "session-only",
            courseActionsEnabled: true,
            updatedAt: Date.now(),
            updatedAtIso: isoNow()
        }, extra || {}));
        lastTrainingSdbReport = report;
        writeJson(TRAINING_SDB_REPORT_KEY, report);
        publishTrainingExternalState("training-sdb-report");
        return report;
    }

    function setTrainingButtonState(button, mode, label, message) {
        if (!button) return;
        if (!button.dataset.originalText) button.dataset.originalText = button.textContent || "Ação";
        button.classList.remove("ncc-training-working", "ncc-training-success", "ncc-training-error");
        if (mode === "loading") {
            button.disabled = true;
            button.textContent = label || "Retirando...";
            button.classList.add("ncc-training-working");
        } else if (mode === "success") {
            button.disabled = false;
            button.textContent = label || "Itens retirados";
            button.classList.add("ncc-training-success");
        } else if (mode === "error") {
            button.disabled = false;
            button.textContent = label || "Erro no SDB";
            button.classList.add("ncc-training-error");
        } else {
            button.disabled = false;
            button.textContent = button.dataset.originalText || label || "Ação";
        }
        if (message) button.title = String(message);
    }

    function getTrainingItemsForPet(petName) {
        return getTrainingRequiredItemsForPet(petName).map(normalizeTrainingText).filter(Boolean);
    }

    function normalizeTrainingSdbResultMessage(message, fallback) {
        const text = normalizeTrainingText(message || "");
        if (!text) return fallback || "Resposta do SDB não reconhecida.";
        if (/instrumentationKey|appInsights|trackException|function\s*\(|window\.|document\.|disableAjaxTracking/i.test(text) || text.length > 240) {
            return fallback || "Resposta do SDB não reconhecida.";
        }
        return text;
    }

    async function trainingFetchText(url, options) {
        const target = new URL(String(url || ""), location.href);
        if (!/\.neopets\.com$/i.test(target.hostname) && !/^neopets\.com$/i.test(target.hostname)) throw new Error("Destino fora do Neopets bloqueado.");
        const response = await fetch(target.href, Object.assign({ credentials: "same-origin", cache: "no-store" }, options || {}));
        const text = await response.text();
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        return text;
    }

    async function trainingFetchJson(url, options) {
        const target = new URL(String(url || ""), location.href);
        if (!/\.neopets\.com$/i.test(target.hostname) && !/^neopets\.com$/i.test(target.hostname)) throw new Error("Destino fora do Neopets bloqueado.");
        const response = await fetch(target.href, Object.assign({ credentials: "same-origin", cache: "no-store" }, options || {}));
        let data = null;
        try { data = await response.json(); }
        catch (error) { throw new Error(`Resposta JSON inválida do SDB (${errorToMessage(error)})`); }
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        return data;
    }

    function extractTrainingSdbDataFromHtml(html) {
        const source = String(html || "");
        const match = source.match(/window\.__sdbData\s*=\s*(\{[\s\S]*?\});/);
        if (!match) return {};
        try { return JSON.parse(match[1]); }
        catch (_) { return {}; }
    }

    async function getTrainingSdbContext(force) {
        if (!force && trainingSessionSdbContext?.refCk) return trainingSessionSdbContext;
        const html = await trainingFetchText("/safetydeposit.phtml");
        const data = extractTrainingSdbDataFromHtml(html);
        const context = {
            refCk: String(data.refCk || ""),
            pinRequired: data.pinRequired === true,
            capturedAt: Date.now()
        };
        trainingSessionSdbContext = context;
        return context;
    }

    async function searchTrainingModernSdbItem(itemName, context) {
        const cleanName = normalizeTrainingText(itemName);
        if (!cleanName || !context?.refCk) return null;
        const payload = {
            page: 1,
            per_page: 30,
            search: cleanName,
            category: "",
            sort: "",
            _ref_ck: context.refCk
        };
        const data = await trainingFetchJson(TRAINING_MODERN_SDB_ITEMS_ENDPOINT, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "x-requested-with": "XMLHttpRequest"
            },
            body: JSON.stringify(payload)
        });
        const items = Array.isArray(data?.data?.items) ? data.data.items : [];
        const wanted = cleanName.toLowerCase();
        const exact = items.find(item => normalizeTrainingText(item?.obj_name || "").toLowerCase() === wanted);
        if (exact) return exact;
        const close = items.find(item => {
            const name = normalizeTrainingText(item?.obj_name || "").toLowerCase();
            return name && (name.includes(wanted) || wanted.includes(name));
        });
        return items.length === 1 ? (close || items[0]) : (close || null);
    }

    async function retrieveTrainingItemsFromModernSdb(items, options) {
        const list = (items || []).map(normalizeTrainingText).filter(Boolean);
        if (!list.length) return { ok: false, handled: true, message: "Nenhum item de treino foi detectado para retirar do SDB." };
        const context = await getTrainingSdbContext(false);
        if (!context?.refCk) return { ok: false, handled: false, message: "SDB moderno não identificado." };
        const countsByName = {};
        list.forEach(name => { countsByName[name] = (countsByName[name] || 0) + 1; });
        const moves = [];
        const missing = [];
        const insufficient = [];
        for (const [name, count] of Object.entries(countsByName)) {
            const item = await searchTrainingModernSdbItem(name, context);
            if (!item?.obj_info_id) {
                missing.push(name);
                continue;
            }
            const available = parseInt(String(item.amount || "0").replace(/[^0-9]/g, ""), 10) || 0;
            if (available < count) {
                insufficient.push(`${name} (${available}/${count})`);
                continue;
            }
            moves.push({ obj_info_id: item.obj_info_id, quantity: count, action: "inventory", obj_name: item.obj_name || name });
        }
        if (!moves.length) {
            const parts = [];
            if (missing.length) parts.push(`não encontrados: ${missing.join(", ")}`);
            if (insufficient.length) parts.push(`quantidade insuficiente: ${insufficient.join(", ")}`);
            return { ok: false, handled: true, message: `Não encontrei item suficiente no SDB (${parts.join("; ") || "sem itens movíveis"}).` };
        }
        const payload = {
            moves: moves.map(item => ({ obj_info_id: item.obj_info_id, quantity: item.quantity, action: "inventory" })),
            pin: String(options?.pin || ""),
            _ref_ck: context.refCk
        };
        const data = await trainingFetchJson(TRAINING_MODERN_SDB_MOVE_ENDPOINT, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "x-requested-with": "XMLHttpRequest"
            },
            body: JSON.stringify(payload)
        });
        if (data?.success) {
            const summary = Object.entries(countsByName).map(([name, count]) => `${name} x${count}`).join(", ");
            return { ok: true, handled: true, mode: "modern", message: `Itens enviados do SDB para o inventário: ${summary}${missing.length ? `. Não encontrados: ${missing.join(", ")}` : ""}${insufficient.length ? `. Quantidade insuficiente: ${insufficient.join(", ")}` : ""}` };
        }
        if (data?.error === "pin_required" && !options?.pin) {
            const pin = getTrainingPinForRequest();
            if (pin) return retrieveTrainingItemsFromModernSdb(list, { pin });
            return { ok: false, handled: true, mode: "modern", message: "PIN exigido pelo SDB. Operação cancelada sem PIN." };
        }
        if (data?.error === "pin_wrong") {
            trainingSessionPin = "";
            return { ok: false, handled: true, mode: "modern", message: data.message || "PIN incorreto para o SDB." };
        }
        if (data?.error === "partial_error") {
            const moved = Number(data.success_count || 0) || 0;
            return { ok: moved > 0, handled: true, mode: "modern", message: data.message || `Movimento parcial no SDB: ${moved} item(ns) processado(s).` };
        }
        return { ok: false, handled: true, mode: "modern", message: data?.message || data?.error || "O SDB não confirmou a retirada dos itens." };
    }

    async function resolveTrainingItemIdFromLegacySdb(itemName) {
        const cleanName = normalizeTrainingText(itemName);
        if (!cleanName) return "";
        if (TRAINING_PAYMENT_ITEM_IDS[cleanName]) return String(TRAINING_PAYMENT_ITEM_IDS[cleanName]);
        const params = new URLSearchParams({ obj_name: cleanName, category: "0" });
        const html = await trainingFetchText(`/safetydeposit.phtml?${params.toString()}`);
        const doc = new DOMParser().parseFromString(html, "text/html");
        const inputs = Array.from(doc.querySelectorAll('input[name^="back_to_inv["]'));
        const wanted = cleanName.toLowerCase();
        const exact = inputs.find(input => {
            const host = input.closest("tr") || input.closest("td") || input.parentElement;
            return normalizeTrainingText(host?.textContent || "").toLowerCase().includes(wanted);
        });
        const selected = exact || (inputs.length === 1 ? inputs[0] : null);
        const match = selected?.getAttribute("name")?.match(/back_to_inv\[(\d+)\]/);
        return match ? match[1] : "";
    }

    async function retrieveTrainingItemsFromLegacySdb(items, options) {
        const list = (items || []).map(normalizeTrainingText).filter(Boolean);
        if (!list.length) return { ok: false, mode: "legacy", message: "Nenhum item de treino foi detectado para retirar do SDB." };
        const countsByName = {};
        list.forEach(name => { countsByName[name] = (countsByName[name] || 0) + 1; });
        const postData = new URLSearchParams();
        const missing = [];
        for (const [name, count] of Object.entries(countsByName)) {
            const id = await resolveTrainingItemIdFromLegacySdb(name);
            if (id) postData.set(`back_to_inv[${id}]`, String(count));
            else missing.push(name);
        }
        postData.set("category", "0");
        postData.set("offset", "0");
        if (options?.pin) postData.set("pin", String(options.pin));
        if (![...postData.keys()].some(key => key.startsWith("back_to_inv["))) {
            return { ok: false, mode: "legacy", message: `Não encontrei estes itens no SDB: ${missing.join(", ")}` };
        }
        const html = await trainingFetchText(TRAINING_LEGACY_SDB_MOVE_ENDPOINT, {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
            body: postData.toString()
        });
        if (/\bPIN\b|password|passcode/i.test(html) && !options?.pin) {
            const pin = getTrainingPinForRequest();
            if (pin) return retrieveTrainingItemsFromLegacySdb(list, { pin });
        }
        const error = html.match(/<b>Error:\s*<\/b>\s*([\s\S]*?)<\/div>/i) || html.match(/Error:\s*([^<]+)/i);
        if (error) return { ok: false, mode: "legacy", message: `Erro do SDB: ${normalizeTrainingSdbResultMessage(error[1], "Erro do SDB.")}` };
        const text = normalizeTrainingText(new DOMParser().parseFromString(html, "text/html").body?.textContent || html);
        if (/you do not have|you don't have|not have enough|not in your safety deposit|could not find|no items? matching|there are no/i.test(text)) {
            return { ok: false, mode: "legacy", message: `Não encontrei item suficiente no SDB. ${text.slice(0, 180)}` };
        }
        const summary = Object.entries(countsByName).map(([name, count]) => `${name} x${count}`).join(", ");
        return { ok: true, mode: "legacy", message: `Itens enviados do SDB para o inventário: ${summary}${missing.length ? `. Não encontrados: ${missing.join(", ")}` : ""}` };
    }

    async function retrieveTrainingItemsFromSdb(items, options) {
        try {
            const modern = await retrieveTrainingItemsFromModernSdb(items, options || {});
            if (modern?.handled !== false) return modern;
        } catch (error) {
            saveTrainingSdbReport("modern-fallback", `SDB moderno falhou; tentando legado (${errorToMessage(error)}).`, { error: errorToMessage(error) });
        }
        return retrieveTrainingItemsFromLegacySdb(items, options || {});
    }

    function classifyTrainingSdbResult(result) {
        if (result?.ok) return "success";
        const text = normalizeTrainingText(result?.message || "");
        if (/não encontrei|não encontrado|not found|not have|do not have|don't have|sem item|quantidade insuficiente/i.test(text)) return "missing";
        return "error";
    }

    async function getTrainingItemsForPetAction(petName, button) {
        const school = getTrainingSchoolFromLocation();
        const cleanPetName = normalizeTrainingText(petName);
        if (!school || !cleanPetName) return;
        if (school.id === "pirate") {
            openTrainingSdbForPet(cleanPetName, button);
            return;
        }
        const items = getTrainingItemsForPet(cleanPetName);
        if (!items.length) {
            const message = `${cleanPetName}: nenhum Codestone/Red Codestone detectado na página.`;
            setTrainingButtonState(button, "error", "Itens não detectados", message);
            setTrainingPanelStatus(message);
            saveTrainingSdbReport("no-items", message, { petName: cleanPetName, schoolId: school.id });
            return;
        }
        if (!trainingConfirmAction(`Retirar do SDB para o inventário os itens de ${cleanPetName}?\n\n${items.join("\n")}`)) return;
        setTrainingButtonState(button, "loading", "Retirando...");
        setTrainingPanelStatus(`${cleanPetName}: retirando itens do SDB...`);
        saveTrainingSdbReport("running", `${cleanPetName}: retirando itens do SDB...`, { petName: cleanPetName, schoolId: school.id, items });
        try {
            const result = await retrieveTrainingItemsFromSdb(items);
            const mode = classifyTrainingSdbResult(result);
            setTrainingButtonState(button, mode === "success" ? "success" : "error", mode === "success" ? "Itens retirados" : "Erro no SDB", result.message);
            const message = `${cleanPetName}: ${normalizeTrainingSdbResultMessage(result.message, "Operação do SDB concluída, confira o inventário.")}`;
            setTrainingPanelStatus(message);
            saveTrainingSdbReport(result.ok ? "success" : "error", message, { petName: cleanPetName, schoolId: school.id, items, result });
        } catch (error) {
            const message = `${cleanPetName}: falha ao retirar itens do SDB (${errorToMessage(error)}).`;
            setTrainingButtonState(button, "error", "Erro no SDB", message);
            setTrainingPanelStatus(message);
            saveTrainingSdbReport("error", message, { petName: cleanPetName, schoolId: school.id, items, error: errorToMessage(error) });
        }
    }

    async function getAllTrainingSdbItemsAction(button) {
        const school = getTrainingSchoolFromLocation();
        if (!school) return;
        if (school.id === "pirate") {
            copyTrainingPanelText(formatTrainingItemCounts(getTrainingPendingRequiredItemCounts()), "Lista de Dubloons pendentes copiada.");
            return;
        }
        const targets = getTrainingPayTargets();
        let items = [];
        targets.forEach(target => { items = items.concat(getTrainingItemsForPet(target.petName)); });
        if (!items.length) {
            const message = "Nenhum item pendente de pagamento detectado na página.";
            setTrainingButtonState(button, "error", "Itens não detectados", message);
            setTrainingPanelStatus(message);
            saveTrainingSdbReport("no-items", message, { schoolId: school.id });
            return;
        }
        const counts = {};
        items.forEach(name => { counts[name] = (counts[name] || 0) + 1; });
        const list = Object.entries(counts).map(([name, count]) => `${name} x${count}`).join("\n");
        if (!trainingConfirmAction(`Retirar TODOS estes itens do SDB para o inventário?\n\n${list}`)) return;
        setTrainingButtonState(button, "loading", "Retirando todos...");
        setTrainingPanelStatus("Retirando todos os itens de treino do SDB...");
        saveTrainingSdbReport("running", "Retirando todos os itens de treino do SDB...", { schoolId: school.id, items });
        try {
            const result = await retrieveTrainingItemsFromSdb(items);
            const mode = classifyTrainingSdbResult(result);
            setTrainingButtonState(button, mode === "success" ? "success" : "error", mode === "success" ? "Itens retirados" : "Erro no SDB", result.message);
            const message = normalizeTrainingSdbResultMessage(result.message, "Operação do SDB concluída, confira o inventário.");
            setTrainingPanelStatus(message);
            saveTrainingSdbReport(result.ok ? "success" : "error", message, { schoolId: school.id, items, result });
        } catch (error) {
            const message = `Falha ao retirar itens do SDB (${errorToMessage(error)}).`;
            setTrainingButtonState(button, "error", "Erro no SDB", message);
            setTrainingPanelStatus(message);
            saveTrainingSdbReport("error", message, { schoolId: school.id, items, error: errorToMessage(error) });
        }
    }

    function openTrainingSdbForPet(petName, button) {
        const items = getTrainingItemsForPet(petName);
        const first = items[0] || "";
        if (!first) {
            setTrainingPanelStatus(`${petName}: nenhum item detectado para buscar no SDB.`);
            return;
        }
        if (items.length > 1) copyTrainingPanelText(formatTrainingItemCounts(getTrainingItemListToCounts(items)), `${petName}: lista de itens copiada.`);
        openTrainingNeutralUrl(`/safetydeposit.phtml?obj_name=${encodeURIComponent(first)}`, `SDB aberto para: ${first}`);
        setTrainingButtonState(button, "success", "SDB aberto");
    }

    function getTrainingItemListToCounts(items) {
        const counts = {};
        (items || []).map(normalizeTrainingText).filter(Boolean).forEach(name => { counts[name] = (counts[name] || 0) + 1; });
        return counts;
    }


    function getTrainingCourseActionReportFallback() {
        return clonePlain(lastTrainingCourseActionReport || readJson(TRAINING_COURSE_ACTION_REPORT_KEY, null) || {
            schema: "ncc-aaa-training-course-action-report-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "training-course-actions",
            status: "idle",
            message: "Nenhuma ação oficial de curso executada nesta sessão.",
            updatedAt: 0,
            updatedAtIso: ""
        });
    }

    function saveTrainingCourseActionReport(status, message, extra) {
        const report = clonePlain(Object.assign({
            schema: "ncc-aaa-training-course-action-report-v1",
            version: VERSION,
            source: "ncc-aaa",
            sourceModule: "training-course-actions",
            status: status || "unknown",
            message: String(message || ""),
            safetyClass: "confirmed-write",
            userClickRequired: true,
            confirmationRequired: true,
            automaticOnBoot: false,
            networkAccess: "same-origin official course endpoint only after click and confirmation",
            updatedAt: Date.now(),
            updatedAtIso: isoNow()
        }, extra || {}));
        lastTrainingCourseActionReport = report;
        writeJson(TRAINING_COURSE_ACTION_REPORT_KEY, report);
        publishTrainingExternalState("training-course-action-report");
        return report;
    }

    function trainingSleep(ms) {
        return new Promise(resolve => window.setTimeout(resolve, Math.max(0, Number(ms) || 0)));
    }

    function getTrainingSafeBatchDelayMs() {
        return 2000 + Math.floor(Math.random() * 2001);
    }

    async function trainingCountdownDelay(ms, onTick) {
        const started = Date.now();
        let left = Math.max(0, Number(ms) || 0);
        while (left > 0) {
            if (typeof onTick === "function") onTick(left);
            await trainingSleep(Math.min(250, left));
            left = ms - (Date.now() - started);
        }
        if (typeof onTick === "function") onTick(0);
    }

    function getTrainingVisibleText(html) {
        try {
            const doc = new DOMParser().parseFromString(String(html || ""), "text/html");
            doc.querySelectorAll("script, style, noscript").forEach(el => el.remove());
            return normalizeTrainingText(doc.body?.textContent || doc.documentElement?.textContent || "");
        } catch (_) {
            return normalizeTrainingText(String(html || "").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " "));
        }
    }

    function sanitizeTrainingCourseMessage(message, fallback) {
        const text = normalizeTrainingText(message || "");
        if (!text) return fallback || "Resposta do Neopets não reconhecida. Confira a página.";
        if (/instrumentationKey|appInsights|trackException|autoException|function\s*\(|window\.|document\.|disableAjaxTracking/i.test(text) || text.length > 240) {
            return fallback || "Resposta do Neopets não reconhecida. Confira a página.";
        }
        return text;
    }

    function extractTrainingVisibleError(html) {
        let doc = null;
        try {
            doc = new DOMParser().parseFromString(String(html || ""), "text/html");
            doc.querySelectorAll("script, style, noscript").forEach(el => el.remove());
        } catch (_) {
            doc = null;
        }
        if (doc) {
            const boldErrors = Array.from(doc.querySelectorAll("b, strong")).filter(el => /^\s*Error\s*:?\s*$/i.test(el.textContent || ""));
            for (const marker of boldErrors) {
                const box = marker.closest("div, td, p, center, body") || marker.parentElement;
                const text = normalizeTrainingText(box?.textContent || marker.parentElement?.textContent || "");
                if (text) return sanitizeTrainingCourseMessage(text.replace(/^Error\s*:?\s*/i, ""), "Erro detectado pelo Neopets.");
            }
        }
        const visibleText = getTrainingVisibleText(html);
        const visibleError = visibleText.match(/\bError\s*:\s*([^.!?]{1,180}(?:[.!?]|$))/i);
        if (visibleError) return sanitizeTrainingCourseMessage(visibleError[1], "Erro detectado pelo Neopets.");
        return "";
    }

    function isTrainingCourseStartSuccess(html, petName, courseType) {
        const text = getTrainingVisibleText(html).toLowerCase();
        const pet = normalizeTrainingText(petName).toLowerCase();
        const course = normalizeTrainingText(courseType).toLowerCase();
        if (!text) return false;
        if (pet && course && text.includes(pet) && text.includes(`currently studying ${course}`)) return true;
        if (pet && text.includes(pet) && text.includes("this course has not been paid for yet")) return true;
        if (text.includes("this course has not been paid for yet")) return true;
        if (text.includes("course has started") || text.includes("started the course")) return true;
        return false;
    }

    function setTrainingCourseButtonState(button, mode, label, message) {
        if (!button) return;
        if (!button.dataset.originalText) button.dataset.originalText = button.textContent || "Ação";
        button.classList.remove("ncc-training-working", "ncc-training-success", "ncc-training-error");
        if (mode === "loading") {
            button.disabled = true;
            button.textContent = label || "Processando...";
            button.classList.add("ncc-training-working");
        } else if (mode === "success") {
            button.disabled = false;
            button.textContent = label || "Concluído";
            button.classList.add("ncc-training-success");
        } else if (mode === "error") {
            button.disabled = false;
            button.textContent = label || "Erro";
            button.classList.add("ncc-training-error");
        } else {
            button.disabled = false;
            button.textContent = button.dataset.originalText || label || "Ação";
        }
        if (message) button.title = String(message);
    }

    function setTrainingProgressHtml(html) {
        const box = document.querySelector(`#${TRAINING_PAGE_PANEL_ID} .ncc-training-progress`);
        if (!box) return;
        box.innerHTML = html || "";
        box.style.display = html ? "block" : "none";
    }

    function renderTrainingBatchProgressRows(items) {
        return `<div>${(items || []).map((item, index) => `<div class="ncc-training-progress-row ${escapeAttribute(item.tone || "waiting")}"><span>${index + 1}</span><span><b>${escapeHtml(item.petName || "")}</b> — ${escapeHtml(item.course || item.action || "")}</span><span>${escapeHtml(item.status || "Aguardando")}</span><span>${escapeHtml(item.message || "")}</span></div>`).join("")}</div>`;
    }

    function updateTrainingBatchProgress(items, headline, note) {
        setTrainingProgressHtml(`<b>${escapeHtml(headline || "Processando...")}</b>${note ? `<p>${escapeHtml(note)}</p>` : ""}${renderTrainingBatchProgressRows(items)}`);
    }

    function saveTrainingBatchSummary(summary) {
        try { sessionStorage.setItem(TRAINING_BATCH_SUMMARY_KEY, JSON.stringify(Object.assign({ time: Date.now() }, summary || {}))); }
        catch (_) {}
    }

    async function payTrainingCourse(petName) {
        const school = getTrainingSchoolFromLocation();
        const cleanPetName = normalizeTrainingText(petName);
        if (!school || !cleanPetName) return { ok: false, message: "Abra a página de status da academia para pagar." };
        const url = `${school.processPath}?type=pay&pet_name=${encodeURIComponent(cleanPetName)}`;
        const html = await trainingFetchText(url, { method: "GET" });
        const error = extractTrainingVisibleError(html);
        if (error) return { ok: false, message: error };
        const visible = getTrainingVisibleText(html);
        if (/do not have|don't have|not have enough|cannot afford|missing|required/i.test(visible)) {
            return { ok: false, message: sanitizeTrainingCourseMessage(visible, "Pagamento não confirmado pelo Neopets.") };
        }
        return { ok: true, message: "Pagamento enviado. A página será sincronizada." };
    }

    async function completeTrainingCourse(petName) {
        const school = getTrainingSchoolFromLocation();
        const cleanPetName = normalizeTrainingText(petName);
        if (!school || !cleanPetName) return { ok: false, message: "Abra a página de status da academia para completar." };
        const body = new URLSearchParams({ type: "complete", pet_name: cleanPetName });
        const html = await trainingFetchText(school.processPath, {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
            body: body.toString()
        });
        const error = extractTrainingVisibleError(html);
        if (error) return { ok: false, message: error };
        const stat = (html.match(/increased\s+(\w+)/i) || ["", ""])[1];
        const bonus = html.includes("SUPER BONUS") ? ((html.match(/SUPER BONUS\s*-\s*You went up\s+(\d+)\s+points/i) || ["", "1"])[1]) : "1";
        const state = loadTrainingReaderState();
        Object.keys(TRAINING_SCHOOLS).forEach(key => {
            state.schools[key] = (state.schools[key] || []).filter(item => normalizeTrainingText(item.name).toLowerCase() !== cleanPetName.toLowerCase());
        });
        saveTrainingReaderState(state, `${cleanPetName}: curso completo removido do estado local.`);
        return { ok: true, message: stat ? `Curso completo: +${bonus} ${stat}.` : "Curso enviado para completar. Confira a página." };
    }

    async function startTrainingCourse(petName, courseType) {
        const school = getTrainingSchoolFromLocation();
        const cleanPetName = normalizeTrainingText(petName);
        const cleanCourse = normalizeTrainingText(courseType);
        if (!school || !cleanPetName || !cleanCourse) return { ok: false, message: "Seleção de treino incompleta." };
        const body = new URLSearchParams({ type: "start", course_type: cleanCourse, pet_name: cleanPetName });
        const html = await trainingFetchText(school.processPath, {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
            body: body.toString()
        });
        const error = extractTrainingVisibleError(html);
        if (error) return { ok: false, message: error };
        if (isTrainingCourseStartSuccess(html, cleanPetName, cleanCourse)) return { ok: true, message: "Curso iniciado." };
        return { ok: true, message: "Solicitação enviada. O status será confirmado após sincronização." };
    }

    function refreshTrainingAfterOfficialAction(reason, delayMs) {
        bootTrainingStateReader(reason || "training-official-action");
        publishTrainingExternalState(reason || "training-official-action");
        window.setTimeout(() => { location.reload(); }, Math.max(700, Number(delayMs) || 1200));
    }

    async function payTrainingCourseAction(petName, button) {
        const cleanPetName = normalizeTrainingText(petName);
        const items = getTrainingItemsForPet(cleanPetName);
        if (!trainingConfirmAction(`Pagar o curso de ${cleanPetName}?\n\nIsto pode consumir estes itens do inventário:\n${items.join("\n") || "(itens não detectados)"}`)) return;
        setTrainingCourseButtonState(button, "loading", "Pagando...");
        setTrainingPanelStatus(`${cleanPetName}: pagando curso...`);
        saveTrainingCourseActionReport("running", `${cleanPetName}: pagando curso...`, { action: "pay-course", petName: cleanPetName, items });
        try {
            const result = await payTrainingCourse(cleanPetName);
            const message = `${cleanPetName}: ${sanitizeTrainingCourseMessage(result.message, "Pagamento enviado. Confira a página.")}`;
            setTrainingCourseButtonState(button, result.ok ? "success" : "error", result.ok ? "Pago" : "Erro", message);
            setTrainingPanelStatus(message);
            saveTrainingCourseActionReport(result.ok ? "success" : "error", message, { action: "pay-course", petName: cleanPetName, items, result });
            if (result.ok) refreshTrainingAfterOfficialAction("training-pay-course", 900);
        } catch (error) {
            const message = `${cleanPetName}: falha ao pagar (${errorToMessage(error)}).`;
            setTrainingCourseButtonState(button, "error", "Erro", message);
            setTrainingPanelStatus(message);
            saveTrainingCourseActionReport("error", message, { action: "pay-course", petName: cleanPetName, items, error: errorToMessage(error) });
        }
    }

    async function payAllTrainingCoursesAction(button) {
        const targets = getTrainingPayTargets();
        if (!targets.length) {
            setTrainingPanelStatus("Nenhum curso aguardando pagamento detectado.");
            return;
        }
        const lines = targets.map(target => `${target.petName}: ${getTrainingItemsForPet(target.petName).join(", ") || "itens não detectados"}`);
        if (!trainingConfirmAction(`Pagar TODOS os cursos detectados?\n\n${lines.join("\n")}`)) return;
        const progress = targets.map(target => ({ petName: target.petName, action: "Pagar", status: "Aguardando", tone: "waiting", message: "" }));
        setTrainingCourseButtonState(button, "loading", "Pagando todos...");
        saveTrainingCourseActionReport("running", "Pagando todos os cursos detectados...", { action: "pay-all", count: targets.length });
        let success = 0;
        let failed = 0;
        const failures = [];
        for (let i = 0; i < targets.length; i += 1) {
            const item = progress[i];
            item.status = "Enviando";
            item.tone = "loading";
            updateTrainingBatchProgress(progress, `Pagando ${i + 1} de ${targets.length}`, targets[i].petName);
            try {
                const result = await payTrainingCourse(targets[i].petName);
                if (result.ok) {
                    success += 1;
                    item.status = "Pago";
                    item.tone = "success";
                    item.message = result.message || "Pagamento enviado.";
                } else {
                    failed += 1;
                    item.status = "Erro";
                    item.tone = "error";
                    item.message = sanitizeTrainingCourseMessage(result.message, "Falha no pagamento.");
                    failures.push({ petName: targets[i].petName, message: item.message });
                }
            } catch (error) {
                failed += 1;
                item.status = "Erro";
                item.tone = "error";
                item.message = errorToMessage(error);
                failures.push({ petName: targets[i].petName, message: item.message });
            }
            updateTrainingBatchProgress(progress, `Pagando ${i + 1} de ${targets.length}`, `${success} sucesso(s), ${failed} falha(s).`);
            if (i < targets.length - 1) {
                const delay = getTrainingSafeBatchDelayMs();
                await trainingCountdownDelay(delay, left => updateTrainingBatchProgress(progress, `Pagando ${i + 1} de ${targets.length}`, `Aguardando ${(left / 1000).toFixed(1)}s antes do próximo envio...`));
            }
        }
        const message = `Pagamentos processados: ${success} sucesso(s), ${failed} falha(s).`;
        setTrainingCourseButtonState(button, failed ? "error" : "success", failed ? "Conferir" : "Pagos", message);
        setTrainingPanelStatus(`${message} Sincronizando página...`);
        saveTrainingBatchSummary({ title: "Pagamentos processados.", success, failed, failures });
        saveTrainingCourseActionReport(failed ? "partial" : "success", message, { action: "pay-all", success, failed, failures });
        refreshTrainingAfterOfficialAction("training-pay-all", 1200);
    }

    async function completeTrainingCourseAction(petName, button) {
        const cleanPetName = normalizeTrainingText(petName);
        if (!trainingConfirmAction(`Completar o curso de ${cleanPetName}?`)) return;
        setTrainingCourseButtonState(button, "loading", "Completando...");
        setTrainingPanelStatus(`${cleanPetName}: completando curso...`);
        saveTrainingCourseActionReport("running", `${cleanPetName}: completando curso...`, { action: "complete-course", petName: cleanPetName });
        try {
            const result = await completeTrainingCourse(cleanPetName);
            const message = `${cleanPetName}: ${sanitizeTrainingCourseMessage(result.message, "Curso enviado para completar.")}`;
            setTrainingCourseButtonState(button, result.ok ? "success" : "error", result.ok ? "Completo" : "Erro", message);
            setTrainingPanelStatus(message);
            saveTrainingCourseActionReport(result.ok ? "success" : "error", message, { action: "complete-course", petName: cleanPetName, result });
            if (result.ok) refreshTrainingAfterOfficialAction("training-complete-course", 1200);
        } catch (error) {
            const message = `${cleanPetName}: falha ao completar (${errorToMessage(error)}).`;
            setTrainingCourseButtonState(button, "error", "Erro", message);
            setTrainingPanelStatus(message);
            saveTrainingCourseActionReport("error", message, { action: "complete-course", petName: cleanPetName, error: errorToMessage(error) });
        }
    }

    async function completeAllTrainingCoursesAction(button) {
        const targets = getTrainingCompleteTargets();
        if (!targets.length) {
            setTrainingPanelStatus("Nenhum curso completo detectado na página.");
            return;
        }
        if (!trainingConfirmAction(`Completar TODOS os cursos detectados?\n\n${targets.map(target => target.petName).join("\n")}`)) return;
        const progress = targets.map(target => ({ petName: target.petName, action: "Completar", status: "Aguardando", tone: "waiting", message: "" }));
        setTrainingCourseButtonState(button, "loading", "Completando todos...");
        let success = 0;
        let failed = 0;
        const failures = [];
        saveTrainingCourseActionReport("running", "Completando todos os cursos detectados...", { action: "complete-all", count: targets.length });
        for (let i = 0; i < targets.length; i += 1) {
            const item = progress[i];
            item.status = "Enviando";
            item.tone = "loading";
            updateTrainingBatchProgress(progress, `Completando ${i + 1} de ${targets.length}`, targets[i].petName);
            try {
                const result = await completeTrainingCourse(targets[i].petName);
                if (result.ok) {
                    success += 1;
                    item.status = "Completo";
                    item.tone = "success";
                    item.message = result.message || "Curso completo.";
                } else {
                    failed += 1;
                    item.status = "Erro";
                    item.tone = "error";
                    item.message = sanitizeTrainingCourseMessage(result.message, "Falha ao completar.");
                    failures.push({ petName: targets[i].petName, message: item.message });
                }
            } catch (error) {
                failed += 1;
                item.status = "Erro";
                item.tone = "error";
                item.message = errorToMessage(error);
                failures.push({ petName: targets[i].petName, message: item.message });
            }
            updateTrainingBatchProgress(progress, `Completando ${i + 1} de ${targets.length}`, `${success} sucesso(s), ${failed} falha(s).`);
            if (i < targets.length - 1) {
                const delay = getTrainingSafeBatchDelayMs();
                await trainingCountdownDelay(delay, left => updateTrainingBatchProgress(progress, `Completando ${i + 1} de ${targets.length}`, `Aguardando ${(left / 1000).toFixed(1)}s antes do próximo envio...`));
            }
        }
        const message = `Cursos completos processados: ${success} sucesso(s), ${failed} falha(s).`;
        setTrainingCourseButtonState(button, failed ? "error" : "success", failed ? "Conferir" : "Completos", message);
        setTrainingPanelStatus(`${message} Sincronizando página...`);
        saveTrainingBatchSummary({ title: "Cursos completos processados.", success, failed, failures });
        saveTrainingCourseActionReport(failed ? "partial" : "success", message, { action: "complete-all", success, failed, failures });
        refreshTrainingAfterOfficialAction("training-complete-all", 1400);
    }

    async function startSelectedTrainingCoursesAction(button) {
        const panel = document.getElementById(TRAINING_PAGE_PANEL_ID);
        const selected = Array.from(panel?.querySelectorAll('input[data-training-course="1"]:checked') || []).map(input => ({
            petName: normalizeTrainingText(input.dataset.petName || ""),
            course: normalizeTrainingText(input.value || ""),
            status: "Aguardando",
            tone: "waiting",
            message: ""
        })).filter(item => item.petName && item.course);
        if (!selected.length) {
            setTrainingPanelStatus("Nenhum treino selecionado.");
            setTrainingProgressHtml("<b>Nenhum treino selecionado.</b>");
            return;
        }
        if (!trainingConfirmAction(`Iniciar estes treinamentos?\n\n${selected.map(item => `${item.petName}: ${item.course}`).join("\n")}`)) return;
        Array.from(panel?.querySelectorAll("button, input") || []).forEach(el => { el.disabled = true; });
        setTrainingCourseButtonState(button, "loading", "Iniciando...");
        saveTrainingCourseActionReport("running", "Iniciando cursos selecionados...", { action: "start-selected", selected });
        let success = 0;
        let failed = 0;
        const failures = [];
        updateTrainingBatchProgress(selected, "Iniciando treinos selecionados...", "As solicitações serão enviadas uma por vez, com intervalo de segurança.");
        for (let i = 0; i < selected.length; i += 1) {
            const item = selected[i];
            item.status = "Enviando";
            item.tone = "loading";
            item.message = "Solicitação em andamento.";
            updateTrainingBatchProgress(selected, `Iniciando ${i + 1} de ${selected.length}`, `${item.petName} — ${item.course}`);
            try {
                const result = await startTrainingCourse(item.petName, item.course);
                if (result.ok) {
                    success += 1;
                    item.status = "Iniciado";
                    item.tone = "success";
                    item.message = result.message || "Curso iniciado.";
                } else {
                    failed += 1;
                    item.status = "Erro";
                    item.tone = "error";
                    item.message = sanitizeTrainingCourseMessage(result.message, "Falha ao iniciar.");
                    failures.push({ petName: item.petName, course: item.course, message: item.message });
                }
            } catch (error) {
                failed += 1;
                item.status = "Erro";
                item.tone = "error";
                item.message = errorToMessage(error);
                failures.push({ petName: item.petName, course: item.course, message: item.message });
            }
            updateTrainingBatchProgress(selected, `Iniciando ${i + 1} de ${selected.length}`, `${success} sucesso(s), ${failed} falha(s).`);
            if (i < selected.length - 1) {
                const delay = getTrainingSafeBatchDelayMs();
                await trainingCountdownDelay(delay, left => updateTrainingBatchProgress(selected, `Iniciando ${i + 1} de ${selected.length}`, `Aguardando ${(left / 1000).toFixed(1)}s antes do próximo envio...`));
            }
        }
        const message = `Treinos selecionados processados: ${success} iniciado(s), ${failed} falha(s).`;
        setTrainingPanelStatus(`${message} Sincronizando status da academia...`);
        saveTrainingBatchSummary({ title: "Treinos selecionados processados.", success, failed, failures });
        saveTrainingCourseActionReport(failed ? "partial" : "success", message, { action: "start-selected", success, failed, failures });
        refreshTrainingAfterOfficialAction("training-start-selected", 2000);
    }

    function handleTrainingPagePanelClick(event) {
        const button = event.target.closest("[data-training-panel-action]");
        if (!button || !button.closest(`#${TRAINING_PAGE_PANEL_ID}`)) return;
        const action = button.dataset.trainingPanelAction || "";
        const itemName = normalizeTrainingText(button.dataset.itemName || "");
        const petName = normalizeTrainingText(button.dataset.petName || "");
        if (action === "get-pet-sdb-items") {
            getTrainingItemsForPetAction(petName, button);
            return;
        }
        if (action === "get-all-sdb-items") {
            getAllTrainingSdbItemsAction(button);
            return;
        }
        if (action === "open-pet-sdb-search") {
            openTrainingSdbForPet(petName, button);
            return;
        }
        if (action === "open-sdb-item") {
            openTrainingNeutralUrl(`/safetydeposit.phtml?obj_name=${encodeURIComponent(itemName)}`, `SDB aberto para: ${itemName}`);
            return;
        }
        if (action === "open-ssw-item") {
            openTrainingNeutralUrl(`/ssw.phtml?string=${encodeURIComponent(itemName)}`, `SSW aberto para: ${itemName}`);
            return;
        }
        if (action === "copy-item-name") {
            copyTrainingPanelText(itemName, `Nome copiado: ${itemName}`);
            return;
        }
        if (action === "copy-pending-items") {
            const text = formatTrainingItemCounts(getTrainingPendingRequiredItemCounts());
            copyTrainingPanelText(text, text ? "Lista de itens pendentes copiada." : "Nenhum item pendente detectado.");
            return;
        }
        if (action === "open-first-pending-sdb") {
            const first = Object.keys(getTrainingPendingRequiredItemCounts())[0] || "";
            if (first) openTrainingNeutralUrl(`/safetydeposit.phtml?obj_name=${encodeURIComponent(first)}`, `SDB aberto para: ${first}`);
            else setTrainingPanelStatus("Nenhum item pendente detectado.");
            return;
        }
        if (action === "pay-course") {
            payTrainingCourseAction(petName, button);
            return;
        }
        if (action === "pay-all-courses") {
            payAllTrainingCoursesAction(button);
            return;
        }
        if (action === "complete-course") {
            completeTrainingCourseAction(petName, button);
            return;
        }
        if (action === "complete-all-courses") {
            completeAllTrainingCoursesAction(button);
            return;
        }
        if (action === "start-selected-courses") {
            startSelectedTrainingCoursesAction(button);
            return;
        }
        if (action === "clear-selected-courses") {
            const panel = document.getElementById(TRAINING_PAGE_PANEL_ID);
            panel?.querySelectorAll('input[data-training-course="1"]').forEach(input => { input.checked = false; });
            setTrainingPanelStatus("Seleção de cursos limpa.");
        }
    }

    function handleTrainingPagePanelChange(event) {
        const control = event.target.closest('input[data-training-course="1"]');
        if (!control || !control.closest(`#${TRAINING_PAGE_PANEL_ID}`)) return;
        setTrainingPanelStatus(`${control.dataset.petName || "Pet"}: ${control.value || "curso"} selecionado. Use “Iniciar selecionados” para enviar com confirmação.`);
    }

    function openTrainingNeutralUrl(url, message) {
        const safeUrl = String(url || "");
        if (!safeUrl) return;
        try { window.open(safeUrl, "_blank", "noopener,noreferrer"); }
        catch (_) { location.href = safeUrl; }
        setTrainingPanelStatus(message || "Busca aberta.");
    }

    function copyTrainingPanelText(text, message) {
        const value = String(text || "").trim();
        if (!value) {
            setTrainingPanelStatus(message || "Nada para copiar.");
            return;
        }
        if (navigator.clipboard?.writeText) {
            navigator.clipboard.writeText(value).then(() => setTrainingPanelStatus(message || "Texto copiado."), () => fallbackTrainingPanelCopy(value));
        } else {
            fallbackTrainingPanelCopy(value);
        }
    }

    function fallbackTrainingPanelCopy(value) {
        try {
            const textarea = document.createElement("textarea");
            textarea.value = value;
            textarea.setAttribute("readonly", "readonly");
            textarea.style.position = "fixed";
            textarea.style.left = "-9999px";
            textarea.style.top = "-9999px";
            document.body.appendChild(textarea);
            textarea.select();
            const ok = document.execCommand("copy");
            textarea.remove();
            setTrainingPanelStatus(ok ? "Texto copiado." : "Não consegui copiar automaticamente.");
        } catch (_) {
            setTrainingPanelStatus("Não consegui copiar automaticamente.");
        }
    }

    function setTrainingPanelStatus(message) {
        const box = document.querySelector(`#${TRAINING_PAGE_PANEL_ID} .ncc-training-status`);
        if (box) box.textContent = String(message || "");
    }

    function publishExternalState(reason) {
        try {
            const externalState = buildExternalState(reason);
            lastPublishedAt = externalState.updatedAt;
            writeExternalStateToStorage(externalState);
            publishExternalStateToGlobals(externalState);
            dispatchExternalState(externalState);
            publishTrainingExternalState(reason || "external-state");
            publishBattledomeExternalState(reason || "external-state");
            writeBaseEntryContract(reason || "external-state");
            writeBaseEntryReadiness(reason || "external-state");
            return externalState;
        } catch (error) {
            bootErrors.push(errorToMessage(error));
            console.warn("[NCC AAA] Falha ao publicar External State.", error);
            return null;
        }
    }

    function writeExternalStateToStorage(externalState) {
        try {
            localStorage.setItem(EXTERNAL_STATE_KEY, JSON.stringify(externalState));
        } catch (error) {
            bootErrors.push(`external-storage:${errorToMessage(error)}`);
        }
    }

    function publishExternalStateToGlobals(externalState) {
        const publicWindow = getPublicWindow();
        try { window[GLOBAL_EXTERNAL_STATE_NAME] = externalState; } catch (_) {}
        try { window.NCC_AAA_LAST_EXTERNAL_STATE_AT = externalState.updatedAt; } catch (_) {}
        if (publicWindow && publicWindow !== window) {
            try { publicWindow[GLOBAL_EXTERNAL_STATE_NAME] = externalState; } catch (_) {}
            try { publicWindow.NCC_AAA_LAST_EXTERNAL_STATE_AT = externalState.updatedAt; } catch (_) {}
        }
    }

    function dispatchExternalState(externalState) {
        dispatchStateEvent(window, EXTERNAL_STATE_EVENT, externalState);
        dispatchStateEvent(window, STATE_EVENT, externalState);
        dispatchStateEvent(document, EXTERNAL_STATE_EVENT, externalState);
        dispatchStateEvent(document, STATE_EVENT, externalState);

        const publicWindow = getPublicWindow();
        if (publicWindow && publicWindow !== window) {
            dispatchStateEvent(publicWindow, EXTERNAL_STATE_EVENT, externalState);
            dispatchStateEvent(publicWindow, STATE_EVENT, externalState);
        }
    }

    function dispatchStateEvent(target, eventName, externalState) {
        if (!target || !eventName) return false;
        try {
            target.dispatchEvent(new CustomEvent(eventName, {
                detail: clonePlain(externalState)
            }));
            return true;
        } catch (_) {
            return false;
        }
    }

    function exposeBridge() {
        const bridge = Object.freeze({
            version: VERSION,
            schema: "ncc-aaa-bridge-v1",
            getVersion: () => VERSION,
            getFoundation: () => clonePlain(FOUNDATION),
            getCommunicationContract: () => clonePlain(COMMUNICATION_CONTRACT),
            getBaseEntryContract: () => clonePlain(buildBaseEntryContract("bridge")),
            getBaseEntryReadiness: () => clonePlain(buildBaseEntryReadiness("bridge")),
            publishBaseEntryContract: (reason) => {
                writeBaseEntryContract(reason || "bridge");
                return clonePlain(writeBaseEntryReadiness(reason || "bridge"));
            },
            getIdentity: () => clonePlain(identity),
            getState: () => clonePlain(state),
            getStateMeta: () => clonePlain(buildStateMeta()),
            getVisualPreferences: () => clonePlain(readJson(visualPreferencesKey, null)),
            getStorageReport: () => clonePlain(buildStorageReport()),
            getBridgeStatus: () => clonePlain(buildBridgeStatus()),
            getMountReport: () => clonePlain(buildMountReport()),
            getSafetyReport: () => clonePlain(buildSafetyReport()),
            getCssSafetyReport: () => clonePlain(buildCssSafetyReport()),
            getRenderReport: () => clonePlain(buildRenderReport()),
            getCoreOrganizationReport: () => clonePlain(buildCoreOrganizationReport()),
            getCellblockReport: () => clonePlain(buildCellblockReport()),
            getKikoPopReport: () => clonePlain(buildKikoPopReport()),
            getNeoQuestReport: () => clonePlain(buildNeoQuestReport()),
            getNeoQuestTwoReport: () => clonePlain(buildNeoQuestTwoReport()),
            getTyranuEvavuReport: () => clonePlain(buildTyranuEvavuReport("bridge")),
            getKissTheMortogReport: () => clonePlain(buildKissTheMortogReport("bridge")),
            getGodoriReport: () => clonePlain(buildGodoriReport("bridge")),
            getArenaModuleRegistry: () => clonePlain(ARENA_MODULE_REGISTRY),
            getTrainingExternalState: () => clonePlain(buildTrainingExternalState("bridge")),
            getTrainingSdbReport: () => clonePlain(getTrainingSdbReportFallback()),
            getTrainingCourseActionReport: () => clonePlain(getTrainingCourseActionReportFallback()),
            getTrainingEquivalenceAudit: () => clonePlain(buildTrainingEquivalenceAuditReport("bridge")),
            getBattledomeTrackerState: () => clonePlain(loadBattledomeState()),
            getBattledomeTrackerReport: () => clonePlain(buildBattledomeTrackerReport("bridge")),
            getBattledomeFavoriteOpponents: () => clonePlain(getBattledomeFavoriteOpponents()),
            getBattledomeFilteredPrizes: () => clonePlain(getBattledomeFilteredPrizes(loadBattledomeState())),
            getBattledomeLoadoutWeapons: () => clonePlain(loadBattledomeState().loadoutWeapons || []),
            applyBattledomeFavoriteLoadout: () => applyBattledomeFavoriteLoadout({ force: true }),
            getBattledomeTrackerAudit: () => clonePlain(buildBattledomeTrackerAuditReport("bridge")),
            getBattledomeManualTestPlan: () => clonePlain(buildBattledomeManualTestPlan("bridge")),
            getBattledomeAssistedTestChecklist: () => clonePlain(buildBattledomeAssistedTestChecklist("bridge")),
            copyBattledomeAssistedTestReport: () => copyBattledomeAssistedTestReport(),
            copyBattledomeCurrentLog: () => copyBattledomeCurrentLog(),
            getBattledomeTrackerContract: () => clonePlain(BATTLEDOME_TRACKER_CONTRACT),
            getBattledomeExternalState: () => clonePlain(buildBattledomeExternalState("bridge")),
            getTrophyTrackerState: () => clonePlain(loadTrophyTrackerState()),
            getTrophyTrackerReport: () => clonePlain(buildTrophyTrackerReport("bridge")),
            getTrophyTrackerCatalog: () => clonePlain(TROPHY_TRACKER_CATEGORIES),
            publishBattledomeExternalState: (reason) => clonePlain(publishBattledomeExternalState(reason || "bridge-battledome-publish")),
            syncTrainingStateReader: (reason) => clonePlain(bootTrainingStateReader(reason || "bridge-training-sync")),
            publishTrainingExternalState: (reason) => clonePlain(publishTrainingExternalState(reason || "bridge-training-publish")),
            getHealthReport: () => clonePlain(buildHealthReport("bridge")),
            getBackstageReport: () => clonePlain(buildBackstageReport("bridge")),
            runSelfTest: () => clonePlain(saveSelfTest("bridge")),
            saveHealthReport: () => clonePlain(saveHealthReport("bridge")),
            getLastPublishedAt: () => lastPublishedAt,
            isReady: () => true,
            ping: () => ({ ok: true, source: "ncc-aaa", version: VERSION, at: isoNow() }),
            resetVisualPreferences: () => {
                resetVisualPreferences();
                return clonePlain(readJson(visualPreferencesKey, null));
            },
            getExternalState: () => buildExternalState("bridge"),
            publishExternalState: (reason) => publishExternalState(reason || "bridge-publish"),
            getDiagnostic: () => buildDiagnostic(),
            refresh: () => {
                const published = publishExternalState("bridge-refresh");
                render({ reason: "bridge-refresh", forceFull: true });
                return Boolean(published);
            }
        });

        const publicWindow = getPublicWindow();
        defineBridge(window, bridge);
        if (publicWindow && publicWindow !== window) defineBridge(publicWindow, bridge);
    }

    function getBridgeMethodNames() {
        return [
            "getVersion",
            "getFoundation",
            "getCommunicationContract",
            "getBaseEntryContract",
            "getBaseEntryReadiness",
            "publishBaseEntryContract",
            "getIdentity",
            "getState",
            "getStateMeta",
            "getVisualPreferences",
            "getStorageReport",
            "getBridgeStatus",
            "getMountReport",
            "getSafetyReport",
            "getCssSafetyReport",
            "getRenderReport",
            "getCoreOrganizationReport",
            "getCellblockReport",
            "getKikoPopReport",
            "getNeoQuestReport",
            "getNeoQuestTwoReport",
            "getTyranuEvavuReport",
            "getKissTheMortogReport",
            "getGodoriReport",
            "getArenaModuleRegistry",
            "getTrainingExternalState",
            "getTrainingSdbReport",
            "getTrainingCourseActionReport",
            "getTrainingEquivalenceAudit",
            "getBattledomeTrackerState",
            "getBattledomeTrackerReport",
            "getBattledomeFavoriteOpponents",
                "getBattledomeFilteredPrizes",
            "getBattledomeLoadoutWeapons",
            "applyBattledomeFavoriteLoadout",
            "getBattledomeTrackerAudit",
            "getBattledomeManualTestPlan",
            "getBattledomeAssistedTestChecklist",
            "copyBattledomeAssistedTestReport",
            "copyBattledomeCurrentLog",
            "getBattledomeTrackerContract",
            "getBattledomeExternalState",
            "publishBattledomeExternalState",
            "getTrophyTrackerState",
            "getTrophyTrackerReport",
            "getTrophyTrackerCatalog",
            "syncTrainingStateReader",
            "publishTrainingExternalState",
            "getHealthReport",
            "getBackstageReport",
            "runSelfTest",
            "saveHealthReport",
            "getLastPublishedAt",
            "isReady",
            "ping",
            "resetVisualPreferences",
            "getExternalState",
            "publishExternalState",
            "getDiagnostic",
            "refresh"
        ];
    }

    function buildStateMeta() {
        return clonePlain({
            schema: "ncc-aaa-state-meta-v1",
            version: VERSION,
            at: isoNow(),
            playerId: state?.playerId || identity?.playerId || "local",
            username: state?.username || identity?.username || "Neopiano",
            revision: normalizeRevision(state?.revision),
            sessionId: state?.sessionId || SESSION_ID,
            localSessionId: SESSION_ID,
            lastCommitReason: state?.lastCommitReason || "",
            lastStorageSyncAt,
            storageKey: storageKey || "",
            visualPreferencesKey: visualPreferencesKey || "",
            immutableCommits: true,
            secondaryWritesDebounced: true,
            pendingDebouncedWrites: debouncedStorageWrites.size,
            storageSyncListenerRegistered,
            renderPartialEnabled: true,
            lastRenderMode,
            lastRenderReason
        });
    }

    function buildBridgeStatus() {
        const publicWindow = getPublicWindow();
        return clonePlain({
            schema: "ncc-aaa-bridge-status-v1",
            version: VERSION,
            at: isoNow(),
            bridgeName: GLOBAL_BRIDGE_NAME,
            windowBridge: Boolean(window[GLOBAL_BRIDGE_NAME]),
            unsafeWindowBridge: Boolean(publicWindow?.[GLOBAL_BRIDGE_NAME]),
            externalStateGlobal: Boolean(window[GLOBAL_EXTERNAL_STATE_NAME] || publicWindow?.[GLOBAL_EXTERNAL_STATE_NAME]),
            externalStateKey: EXTERNAL_STATE_KEY,
            eventNames: [EXTERNAL_STATE_EVENT, STATE_EVENT, BASE_ENTRY_EVENT, TRAINING_EXTERNAL_STATE_EVENT, BATTLEDOME_EXTERNAL_STATE_EVENT],
            methods: getBridgeMethodNames(),
            ready: true,
            mirrorOnly: false,
            renderPartialEnabled: true
        });
    }

    function defineBridge(targetWindow, bridge) {
        try {
            Object.defineProperty(targetWindow, "NCCAAA", {
                value: bridge,
                writable: false,
                configurable: true
            });
        } catch (_) {
            try { targetWindow.NCCAAA = bridge; } catch (__) {}
        }
    }

    function buildMountReport() {
        return clonePlain({
            schema: "ncc-aaa-mount-report-v1",
            version: VERSION,
            at: isoNow(),
            expectedPath: MOUNT_PATH,
            legacyPath: LEGACY_MOUNT_PATH,
            supportedPaths: MOUNT_PATHS,
            currentPath: location.pathname,
            isMountPage: isMountPage(),
            isPrimaryMountPage: isPrimaryMountPage(),
            isLegacyMountPage: isLegacyMountPage(),
            appId: APP_ID,
            styleId: STYLE_ID,
            markerId: MARKER_ID,
            rootExists: Boolean(root),
            targetFound: Boolean(mountedTarget),
            targetSelector: mountedTarget ? describeElement(mountedTarget) : "",
            targetPreservesShell: true,
            fullWidthMount: document.documentElement.getAttribute("data-ncc-aaa-fullwidth") === "true",
            sidebarHiddenForAaa: Boolean(document.querySelector("[data-ncc-aaa-hidden-sidebar='true']")),
            contentColSpan: mountedTarget && typeof mountedTarget.colSpan === "number" ? mountedTarget.colSpan : null,
            replacesSketchesContentOnly: isLegacyMountPage(),
            replacesPetpageBody: isPrimaryMountPage(),
            headerPresent: Boolean(document.querySelector("#header")),
            sidebarPresent: Boolean(document.querySelector("td.sidebar, .sidebar")),
            footerPresent: Boolean(document.querySelector("#footer")),
            superfooterPresent: Boolean(document.querySelector("#superfooter")),
            title: document.title || ""
        });
    }

    function buildSafetyReport() {
        return clonePlain({
            schema: "ncc-aaa-safety-report-v1",
            version: VERSION,
            at: isoNow(),
            mode: "game-helper-foundation",
            moduleCount: GAME_MODULE_REGISTRY.length,
            migratedGameModules: GAME_MODULE_REGISTRY.map(module => module.id),
            automaticOfficialAction: false,
            automaticGameAction: "conditional-user-toggle",
            automaticGameActionWithoutUserToggle: false,
            clickAutomation: "neoquest-user-keypress-and-session-toggle-autoplayers",
            keyboardAutomation: "neoquest-map-shortcuts-only",
            formSubmit: "autoplayers-only-after-session-toggle",
            formSubmitWithoutUserToggle: false,
            networkAccess: "kiko-pop-page-jquery-after-click-and-neoquest2-keyh-xhr-only",
            storageDeletion: false,
            cssCustomPropertiesSanitized: true,
            cssInlineStyleHardened: true,
            touchesNccBaseKeys: false,
            touchesNccRpgKeys: false,
            automationModules: {
                tyranuEvavu: {
                    pageOnly: TYRANU_EVAVU_PATH,
                    enabledByDefault: false,
                    requiresFloatingToggle: true,
                    sessionOnlyToggle: true,
                    programmaticClicks: "only after user turns module ON for the current session",
                    formSubmit: "only after user turns module ON for the current session",
                    networkAccessAdded: false
                },
                kissTheMortog: {
                    pageOnly: KISS_THE_MORTOG_PLAY_PATH,
                    processPage: KISS_THE_MORTOG_PROCESS_PATH,
                    enabledByDefault: false,
                    requiresFloatingToggle: true,
                    sessionOnlyToggle: true,
                    maxSafeScore: KISS_THE_MORTOG_MAX_SAFE_SCORE,
                    programmaticClicks: "only after user turns module ON for the current session",
                    formSubmit: "only after user turns module ON for the current session",
                    networkAccessAdded: false
                },
                godori: {
                    pageOnly: GODORI_PATH_PREFIX,
                    enabledByDefault: false,
                    requiresFloatingToggle: true,
                    sessionOnlyToggle: true,
                    avatarTargetHands: 250,
                    smartAlertHandler: true,
                    programmaticClicks: "only after user turns module ON for the current session",
                    formSubmit: "only after user turns module ON for the current session",
                    networkAccessAdded: false
                }
            },
            allowedUiActions: [
                "switch-view",
                "open-avatar-modal",
                "close-avatar-modal",
                "select-avatar",
                "select-team",
                "select-theme",
                "copy-diagnostic",
                "copy-health",
                "save-health-report",
                "refresh",
                "reset-visual",
                "bd-copy-log",
                "bd-toggle-prize-paused",
                "bd-clear-log",
                "bd-clear-opponents",
                "bd-toggle-loadout-enabled",
                "bd-apply-loadout",
                "bd-clear-loadout",
                "bd-test-step-status",
                "bd-test-copy-report",
                "bd-test-reset",
                "trophy-copy-summary",
                "trophy-open-popup",
                "tyranu-toggle-autoplayer",
                "kiss-toggle-autoplayer",
                "kiss-target-change"
            ],
            actionGroups: {
                visualPreferences: ["open-avatar-modal", "select-avatar", "select-team", "select-theme", "reset-visual"],
                battlegroundLocal: ["bd-copy-log", "bd-apply-loadout", "bd-toggle-loadout-enabled"],
                trophyTrackerReadOnly: ["trophy-copy-summary", "trophy-open-popup"],
                tyranuEvavuAutoplayer: ["tyranu-toggle-autoplayer"],
                kissTheMortogAutoplayer: ["kiss-toggle-autoplayer", "kiss-target-change"],
                godoriAutoplayer: ["godori-toggle-autoplayer", "godori-auto-play-again", "godori-auto-stop-250"],
                diagnostics: ["copy-diagnostic", "copy-health", "save-health-report", "refresh"]
            }
        });
    }

    function buildCssSafetyReport() {
        const themeId = normalizeThemeId(state?.themeId) || FALLBACK_THEME_ID;
        const rawTone = THEME_TONES[themeId] || THEME_TONES[FALLBACK_THEME_ID] || THEME_TONES.basic || {};
        const normalizedTone = normalizeThemeTone(rawTone);
        const checks = CSS_THEME_COLOR_KEYS.map(key => {
            const rawValue = rawTone?.[key];
            const fallbackValue = CSS_THEME_COLOR_FALLBACKS[key];
            const normalizedValue = normalizedTone[key];
            return {
                key,
                rawValue: String(rawValue == null ? "" : rawValue),
                normalizedValue,
                fallbackValue,
                valid: isSafeHexColor(rawValue),
                usedFallback: normalizedValue === fallbackValue && String(rawValue || "").trim() !== fallbackValue
            };
        });
        const invalid = checks.filter(check => !check.valid);
        return clonePlain({
            schema: "ncc-aaa-css-safety-report-v1",
            version: VERSION,
            at: isoNow(),
            themeId,
            stylePolicy: "hex-only-css-custom-properties",
            inlineStyleMode: "sanitized-css-variables",
            allowedColorPattern: "#RGB, #RRGGBB, #RRGGBBAA",
            safe: invalid.length === 0,
            invalidCount: invalid.length,
            checks,
            sanitizedTone: normalizedTone,
            styleAttribute: buildThemeStyleAttribute(normalizedTone)
        });
    }

    function buildStorageHealth() {
        const externalRaw = readJson(EXTERNAL_STATE_KEY, null);
        return clonePlain({
            schema: "ncc-aaa-storage-health-v1",
            version: VERSION,
            at: isoNow(),
            available: storageAvailable(),
            namespace: "ncc.aaa",
            keys: {
                identity: IDENTITY_KEY,
                playerId: PLAYER_ID_KEY,
                username: PLAYER_USERNAME_KEY,
                latestState: LATEST_STATE_KEY,
                state: storageKey || "",
                visualPreferences: visualPreferencesKey || "",
                externalState: EXTERNAL_STATE_KEY,
                storageManifest: STORAGE_MANIFEST_KEY,
                storageAudit: STORAGE_AUDIT_KEY,
                lastDiagnostic: LAST_DIAGNOSTIC_KEY,
                lastHealthReport: LAST_HEALTH_REPORT_KEY,
                lastSelfTest: LAST_SELF_TEST_KEY,
                baseEntryContract: BASE_ENTRY_CONTRACT_KEY,
                baseEntryReadiness: BASE_ENTRY_READINESS_KEY,
                baseEntryEvent: BASE_ENTRY_EVENT,
                globalBaseEntryContract: GLOBAL_BASE_ENTRY_CONTRACT_NAME,
                globalBaseEntryReadiness: GLOBAL_BASE_ENTRY_READINESS_NAME
            },
            hasState: Boolean(storageKey && readJson(storageKey, null)),
            hasVisualPreferences: Boolean(visualPreferencesKey && readJson(visualPreferencesKey, null)),
            hasExternalState: Boolean(externalRaw),
            hasBaseEntryContract: Boolean(readJson(BASE_ENTRY_CONTRACT_KEY, null)),
            hasBaseEntryReadiness: Boolean(readJson(BASE_ENTRY_READINESS_KEY, null)),
            externalSchema: externalRaw?.schema || "",
            externalVersion: externalRaw?.version || "",
            perUserIsolation: true,
            immutableCommits: true,
            revision: normalizeRevision(state?.revision),
            sessionId: state?.sessionId || SESSION_ID,
            localSessionId: SESSION_ID,
            lastCommitReason: state?.lastCommitReason || "",
            lastStorageSyncAt,
            secondaryWritesDebounced: true,
            pendingDebouncedWrites: debouncedStorageWrites.size,
            touchesNccBaseKeys: false,
            touchesNccRpgKeys: false
        });
    }

    function buildCommunicationHealth() {
        const publicWindow = getPublicWindow();
        const bridge = window[GLOBAL_BRIDGE_NAME] || publicWindow?.[GLOBAL_BRIDGE_NAME] || null;
        const methodNames = getBridgeMethodNames();
        const missingMethods = methodNames.filter(name => typeof bridge?.[name] !== "function");
        return clonePlain({
            schema: "ncc-aaa-communication-health-v1",
            version: VERSION,
            at: isoNow(),
            bridgeName: GLOBAL_BRIDGE_NAME,
            bridgeReady: Boolean(bridge),
            windowBridge: Boolean(window[GLOBAL_BRIDGE_NAME]),
            unsafeWindowBridge: Boolean(publicWindow?.[GLOBAL_BRIDGE_NAME]),
            globalExternalState: Boolean(window[GLOBAL_EXTERNAL_STATE_NAME] || publicWindow?.[GLOBAL_EXTERNAL_STATE_NAME]),
            localStorageExternalState: Boolean(readJson(EXTERNAL_STATE_KEY, null)),
            events: [EXTERNAL_STATE_EVENT, STATE_EVENT, BASE_ENTRY_EVENT],
            methodCount: methodNames.length,
            expectedMethods: methodNames,
            missingMethods,
            ready: Boolean(bridge) && missingMethods.length === 0
        });
    }

    function buildSelfTest(reason) {
        const mount = buildMountReport();
        const storage = buildStorageHealth();
        const bridge = buildCommunicationHealth();
        const safety = buildSafetyReport();
        const cssSafety = buildCssSafetyReport();
        const renderReport = buildRenderReport();
        const coreOrganization = buildCoreOrganizationReport();
        const checks = [
            { id: "path-is-supported-mount", ok: isMountPage(mount.currentPath), detail: `${mount.currentPath} · principal ${MOUNT_PATH} · legado ${LEGACY_MOUNT_PATH}` },
            { id: "root-mounted-or-nonvisual-page", ok: !isMountPage(mount.currentPath) || mount.rootExists, detail: mount.rootExists ? "root ok" : "root ausente" },
            { id: "target-found-or-nonvisual-page", ok: !isMountPage(mount.currentPath) || mount.targetFound, detail: mount.targetSelector || "sem alvo" },
            { id: "storage-available", ok: storage.available, detail: storage.available ? "ok" : "indisponível" },
            { id: "external-state-stored", ok: storage.hasExternalState, detail: storage.externalSchema || "sem external state" },
            { id: "base-entry-contract-stored", ok: storage.hasBaseEntryContract, detail: BASE_ENTRY_CONTRACT_KEY },
            { id: "base-entry-readiness-stored", ok: storage.hasBaseEntryReadiness, detail: BASE_ENTRY_READINESS_KEY },
            { id: "bridge-ready", ok: bridge.ready, detail: bridge.missingMethods.length ? bridge.missingMethods.join(", ") : "ok" },
            { id: "official-actions-protected", ok: safety.automaticOfficialAction === false && safety.automaticGameActionWithoutUserToggle === false, detail: "sem ação oficial automática; autoplayers só por toggle de sessão" },
            { id: "minimal-game-modules-registered", ok: safety.moduleCount === GAME_MODULE_REGISTRY.length, detail: "Cellblock, Kiko Pop, Tyranu Evavu, Kiss The Mortog, Godori, NeoQuest I e NeoQuest II ativos" },
            { id: "no-network", ok: (safety.networkAccess === false || String(safety.networkAccess).includes("neoquest2-keyh-xhr-only")), detail: "sem rede em background" },
            { id: "css-theme-sanitized", ok: cssSafety.safe, detail: cssSafety.safe ? "hex-only ok" : `${cssSafety.invalidCount} cor(es) inválida(s)` },
            { id: "render-partial-ready", ok: renderReport.partialRenderEnabled && renderReport.fullRenderFallback, detail: renderReport.lastRenderMode || "aguardando" },
            { id: "core-organization-ready", ok: coreOrganization.sectionCount >= 16 && coreOrganization.gameModuleRegistry.total === GAME_MODULE_REGISTRY.length && coreOrganization.trophyTracker?.ready && coreOrganization.tyranuEvavu?.ready && coreOrganization.kissTheMortog?.ready && coreOrganization.godori?.ready, detail: `${coreOrganization.sectionCount} áreas / módulos de jogos + Arena + Trophy` },
            { id: "no-base-rpg-touch", ok: !storage.touchesNccBaseKeys && !storage.touchesNccRpgKeys, detail: "isolado" }
        ];
        const failed = checks.filter(check => !check.ok);
        return clonePlain({
            schema: "ncc-aaa-self-test-v1",
            version: VERSION,
            reason: reason || "unknown",
            at: isoNow(),
            passed: failed.length === 0,
            total: checks.length,
            passedCount: checks.length - failed.length,
            failedCount: failed.length,
            checks,
            failed
        });
    }

    function saveSelfTest(reason) {
        const test = buildSelfTest(reason || "manual");
        writeJson(LAST_SELF_TEST_KEY, test);
        showToast(test.passed ? "Autoteste salvo: tudo ok." : "Autoteste salvo com avisos.");
        return test;
    }

    function buildHealthSummary() {
        const mount = buildMountReport();
        const storage = buildStorageHealth();
        const bridge = buildCommunicationHealth();
        const safety = buildSafetyReport();
        const cssSafety = buildCssSafetyReport();
        const coreOrganization = buildCoreOrganizationReport();
        const errorCount = bootErrors.length + (lastRenderError ? 1 : 0);
        return clonePlain({
            mountStatus: isMountPage(mount.currentPath) ? (mount.rootExists && mount.targetFound ? "ok" : "atenção") : "standby",
            storageStatus: storage.available ? "ok" : "atenção",
            bridgeStatus: bridge.ready ? "ok" : "atenção",
            safetyStatus: !safety.automaticOfficialAction && safety.automaticGameActionWithoutUserToggle === false ? "ok" : "atenção",
            cssSafetyStatus: cssSafety.safe ? "ok" : "atenção",
            renderStatus: lastRenderError ? "atenção" : "ok",
            coreOrganizationStatus: coreOrganization.sectionCount >= 16 && coreOrganization.gameModuleRegistry.total === GAME_MODULE_REGISTRY.length && coreOrganization.trophyTracker?.ready && coreOrganization.tyranuEvavu?.ready && coreOrganization.kissTheMortog?.ready && coreOrganization.godori?.ready ? "ok" : "atenção",
            errorCount
        });
    }

    function buildHealthReport(reason) {
        const mount = buildMountReport();
        const storage = buildStorageHealth();
        const bridge = buildCommunicationHealth();
        const safety = buildSafetyReport();
        const cssSafety = buildCssSafetyReport();
        const selfTest = buildSelfTest(reason || "health-report");
        const coreOrganization = buildCoreOrganizationReport();
        return clonePlain({
            schema: "ncc-aaa-health-report-v1",
            version: VERSION,
            reason: reason || "unknown",
            at: isoNow(),
            source: "ncc-aaa",
            status: selfTest.passed ? "ok" : "attention",
            summary: buildHealthSummary(),
            mount,
            identity: clonePlain(identity || {}),
            storage,
            bridge,
            safety,
            cssSafety,
            render: buildRenderReport(),
            coreOrganization,
            baseEntry: {
                contract: buildBaseEntryContract("health-report"),
                readiness: buildBaseEntryReadiness("health-report")
            },
            selfTest,
            errors: bootErrors.concat(lastRenderError ? [lastRenderError] : [])
        });
    }

    function buildBackstageReport(reason) {
        const mount = buildMountReport();
        const storage = buildStorageHealth();
        const bridge = buildCommunicationHealth();
        const safety = buildSafetyReport();
        const cssSafety = buildCssSafetyReport();
        const selfTest = buildSelfTest(reason || "backstage-report");
        const coreOrganization = buildCoreOrganizationReport();
        const baseEntryContract = buildBaseEntryContract("backstage-report");
        const baseEntryReadiness = buildBaseEntryReadiness("backstage-report");
        const errorCount = bootErrors.length + (lastRenderError ? 1 : 0);
        const allOk = selfTest.passed && errorCount === 0;
        return clonePlain({
            schema: "ncc-aaa-backstage-report-v1",
            version: VERSION,
            reason: reason || "unknown",
            at: isoNow(),
            status: allOk ? "ok" : "attention",
            statusLabel: allOk ? "bastidores ok" : "bastidores com avisos",
            moduleLabel: `${GAME_MODULE_REGISTRY.length} módulos de jogos ativos`,
            safetyLabel: safety.automaticGameActionWithoutUserToggle === false ? "ações protegidas" : "atenção",
            cssSafetyLabel: cssSafety.safe ? "CSS seguro" : "CSS com avisos",
            bridgeLabel: bridge.ready ? "bridge pronta" : "bridge atenção",
            mount: {
                ok: !isMountPage(mount.currentPath) || (mount.rootExists && mount.targetFound),
                currentPath: mount.currentPath,
                rootExists: mount.rootExists,
                targetFound: mount.targetFound,
                targetSelector: mount.targetSelector,
                headerPresent: mount.headerPresent,
                sidebarPresent: mount.sidebarPresent,
                footerPresent: mount.footerPresent,
                superfooterPresent: mount.superfooterPresent
            },
            bridge: {
                ready: bridge.ready,
                bridgeName: bridge.bridgeName,
                methodCount: bridge.methodCount,
                globalExternalState: bridge.globalExternalState,
                localStorageExternalState: bridge.localStorageExternalState,
                events: bridge.events,
                missingMethods: bridge.missingMethods
            },
            storage: {
                available: storage.available,
                namespace: storage.namespace,
                hasState: storage.hasState,
                hasVisualPreferences: storage.hasVisualPreferences,
                hasExternalState: storage.hasExternalState,
                hasBaseEntryContract: storage.hasBaseEntryContract,
                hasBaseEntryReadiness: storage.hasBaseEntryReadiness,
                touchesNccBaseKeys: storage.touchesNccBaseKeys,
                touchesNccRpgKeys: storage.touchesNccRpgKeys
            },
            cssSafety: {
                safe: cssSafety.safe,
                themeId: cssSafety.themeId,
                invalidCount: cssSafety.invalidCount,
                stylePolicy: cssSafety.stylePolicy
            },
            render: buildRenderReport(),
            coreOrganization,
            safety: {
                safe: !safety.automaticOfficialAction && safety.automaticGameActionWithoutUserToggle === false && safety.formSubmitWithoutUserToggle === false && (safety.networkAccess === false || String(safety.networkAccess).includes("neoquest2-keyh-xhr-only")),
                moduleCount: safety.moduleCount,
                automaticOfficialAction: safety.automaticOfficialAction,
                automaticGameAction: safety.automaticGameAction,
                clickAutomation: safety.clickAutomation,
                keyboardAutomation: safety.keyboardAutomation,
                formSubmit: safety.formSubmit,
                networkAccess: safety.networkAccess,
                storageDeletion: safety.storageDeletion
            },
            baseEntry: {
                ready: Boolean(baseEntryReadiness.ready),
                contractSchema: baseEntryContract.schema,
                readinessSchema: baseEntryReadiness.schema,
                suggestedCardTitle: baseEntryContract?.suggestedBaseCard?.title || "NCC AAA · Jogos",
                entryPoint: ENTRY_POINT,
                storageKey: EXTERNAL_STATE_KEY
            },
            selfTest,
            errorCount,
            errors: bootErrors.concat(lastRenderError ? [lastRenderError] : [])
        });
    }

    function saveHealthReport(reason) {
        const report = buildHealthReport(reason || "manual-save");
        writeJson(LAST_HEALTH_REPORT_KEY, report);
        writeJson(LAST_DIAGNOSTIC_KEY, buildDiagnostic(false));
        showToast(report.status === "ok" ? "Relatório de saúde salvo." : "Relatório salvo com avisos.");
        return report;
    }

    function buildDiagnostic(persist = true) {
        const externalRaw = readJson(EXTERNAL_STATE_KEY, null);
        return clonePlain({
            schema: DIAGNOSTIC_SCHEMA,
            version: VERSION,
            at: isoNow(),
            foundation: FOUNDATION,
            page: {
                href: location.href,
                pathname: location.pathname,
                expectedPath: MOUNT_PATH,
                legacyPath: LEGACY_MOUNT_PATH,
                supportedPaths: MOUNT_PATHS,
                isMountPage: isMountPage(),
                isPrimaryMountPage: isPrimaryMountPage(),
                isLegacyMountPage: isLegacyMountPage(),
                mounted: Boolean(root),
                targetFound: Boolean(mountedTarget),
                fullWidthMount: document.documentElement.getAttribute("data-ncc-aaa-fullwidth") === "true",
                sidebarHiddenForAaa: Boolean(document.querySelector("[data-ncc-aaa-hidden-sidebar='true']")),
                appId: APP_ID,
                styleId: STYLE_ID
            },
            identity: {
                schema: identity?.schema || IDENTITY_SCHEMA,
                username: identity?.username || "",
                playerId: identity?.playerId || "",
                source: identity?.source || "",
                confidence: identity?.confidence || 0,
                isFallback: Boolean(identity?.isFallback),
                detectedAt: identity?.detectedAt || "",
                candidateCount: Array.isArray(identity?.candidates) ? identity.candidates.length : 0,
                candidates: Array.isArray(identity?.candidates) ? identity.candidates : []
            },
            visual: {
                avatarId: state?.avatarId || "",
                teamId: state?.teamId || "",
                themeId: state?.themeId || "",
                view: state?.view || "",
                shell: "neoquest-icons-audit-v0.1.29"
            },
            cssSafety: buildCssSafetyReport(),
            render: buildRenderReport(),
            coreOrganization: buildCoreOrganizationReport(),
            progress: {
                level: 1,
                xp: 0,
                policy: "pending"
            },
            bridge: {
                window: Boolean(window.NCCAAA),
                unsafeWindow: Boolean(getPublicWindow()?.NCCAAA),
                methods: getBridgeMethodNames(),
                status: buildBridgeStatus(),
                contract: clonePlain(COMMUNICATION_CONTRACT)
            },
            externalState: {
                key: EXTERNAL_STATE_KEY,
                schema: EXTERNAL_STATE_SCHEMA,
                schemaVersion: 1,
                lastPublishedAt,
                storedSchema: externalRaw?.schema || "",
                storedVersion: externalRaw?.version || "",
                storedStateId: externalRaw?.stateId || "",
                storedReason: externalRaw?.reason || ""
            },
            communication: {
                contract: clonePlain(COMMUNICATION_CONTRACT),
                globalBridgeName: GLOBAL_BRIDGE_NAME,
                globalExternalStateName: GLOBAL_EXTERNAL_STATE_NAME,
                globalBaseEntryContractName: GLOBAL_BASE_ENTRY_CONTRACT_NAME,
                globalBaseEntryReadinessName: GLOBAL_BASE_ENTRY_READINESS_NAME,
                events: [EXTERNAL_STATE_EVENT, STATE_EVENT, BASE_ENTRY_EVENT],
                futureConsumer: "NCC Base",
                mirrorOnly: true
            },
            storage: {
                available: storageAvailable(),
                identityKey: IDENTITY_KEY,
                localGuestIdKey: LOCAL_GUEST_ID_KEY,
                playerIdKey: PLAYER_ID_KEY,
                usernameKey: PLAYER_USERNAME_KEY,
                storageKey,
                latestStateKey: LATEST_STATE_KEY,
                visualPreferencesKey,
                storageManifestKey: STORAGE_MANIFEST_KEY,
                storageAuditKey: STORAGE_AUDIT_KEY,
                perUserState: true,
                perUserVisualPreferences: true,
                latestStateCrossAccountFallback: false,
                report: buildStorageReport()
            },
            modules: {
                total: GAME_MODULE_REGISTRY.length,
                migrated: GAME_MODULE_REGISTRY.map(module => module.id),
                active: []
            },
            arena: {
                total: ARENA_MODULE_REGISTRY.length,
                modules: ARENA_MODULE_REGISTRY.map(module => clonePlain(module)),
                trainingState: buildTrainingExternalState("diagnostic")
            },
            safety: {
                automaticOfficialAction: false,
                automaticGameAction: "conditional-user-toggle",
                automaticGameActionWithoutUserToggle: false,
                clickAutomation: "tyranu-evavu-session-toggle-only",
                keyboardAutomation: "neoquest-map-shortcuts-only",
                formSubmit: "tyranu-evavu-session-toggle-only",
                formSubmitWithoutUserToggle: false,
                networkAccess: "kiko-pop-page-jquery-after-click-and-neoquest2-keyh-xhr-only",
                storageDeletion: false
            },
            mountReport: buildMountReport(),
            safetyReport: buildSafetyReport(),
            health: buildHealthReport("diagnostic"),
            selfTest: buildSelfTest("diagnostic"),
            diagnosticTools: {
                lastDiagnosticKey: LAST_DIAGNOSTIC_KEY,
                lastHealthReportKey: LAST_HEALTH_REPORT_KEY,
                lastSelfTestKey: LAST_SELF_TEST_KEY,
                baseEntryContractKey: BASE_ENTRY_CONTRACT_KEY,
                baseEntryReadinessKey: BASE_ENTRY_READINESS_KEY,
                copyDiagnosticAvailable: true,
                copyHealthAvailable: true,
                saveHealthReportAvailable: true,
                copyBaseEntryContractAvailable: true,
                backstageReportAvailable: true
            },
            errors: bootErrors.concat(lastRenderError ? [lastRenderError] : [])
        });
    }

    function copyDiagnostic() {
        const diagnostic = buildDiagnostic();
        writeJson(LAST_DIAGNOSTIC_KEY, diagnostic);
        const text = JSON.stringify(diagnostic, null, 2);
        if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(text).then(() => showToast("Diagnóstico copiado e salvo localmente."), () => showManualCopy(text));
        } else {
            showManualCopy(text);
        }
    }

    function copyHealthReport() {
        const report = buildHealthReport("copy-health");
        writeJson(LAST_HEALTH_REPORT_KEY, report);
        const text = JSON.stringify(report, null, 2);
        if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(text).then(() => showToast("Relatório de saúde copiado e salvo."), () => showManualCopy(text));
        } else {
            showManualCopy(text);
        }
    }

    function copyText(text, successMessage) {
        const safeText = String(text || "");
        if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(safeText).then(
                () => showToast(successMessage || "Texto copiado."),
                () => showManualCopy(safeText)
            );
        } else {
            showManualCopy(safeText);
        }
    }

    function showManualCopy(text) {
        if (!root) return;
        const existing = root.querySelector(".ncc-aaa-copybox");
        if (existing) existing.remove();
        const box = document.createElement("textarea");
        box.className = "ncc-aaa-copybox";
        box.value = text;
        box.setAttribute("readonly", "readonly");
        root.prepend(box);
        box.focus();
        box.select();
        showToast("Selecionei o diagnóstico para cópia manual.");
    }

    function copyBaseEntryContract() {
        const contract = buildBaseEntryContract("copy");
        writeBaseEntryContract("copy");
        writeBaseEntryReadiness("copy");
        copyText(JSON.stringify(contract, null, 2), "Contrato de entrada do NCC Base copiado.");
    }

    function showToast(message) {
        if (!root) return;
        const old = root.querySelector(".ncc-aaa-toast");
        if (old) old.remove();
        const toast = document.createElement("div");
        toast.className = "ncc-aaa-toast";
        toast.textContent = message;
        root.appendChild(toast);
        window.setTimeout(() => toast.remove(), 2200);
    }

    function injectStyles() {
        if (document.getElementById(STYLE_ID)) return;
        const style = document.createElement("style");
        style.id = STYLE_ID;
        style.textContent = `
            .ncc-aaa, .ncc-aaa * { box-sizing: border-box; }
            .ncc-aaa { width: 100%; font-family: Verdana, Arial, Helvetica, sans-serif; color: #172033; }
            .ncc-aaa-shell { --aaa-primary:#42a7dd; --aaa-primary-dark:#154870; --aaa-accent:#ffe58a; --aaa-ink:#0f2d45; --aaa-soft:#dff7ff; width: min(1040px, 100%); margin: 0 auto 24px; padding: 12px; background: radial-gradient(circle at 0% 0%, rgba(255,255,255,.95), rgba(255,255,255,0) 34%), linear-gradient(135deg, #f8fdff, var(--aaa-soft)); border: 2px solid var(--aaa-primary-dark); border-radius: 22px; box-shadow: 0 18px 42px rgba(15, 45, 69, .22); color: var(--aaa-ink); position: relative; overflow: hidden; }
            .ncc-aaa-shell::before { content: ""; position: absolute; inset: -95px auto auto -85px; width: 230px; height: 230px; border-radius: 50%; background: radial-gradient(circle, rgba(255,255,255,.95), rgba(255,255,255,0)); pointer-events: none; }
            .ncc-aaa-shell::after { content: "AAA"; position: absolute; right: 18px; top: 58px; color: var(--aaa-primary-dark); opacity: .045; font-weight: 900; font-size: 96px; letter-spacing: -.08em; pointer-events: none; }
            .ncc-aaa-topbar { position: relative; z-index: 3; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 10px; padding: 8px 14px; border-radius: 999px; border: 2px solid rgba(216,174,66,.88); background: linear-gradient(180deg, #092944, #061b31); box-shadow: inset 0 1px 0 rgba(255,255,255,.18), 0 8px 18px rgba(0,35,66,.18); color: #fff8dd; }
            .ncc-aaa-topbar::before { content: ""; position: absolute; inset: 4px; border-radius: 999px; border: 1px solid rgba(255,255,255,.08); pointer-events: none; }
            .ncc-aaa-top-pill { position: relative; z-index: 1; display: inline-flex; align-items: center; justify-content: center; min-height: 26px; padding: 5px 13px; border-radius: 999px; border: 1px solid rgba(255,248,220,.25); background: linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,.04)); color: #fff8dd; font-size: 11px; line-height: 1; font-weight: 900; letter-spacing: .075em; text-transform: uppercase; text-shadow: 0 1px 1px rgba(0,0,0,.35); box-shadow: inset 0 1px 0 rgba(255,255,255,.12); }
            .ncc-aaa-top-pill-brand { background: linear-gradient(180deg, #fffaf0, #f2dfad); border-color: rgba(216,174,66,.92); color: #08233d; text-shadow: none; }
            .ncc-aaa-brand-icon { width: 18px; height: 18px; border-radius: 5px; object-fit: cover; margin-right: 6px; border: 1px solid rgba(8,35,61,.18); background: #fff; }
            .ncc-aaa-top-pill-version { background: linear-gradient(180deg, #177f83, #0d5967); border-color: rgba(124,234,226,.48); color: #fff8dd; }
            .ncc-aaa-top-pill-path, .ncc-aaa-top-pill-section { background: linear-gradient(180deg, rgba(18,48,86,.92), rgba(9,31,58,.94)); }
            .ncc-aaa-top-pill-gold { background: linear-gradient(180deg, #d7a83a, #9b6f13); border-color: rgba(255,239,170,.72); color: #fff8dd; }
            .ncc-aaa-hero { position: relative; z-index: 1; display: grid; grid-template-columns: 165px minmax(0, 1fr) 145px; grid-template-areas: "character board progress" "player board progress"; gap: 10px 12px; align-items: end; min-height: 300px; padding: 16px; border-radius: 22px; color: #fff; border: 2px solid #f4c542; box-shadow: inset 0 1px 0 rgba(255,255,255,.38), 0 14px 30px rgba(0, 35, 66, .30); overflow: hidden; background: linear-gradient(90deg, rgba(6, 41, 61, .12), rgba(6, 41, 61, .34)), url("https://images.neopets.com/backgrounds/bg_gamesmaster2.gif"), linear-gradient(135deg, #0b5578, #063b5f); background-size: cover; background-position: center top; }
            .ncc-aaa-hero::before { content: ""; position: absolute; inset: 0; background: radial-gradient(circle at 22% 26%, rgba(255,255,255,.16), rgba(255,255,255,0) 38%), linear-gradient(180deg, rgba(0,0,0,.05), rgba(0,28,50,.22)); pointer-events: none; }
            .ncc-aaa-hero::after { content: ""; position: absolute; inset: 11px; border-radius: 17px; border: 1px solid rgba(255, 227, 126, .72); pointer-events: none; box-shadow: inset 0 0 0 1px rgba(255,255,255,.12); }
            .ncc-aaa-stage-character, .ncc-aaa-stage-board, .ncc-aaa-player-ticket, .ncc-aaa-progress-card { position: relative; z-index: 2; }
            .ncc-aaa-stage-character { grid-area: character; align-self: end; justify-self: center; width: 162px; min-height: 185px; display: flex; align-items: flex-end; justify-content: center; margin-bottom: -8px; filter: drop-shadow(0 12px 10px rgba(0,0,0,.33)); }
            .ncc-aaa-stage-character img { max-width: 174px; max-height: 205px; object-fit: contain; display: block; }
            .ncc-aaa-stage-board { grid-area: board; align-self: center; min-height: 216px; display: grid; align-content: center; padding: 17px 18px 16px; border-radius: 17px; background: linear-gradient(180deg, rgba(62, 119, 119, .96), rgba(46, 92, 95, .96)); color: #fff9ea; border: 2px solid #f4c542; box-shadow: inset 0 1px 0 rgba(255,255,255,.26), inset 0 -18px 26px rgba(0,0,0,.10), 0 12px 22px rgba(0,0,0,.22); }
            .ncc-aaa-stage-board-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 3px; }
            .ncc-aaa-stage-board h1 { margin: 0 0 7px; font-size: clamp(27px, 3.5vw, 38px); line-height: .98; color: #fff5d8; letter-spacing: -.045em; text-shadow: 0 2px 0 rgba(0,0,0,.22); }
            .ncc-aaa-event-mark { width: 118px; max-width: 32%; height: auto; object-fit: contain; border-radius: 10px; background: rgba(255,255,255,.86); border: 1px solid rgba(244,197,66,.9); padding: 3px; box-shadow: 0 4px 10px rgba(0,0,0,.15); }
            .ncc-aaa-subtitle { margin: 0; font-size: 12px; line-height: 1.48; max-width: 690px; color: #fff9ea; }
            .ncc-aaa-subtitle strong { color: #fff; }
            .ncc-aaa-kicker { margin: 0 0 6px; font-size: 10px; letter-spacing: .09em; text-transform: uppercase; font-weight: 800; color: var(--aaa-primary-dark); }
            .ncc-aaa-stage-kicker { color: #ffe99c; text-shadow: 0 1px 0 rgba(0,0,0,.25); }
            .ncc-aaa-status-strip { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
            .ncc-aaa-status-strip span, .ncc-aaa-token { display: inline-flex; align-items: center; gap: 4px; padding: 5px 8px; border-radius: 999px; background: var(--aaa-soft); color: var(--aaa-primary-dark); border: 1px solid rgba(0,0,0,.08); font-size: 11px; font-weight: 800; }
            .ncc-aaa-stage-status span { background: rgba(255, 245, 216, .96); color: #063b5f; border-color: rgba(244,197,66,.65); box-shadow: 0 2px 5px rgba(0,0,0,.08); }
            .ncc-aaa-status-strip strong { font-size: 13px; }
            .ncc-aaa-token-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
            .ncc-aaa-hero-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
            .ncc-aaa-stage-actions { margin-top: 12px; }
            .ncc-aaa-player-ticket, .ncc-aaa-progress-card { background: rgba(255,249,234,.95); color: var(--aaa-ink); border-radius: 15px; border: 2px solid rgba(244,197,66,.86); box-shadow: inset 0 1px 0 rgba(255,255,255,.75), 0 8px 16px rgba(0,0,0,.18); padding: 10px; }
            .ncc-aaa-player-ticket { grid-area: player; display: grid; grid-template-columns: 62px 1fr; gap: 9px; align-items: center; align-self: end; }
            .ncc-aaa-player-copy { display: grid; gap: 3px; min-width: 0; }
            .ncc-aaa-player-copy strong { color: var(--aaa-primary-dark); font-size: 15px; line-height: 1.1; word-break: break-word; }
            .ncc-aaa-player-copy span { color: var(--aaa-ink); font-weight: 800; font-size: 11px; }
            .ncc-aaa-player-copy small { color: rgba(0,0,0,.66); font-weight: 800; font-size: 10px; }
            .ncc-aaa-avatar-frame { width: 62px; height: 62px; border-radius: 14px; background: #fff; border: 2px solid var(--aaa-accent); display: grid; place-items: center; overflow: hidden; box-shadow: 0 6px 12px rgba(0,0,0,.18); }
            .ncc-aaa-avatar-frame img { width: 62px; height: 62px; object-fit: cover; display: block; }
            .ncc-aaa-avatar-frame-stage { width: 62px; height: 62px; }
            .ncc-aaa-progress-card { grid-area: progress; text-align: center; display: grid; align-content: center; justify-items: center; align-self: stretch; min-height: 202px; }
            .ncc-aaa-progress-title { display: block; font-size: 12px; color: var(--aaa-primary-dark); margin-bottom: 5px; }
            .ncc-aaa-username { display: block; font-size: 16px; margin-bottom: 8px; color: var(--aaa-primary-dark); word-break: break-word; }
            .ncc-aaa-level-ring { width: 62px; height: 62px; margin: 0 auto 6px; border-radius: 50%; display: grid; place-items: center; background: radial-gradient(circle, #fff 48%, var(--aaa-accent) 49%, var(--aaa-primary) 100%); color: var(--aaa-primary-dark); font-size: 30px; font-weight: 900; border: 2px solid #fff; box-shadow: 0 7px 14px rgba(0,0,0,.18); }
            .ncc-aaa-progress-bar { width: 100%; height: 7px; overflow: hidden; border-radius: 999px; background: rgba(0,0,0,.1); border: 1px solid rgba(0,0,0,.08); margin: 0 auto 6px; }
            .ncc-aaa-progress-bar span { display: block; height: 100%; border-radius: inherit; background: var(--aaa-primary); }
            .ncc-aaa-progress-note { display: block; font-size: 10px; font-weight: 800; }
            .ncc-aaa-quick-stats-card { align-content: center; gap: 8px; }
            .ncc-aaa-quick-medal-grid { width: 100%; display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin: 2px 0 4px; }
            .ncc-aaa-quick-medal { min-height: 54px; display: grid; align-content: center; justify-items: center; border-radius: 14px; border: 1px solid rgba(21,72,112,.14); background: rgba(255,255,255,.78); box-shadow: inset 0 1px 0 rgba(255,255,255,.85); }
            .ncc-aaa-quick-medal strong { display: block; font-size: 24px; line-height: 1; color: #073d63; letter-spacing: -.05em; }
            .ncc-aaa-quick-medal em { display: block; margin-top: 3px; font-style: normal; font-size: 9px; font-weight: 1000; letter-spacing: .05em; text-transform: uppercase; color: rgba(7,61,99,.72); }
            .ncc-aaa-quick-medal.is-bronze { background: linear-gradient(180deg, #fff1df, #f5d2a5); border-color: rgba(166,99,37,.28); }
            .ncc-aaa-quick-medal.is-silver { background: linear-gradient(180deg, #ffffff, #dde7ef); border-color: rgba(98,124,147,.28); }
            .ncc-aaa-quick-medal.is-gold { background: linear-gradient(180deg, #fff9d9, #f4c542); border-color: rgba(180,132,20,.32); }
            .ncc-aaa-quick-mini-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 5px; margin-top: 2px; }
            .ncc-aaa-quick-mini-row span { border-radius: 999px; padding: 4px 6px; background: rgba(255,255,255,.72); border: 1px solid rgba(21,72,112,.12); color: rgba(7,61,99,.82); font-size: 9px; font-weight: 1000; }
            .ncc-aaa-mini-link, .ncc-aaa-mini-button { appearance: none; border: 0; background: transparent; color: var(--aaa-primary-dark); padding: 0; text-align: left; font-size: 10px; font-weight: 800; cursor: pointer; text-decoration: underline; }
            .ncc-aaa-mini-button { margin-top: 2px; }
            .ncc-aaa-tabs, .ncc-aaa-tabs-strip { position: relative; z-index: 2; display: flex; flex-wrap: nowrap; gap: 0; margin: 10px 0 0; padding: 0; border: 2px solid #d8ae42; border-radius: 18px 18px 0 0; overflow: hidden; background: linear-gradient(180deg, #0a2c4c, #061c32); box-shadow: 0 10px 18px rgba(0,28,55,.20), inset 0 1px 0 rgba(255,255,255,.10); }
            .ncc-aaa-tabs::before { content: ""; position: absolute; inset: 0; background: radial-gradient(circle at 18% 0%, rgba(244,197,66,.14), transparent 34%), linear-gradient(90deg, rgba(143,38,55,.18), transparent 20%, transparent 80%, rgba(143,38,55,.16)); pointer-events: none; }
            .ncc-aaa-tab { appearance: none; position: relative; z-index: 1; flex: 1 1 0; min-width: 134px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; border: 0; border-right: 1px solid rgba(255,255,255,.14); border-radius: 0; padding: 14px 18px; background: transparent; color: #fff9df; cursor: pointer; font-weight: 900; font-size: 12px; letter-spacing: .025em; text-transform: uppercase; box-shadow: none; transition: transform .14s ease, background .14s ease, color .14s ease, filter .14s ease; }
            .ncc-aaa-tab:last-child { border-right: 0; }
            .ncc-aaa-tab:hover { transform: translateY(-1px); background: rgba(255,255,255,.08); filter: brightness(1.08); }
            .ncc-aaa-tab.is-active { background: linear-gradient(180deg, #1a7c7a, #0d515f); color: #fffdf0; box-shadow: inset 0 0 0 2px rgba(255,255,255,.12), inset 0 -4px 0 rgba(244,197,66,.40); }
            .ncc-aaa-tab.is-active::after { content: ""; position: absolute; left: 22px; right: 22px; bottom: 4px; height: 3px; border-radius: 999px; background: #f4c542; box-shadow: 0 0 8px rgba(244,197,66,.55); }
            .ncc-aaa-tab-icon { font-size: 15px; line-height: 1; filter: drop-shadow(0 1px 0 rgba(0,0,0,.22)); }
            .ncc-aaa-tab-label { white-space: nowrap; }
            .ncc-aaa-view { position: relative; z-index: 1; min-height: 250px; }
            .ncc-aaa-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 13px; }
            .ncc-aaa-card { background: rgba(255,255,255,.95); border: 1px solid rgba(0,0,0,.12); border-radius: 16px; padding: 14px; box-shadow: 0 7px 18px rgba(0,0,0,.075); }
            .ncc-aaa-card h2, .ncc-aaa-card h3 { color: var(--aaa-primary-dark); margin: 0 0 8px; }
            .ncc-aaa-card p { line-height: 1.5; margin-top: 0; }
            .ncc-aaa-card-wide { grid-column: 1 / -1; }
            .ncc-aaa-dashboard-hub { align-items: stretch; }
            .ncc-aaa-home-stage-card { position: relative; overflow: hidden; display: grid; grid-template-columns: minmax(0, 1fr) 190px; gap: 18px; align-items: center; min-height: 210px; background: linear-gradient(135deg, rgba(255,249,234,.98), rgba(223,246,255,.95)); border: 2px solid rgba(244,197,66,.75); box-shadow: 0 12px 28px rgba(0, 45, 76, .16), inset 0 1px 0 rgba(255,255,255,.85); }
            .ncc-aaa-home-stage-card::before { content: ""; position: absolute; inset: 0; background: radial-gradient(circle at 82% 15%, rgba(244,197,66,.24), transparent 30%), radial-gradient(circle at 6% 100%, rgba(143,38,55,.12), transparent 33%); pointer-events: none; }
            .ncc-aaa-home-stage-card::after { content: "GAMESMASTER HUB"; position: absolute; right: 18px; bottom: 10px; font-size: 34px; line-height: 1; font-weight: 900; letter-spacing: -.04em; color: var(--aaa-primary-dark); opacity: .055; pointer-events: none; }
            .ncc-aaa-home-stage-copy, .ncc-aaa-home-announcer { position: relative; z-index: 1; }
            .ncc-aaa-home-stage-copy h2 { font-size: clamp(25px, 3vw, 34px); margin-bottom: 8px; }
            .ncc-aaa-home-stage-copy p { max-width: 620px; }
            .ncc-aaa-home-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 13px; }
            .ncc-aaa-home-chips span { display: inline-flex; align-items: center; border-radius: 999px; padding: 7px 10px; background: #fff; color: var(--aaa-primary-dark); border: 1px solid rgba(11, 85, 120, .18); font-weight: 900; font-size: 11px; box-shadow: 0 3px 8px rgba(0,0,0,.06); }
            .ncc-aaa-home-chips span::before { content: "✦"; color: var(--aaa-accent); margin-right: 5px; }
            .ncc-aaa-home-announcer { display: grid; gap: 7px; justify-items: center; text-align: center; padding: 12px; border-radius: 18px; background: linear-gradient(180deg, #fff, rgba(255,245,216,.9)); border: 1px solid rgba(184,125,22,.32); box-shadow: inset 0 1px 0 rgba(255,255,255,.9), 0 8px 16px rgba(0,0,0,.10); }
            .ncc-aaa-home-announcer img { max-width: 132px; max-height: 120px; object-fit: contain; filter: drop-shadow(0 8px 8px rgba(0,0,0,.18)); }
            .ncc-aaa-home-announcer strong { color: var(--aaa-primary-dark); font-size: 13px; }
            .ncc-aaa-home-announcer span { font-size: 11px; line-height: 1.35; color: rgba(0,0,0,.68); font-weight: 700; }
            .ncc-aaa-challenge-ribbon { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; padding: 0; overflow: hidden; background: linear-gradient(90deg, #063b5f, #0b5578); border: 2px solid rgba(244,197,66,.72); color: #fff9ea; }
            .ncc-aaa-ribbon-item { display: grid; gap: 3px; justify-items: center; text-align: center; padding: 13px 10px; background: linear-gradient(180deg, rgba(255,255,255,.09), rgba(0,0,0,.06)); }
            .ncc-aaa-ribbon-item + .ncc-aaa-ribbon-item { border-left: 1px solid rgba(255,245,216,.22); }
            .ncc-aaa-ribbon-item strong { font-size: 27px; line-height: 1; color: #ffe99c; text-shadow: 0 2px 0 rgba(0,0,0,.20); }
            .ncc-aaa-ribbon-item span { font-size: 11px; font-weight: 900; text-transform: uppercase; letter-spacing: .04em; }
            .ncc-aaa-ribbon-item small { font-size: 10px; opacity: .84; line-height: 1.25; }
            .ncc-aaa-ribbon-item-safe strong { font-size: 23px; }
            .ncc-aaa-player-summary-card, .ncc-aaa-visual-summary-card { background: linear-gradient(180deg, #fff, rgba(255,249,234,.96)); }
            .ncc-aaa-inline-visual-ticket img { border-color: var(--aaa-accent); box-shadow: 0 7px 14px rgba(0,0,0,.12); }
            .ncc-aaa-next-mission-card { background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,216,.82)); }
            .ncc-aaa-mission-board { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 10px; }
            .ncc-aaa-mission-card { display: grid; gap: 6px; padding: 12px; border-radius: 14px; background: #fff; border: 1px solid rgba(11, 85, 120, .16); box-shadow: inset 0 1px 0 rgba(255,255,255,.9); }
            .ncc-aaa-mission-card strong { color: var(--aaa-primary-dark); font-size: 12px; }
            .ncc-aaa-mission-card span { color: rgba(0,0,0,.70); line-height: 1.4; font-size: 12px; }
            .ncc-aaa-slot-theater { background: linear-gradient(180deg, #fff, rgba(223,246,255,.65)); }
            .ncc-aaa-slot-card-ticket { border-color: rgba(244,197,66,.75); border-width: 2px; }
            .ncc-aaa-backstage-summary { background: linear-gradient(180deg, #fff, rgba(223,246,255,.55)); }
            .ncc-aaa-home-card { display: grid; grid-template-columns: minmax(0, 1fr) minmax(210px, 300px); gap: 14px; align-items: center; }
            .ncc-aaa-home-card h2 { font-size: 24px; }
            .ncc-aaa-home-badges { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px; }
            .ncc-aaa-home-badges span { padding: 9px 10px; border-radius: 13px; background: var(--aaa-soft); color: var(--aaa-primary-dark); border: 1px solid rgba(0,0,0,.08); font-weight: 800; text-align: center; font-size: 11px; }
            .ncc-aaa-quick-board { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 10px; padding: 10px; }
            .ncc-aaa-mini-stat { padding: 11px; border-radius: 14px; background: linear-gradient(180deg, var(--aaa-soft), rgba(255,255,255,.95)); text-align: center; border: 1px solid rgba(0,0,0,.08); }
            .ncc-aaa-mini-stat strong { display: block; font-size: 24px; color: var(--aaa-primary-dark); line-height: 1; }
            .ncc-aaa-mini-stat span { display: block; margin-top: 5px; font-size: 11px; font-weight: 800; }
            .ncc-aaa-mini-stat-safe strong { font-size: 20px; }
            .ncc-aaa-status-card { display: grid; grid-template-columns: minmax(0, 1fr) minmax(250px, 360px); gap: 14px; align-items: center; }
            .ncc-aaa-metrics { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 10px; margin-top: 0; }
            .ncc-aaa-metrics div { padding: 10px; border-radius: 13px; background: var(--aaa-soft); text-align: center; border: 1px solid rgba(0,0,0,.08); }
            .ncc-aaa-metrics strong { display: block; font-size: 25px; color: var(--aaa-primary-dark); }
            .ncc-aaa-metrics span { display: block; font-size: 11px; font-weight: 800; }
            .ncc-aaa-inline-visual { display: grid; grid-template-columns: 74px 1fr; gap: 10px; align-items: center; }
            .ncc-aaa-inline-visual img { width: 74px; height: 74px; object-fit: cover; border-radius: 14px; border: 2px solid var(--aaa-accent); background: #fff; }
            .ncc-aaa-section-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
            .ncc-aaa-section-heading h3, .ncc-aaa-section-heading h2 { margin-bottom: 0; }
            .ncc-aaa-safety-list { display: flex; flex-wrap: wrap; gap: 8px; }
            .ncc-aaa-safety-list span { padding: 7px 9px; border-radius: 999px; background: var(--aaa-soft); color: var(--aaa-primary-dark); font-size: 11px; font-weight: 800; }
            .ncc-aaa-games-intro { overflow: hidden; position: relative; }
            .ncc-aaa-games-intro::after { content: "AAA"; position: absolute; right: 16px; top: 8px; font-size: 48px; font-weight: 900; color: var(--aaa-primary-dark); opacity: .06; pointer-events: none; }
            .ncc-aaa-slot-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px; margin-top: 12px; }
            .ncc-aaa-slot-card { position: relative; display: grid; gap: 5px; padding: 13px; border-radius: 13px; background: var(--aaa-soft); border: 1px dashed var(--aaa-primary-dark); min-height: 108px; }
            .ncc-aaa-slot-card strong { color: var(--aaa-primary-dark); padding-right: 34px; }
            .ncc-aaa-slot-card span { font-size: 11px; font-weight: 800; text-transform: uppercase; }
            .ncc-aaa-slot-card small { color: rgba(0,0,0,.68); line-height: 1.35; }
            .ncc-aaa-slot-card-soft, .ncc-aaa-game-slot { background: linear-gradient(180deg, #fff, var(--aaa-soft)); border-style: solid; box-shadow: inset 0 1px 0 rgba(255,255,255,.8); }
            .ncc-aaa-game-slot { min-height: 122px; }
            .ncc-aaa-slot-pill { justify-self: start; padding: 4px 7px; border-radius: 999px; background: #fff; color: var(--aaa-primary-dark); border: 1px solid rgba(0,0,0,.08); }
            .ncc-aaa-slot-index { position: absolute; right: 10px; top: 10px; opacity: .38; font-size: 18px !important; color: var(--aaa-primary-dark); }

            .ncc-aaa-games-hub { display: grid; gap: 14px; }
            .ncc-aaa-games-stage-panel { position: relative; overflow: hidden; display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 16px; align-items: center; min-height: 165px; background: linear-gradient(135deg, rgba(6,59,95,.98), rgba(61,115,114,.95)), url("https://images.neopets.com/backgrounds/bg_gamesmaster2.gif"); background-size: cover; background-position: center 35%; color: #fff9ea; border: 2px solid rgba(244,197,66,.78); box-shadow: inset 0 1px 0 rgba(255,255,255,.22), 0 12px 25px rgba(0,35,66,.18); }
            .ncc-aaa-games-stage-panel::after { content: ""; position: absolute; inset: 10px; border-radius: 16px; border: 1px solid rgba(255,235,157,.58); pointer-events: none; }
            .ncc-aaa-games-announcer { position: relative; z-index: 1; align-self: end; display: grid; place-items: end center; min-height: 130px; filter: drop-shadow(0 10px 10px rgba(0,0,0,.28)); }
            .ncc-aaa-games-announcer img { max-width: 116px; max-height: 148px; object-fit: contain; display: block; }
            .ncc-aaa-games-stage-copy { position: relative; z-index: 1; max-width: 760px; }
            .ncc-aaa-games-stage-copy .ncc-aaa-kicker { color: #ffe99c; text-shadow: 0 1px 0 rgba(0,0,0,.28); }
            .ncc-aaa-games-stage-copy h2 { color: #fff5d8; text-shadow: 0 2px 0 rgba(0,0,0,.22); font-size: 26px; }
            .ncc-aaa-games-stage-copy p { color: #fff9ea; }
            .ncc-aaa-games-stage-copy strong { color: #fff; }
            .ncc-aaa-games-stage-badges { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 10px; }
            .ncc-aaa-games-stage-badges span { padding: 6px 9px; border-radius: 999px; background: rgba(255,249,234,.95); color: #063b5f; border: 1px solid rgba(244,197,66,.72); font-size: 11px; font-weight: 900; box-shadow: 0 3px 8px rgba(0,0,0,.12); }
            .ncc-aaa-modules-stage-panel { min-height: 178px; }
            .ncc-aaa-modules-active-card, .ncc-aaa-modules-planned-card { background: linear-gradient(180deg, #ffffff, rgba(223,246,255,.50)); }
            .ncc-aaa-modules-section-note { margin: -2px 0 12px; color: rgba(7,61,99,.76); font-size: 12px; }
            .ncc-aaa-module-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 10px; }
            .ncc-aaa-module-card { position: relative; display: grid; gap: 10px; padding: 14px; border-radius: 17px; background: radial-gradient(circle at 100% 100%, rgba(244,197,66,.20), transparent 28%), linear-gradient(180deg, #fff, #e8f9ff); border: 1px solid rgba(21,72,112,.20); box-shadow: inset 0 1px 0 rgba(255,255,255,.9), 0 7px 16px rgba(0,43,75,.09); overflow: hidden; }
            .ncc-aaa-module-card::after { content: ""; position: absolute; inset: 8px; border-radius: 13px; border: 1px dashed rgba(21,72,112,.13); pointer-events: none; }
            .ncc-aaa-module-card-head { position: relative; z-index: 1; display: grid; grid-template-columns: 52px minmax(0, 1fr) auto; gap: 10px; align-items: center; }
            .ncc-aaa-module-icon { width: 50px; height: 50px; display: grid; place-items: center; border-radius: 15px; background: linear-gradient(180deg, #fff, #fff6d9); border: 2px solid rgba(244,197,66,.82); font-size: 25px; box-shadow: 0 4px 11px rgba(0,43,75,.10); }
            .ncc-aaa-module-icon-img { object-fit: contain; padding: 4px; box-sizing: border-box; }
            .ncc-aaa-module-icon-small { width: 42px; height: 42px; font-size: 21px; border-radius: 13px; }
            .ncc-aaa-module-card h3, .ncc-aaa-module-planned-card h3 { margin: 0; color: #073d63; font-size: 17px; line-height: 1.15; }
            .ncc-aaa-module-card .ncc-aaa-kicker { margin: 0 0 3px; }
            .ncc-aaa-module-status { justify-self: end; align-self: start; padding: 5px 8px; border-radius: 999px; background: #dff6ff; color: #073d63; border: 1px solid rgba(21,72,112,.15); font-size: 10px; font-weight: 900; }
            .ncc-aaa-module-card p, .ncc-aaa-module-planned-card p { position: relative; z-index: 1; margin: 0; color: rgba(7,61,99,.80); font-size: 12px; line-height: 1.38; }
            .ncc-aaa-module-meta { position: relative; z-index: 1; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 0; }
            .ncc-aaa-module-meta div { min-width: 0; padding: 7px 8px; border-radius: 11px; background: rgba(255,255,255,.80); border: 1px solid rgba(21,72,112,.12); }
            .ncc-aaa-module-meta dt { color: rgba(7,61,99,.62); font-size: 9px; font-weight: 900; text-transform: uppercase; letter-spacing: .06em; }
            .ncc-aaa-module-meta dd { margin: 2px 0 0; color: #073d63; font-size: 11px; font-weight: 800; overflow-wrap: anywhere; }
            .ncc-aaa-module-tags { position: relative; z-index: 1; display: flex; flex-wrap: wrap; gap: 6px; }
            .ncc-aaa-module-tags span { padding: 5px 7px; border-radius: 999px; background: rgba(255,255,255,.88); color: #154870; border: 1px solid rgba(21,72,112,.14); font-size: 10px; font-weight: 900; }
            .ncc-aaa-module-open { position: relative; z-index: 1; justify-self: start; width: auto; min-width: 150px; color: #fff !important; }
            .ncc-aaa-module-planned-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 10px; margin-top: 10px; }
            .ncc-aaa-module-planned-card { display: grid; grid-template-columns: 48px minmax(0,1fr); gap: 10px; align-items: start; padding: 12px; border-radius: 15px; background: linear-gradient(180deg, #fffdf4, #fff); border: 1px solid rgba(244,197,66,.50); }
            .ncc-aaa-arena-stage-panel { background: linear-gradient(135deg, rgba(54,31,85,.98), rgba(21,72,112,.94)), radial-gradient(circle at 82% 18%, rgba(255,229,138,.34), transparent 33%); border-color: rgba(255,229,138,.82); }
            .ncc-aaa-arena-stage-panel::before { content: "ARENA"; position: absolute; right: 16px; bottom: 8px; color: rgba(255,255,255,.12); font-weight: 900; font-size: 48px; letter-spacing: -.05em; pointer-events: none; }
            .ncc-aaa-arena-announcer img { width: 88px; height: 88px; border-radius: 18px; border: 2px solid rgba(255,229,138,.86); background: #fff; box-shadow: 0 8px 17px rgba(0,0,0,.22); }
            .ncc-aaa-arena-ready-card, .ncc-aaa-arena-slots-card { background: linear-gradient(180deg, #ffffff, rgba(238,233,255,.58)); }
            .ncc-aaa-arena-slot-card { background: linear-gradient(180deg, #fffdf4, #f3efff); border-color: rgba(91,69,166,.22); }
            .ncc-aaa-arena-school-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
            .ncc-aaa-arena-school-card { align-items: flex-start; }
            .ncc-aaa-arena-school-icon { width: 42px; height: 42px; object-fit: contain; border-radius: 12px; background: #fff; border: 1px solid rgba(91,69,166,.18); padding: 4px; flex: 0 0 auto; }
            .ncc-aaa-arena-training-lines { display: grid; gap: 3px; margin-top: 7px; font-size: 11px; color: #31487a; }
            .ncc-aaa-arena-training-lines span { display: inline-flex; width: fit-content; max-width: 100%; border-radius: 999px; padding: 3px 7px; background: rgba(223,246,255,.72); border: 1px solid rgba(66,167,221,.24); font-weight: 700; }
            .ncc-aaa-arena-clean { gap: 12px; }
            .ncc-aaa-arena-clean-hero .ncc-aaa-games-stage-copy p { max-width: 690px; }
            .ncc-aaa-arena-clean-badges span { font-size: 11px; padding: 6px 10px; }
            .ncc-aaa-arena-summary-card { background: linear-gradient(180deg, #ffffff, rgba(223,247,255,.58)); }
            .ncc-aaa-arena-feature-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 10px; }
            .ncc-aaa-arena-feature-card, .ncc-aaa-arena-future-mini-card { display: grid; grid-template-columns: auto 1fr; gap: 10px; align-items: flex-start; border: 1px solid rgba(66,167,221,.26); background: rgba(255,255,255,.74); border-radius: 14px; padding: 12px; box-shadow: inset 0 1px 0 rgba(255,255,255,.78); }
            .ncc-aaa-arena-feature-card h3, .ncc-aaa-arena-future-mini-card h3 { margin: 0 0 4px; color: var(--ncc-aaa-dark); font-size: 14px; }
            .ncc-aaa-arena-feature-card p, .ncc-aaa-arena-future-mini-card p { margin: 0 0 8px; color: rgba(15,45,69,.84); font-size: 12px; line-height: 1.42; }
            .ncc-aaa-arena-feature-icon { width: 38px; height: 38px; display: inline-flex; align-items: center; justify-content: center; border-radius: 12px; background: linear-gradient(180deg, #fff8d4, #fff); border: 1px solid rgba(217,151,42,.34); box-shadow: 0 2px 8px rgba(0,0,0,.08); font-size: 20px; }
            .ncc-aaa-arena-future-card { background: linear-gradient(180deg, #ffffff, rgba(255,253,244,.76)); }
            .ncc-aaa-arena-future-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
            .ncc-aaa-arena-clean-schools { background: linear-gradient(180deg, #ffffff, rgba(255,248,220,.62)); }
            .ncc-aaa-arena-school-card { display: grid; grid-template-columns: auto 1fr; gap: 12px; padding: 14px; background: linear-gradient(180deg, #fff, #fffdf4); }
            .ncc-aaa-arena-school-card h3 { font-size: 15px; margin-bottom: 3px; }
            .ncc-aaa-arena-school-card p { margin-bottom: 8px; }
            .ncc-aaa-arena-school-icon { width: 52px; height: 52px; border-radius: 14px; padding: 6px; border-color: rgba(217,151,42,.32); box-shadow: 0 3px 10px rgba(0,0,0,.1); }
            .ncc-aaa-arena-school-status { display: inline-flex; width: fit-content; border-radius: 999px; padding: 4px 8px; font-size: 11px; font-weight: 900; border: 1px solid rgba(66,167,221,.26); background: rgba(223,247,255,.8); color: #154870; margin-bottom: 6px; }
            .ncc-aaa-arena-school-status.active { background: #e7f9ff; border-color: rgba(66,167,221,.46); }
            .ncc-aaa-arena-school-status.ready { background: #fff0b7; border-color: rgba(217,151,42,.52); color: #533012; }
            .ncc-aaa-arena-school-status.idle { background: #edf4ff; color: #31487a; }
            .ncc-aaa-arena-tech-details { background: linear-gradient(180deg, rgba(255,255,255,.84), rgba(238,233,255,.5)); }
            .ncc-aaa-arena-tech-details summary { cursor: pointer; font-weight: 900; color: var(--ncc-aaa-dark); }
            .ncc-aaa-arena-tech-details p { color: rgba(15,45,69,.78); font-size: 12px; margin: 9px 0; }
            .ncc-aaa-arena-tech-grid { margin: 8px 0 12px; }
            .ncc-aaa-arena-tech-registry { opacity: .92; }
            .ncc-aaa-battledome-foundation-card { background: linear-gradient(180deg, #ffffff, rgba(238,244,255,.72)); border-color: rgba(91,69,166,.2); }
            .ncc-aaa-battledome-foundation-card .ncc-aaa-token { background: #edf4ff; color: #31487a; border-color: rgba(91,69,166,.2); }
            .ncc-aaa-battledome-foundation-grid .ncc-aaa-arena-feature-icon { background: linear-gradient(180deg, #eef4ff, #fff); border-color: rgba(91,69,166,.26); }
            .ncc-aaa-battledome-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
            .ncc-aaa-battledome-safe-note { margin-top: 10px; }
            .ncc-aaa-consumer-arena { gap: 10px; }
            .ncc-aaa-consumer-hero .ncc-aaa-games-stage-copy p { max-width: 560px; }
            .ncc-aaa-consumer-section .ncc-aaa-section-heading { margin-bottom: 4px; }
            .ncc-aaa-consumer-section .ncc-aaa-modules-section-note { margin: 3px 0 8px; font-size: 12px; line-height: 1.35; }
            .ncc-aaa-consumer-grid { gap: 9px; }
            .ncc-aaa-consumer-grid .ncc-aaa-arena-feature-card { min-height: 96px; padding: 10px; }
            .ncc-aaa-consumer-grid .ncc-aaa-arena-feature-card p { margin-bottom: 6px; }
            .ncc-aaa-consumer-actions { margin-top: 10px; gap: 7px; }
            .ncc-aaa-consumer-actions .ncc-aaa-button { min-width: auto; padding-inline: 12px; }
            .ncc-aaa-consumer-detail-grid { display: grid; gap: 8px; margin-top: 10px; }
            .ncc-aaa-consumer-details { border: 1px solid rgba(66,167,221,.22); background: rgba(255,255,255,.74); border-radius: 13px; padding: 0; overflow: hidden; }
            .ncc-aaa-consumer-details > summary { cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; color: var(--ncc-aaa-dark); font-weight: 900; list-style-position: inside; }
            .ncc-aaa-consumer-details > summary span { font-size: 13px; }
            .ncc-aaa-consumer-details > summary small { flex: 0 0 auto; color: #31487a; font-size: 11px; font-weight: 900; }
            .ncc-aaa-consumer-details[open] { background: linear-gradient(180deg, rgba(255,255,255,.88), rgba(248,252,255,.86)); }
            .ncc-aaa-consumer-details[open] > :not(summary) { margin-left: 12px; margin-right: 12px; }
            .ncc-aaa-consumer-details[open] > :last-child { margin-bottom: 12px; }
            .ncc-aaa-consumer-detail-actions { display: flex; flex-wrap: wrap; gap: 7px; margin: 9px 0; }
            .ncc-aaa-consumer-detail-actions .ncc-aaa-button { min-width: auto; padding: 7px 10px; }
            .ncc-aaa-consumer-safe-note { font-size: 11px; line-height: 1.35; padding: 8px 10px; opacity: .9; }
            .ncc-aaa-consumer-future { padding-top: 10px; padding-bottom: 10px; }
            .ncc-aaa-consumer-future-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
            .ncc-aaa-consumer-future .ncc-aaa-arena-future-mini-card { padding: 9px 10px; min-height: 70px; }
            .ncc-aaa-consumer-future .ncc-aaa-arena-future-mini-card p { margin-bottom: 5px; }
            .ncc-aaa-bd-test-panel { margin-top: 12px; padding: 10px; border-radius: 15px; background: linear-gradient(180deg, rgba(238,244,255,.88), rgba(255,255,255,.88)); border: 1px solid rgba(91,69,166,.2); }
            .ncc-aaa-bd-test-panel > summary { cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 10px; color: var(--ncc-aaa-dark); font-weight: 900; }
            .ncc-aaa-bd-test-panel > summary small { color: #31487a; font-size: 11px; font-weight: 900; }
            .ncc-aaa-bd-test-intro { margin: 9px 0; color: rgba(15,45,69,.78); font-size: 12px; font-weight: 800; }
            .ncc-aaa-bd-test-env, .ncc-aaa-bd-test-progress { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
            .ncc-aaa-bd-test-env span, .ncc-aaa-bd-test-progress span { display: inline-flex; gap: 4px; align-items: center; border-radius: 999px; padding: 5px 8px; border: 1px solid rgba(91,69,166,.16); background: rgba(255,255,255,.8); color: #31487a; font-size: 11px; font-weight: 900; }
            .ncc-aaa-bd-test-progress .is-pass { background: #e8fff4; color: #17643d; border-color: rgba(23,100,61,.24); }
            .ncc-aaa-bd-test-progress .is-fail { background: #fff0f0; color: #7d2626; border-color: rgba(125,38,38,.24); }
            .ncc-aaa-bd-test-progress .is-skip { background: #fff9e2; color: #68450d; border-color: rgba(104,69,13,.24); }
            .ncc-aaa-bd-test-sections { display: grid; gap: 9px; margin-top: 10px; }
            .ncc-aaa-bd-test-section { border: 1px solid rgba(91,69,166,.15); background: rgba(255,255,255,.72); border-radius: 14px; padding: 10px; }
            .ncc-aaa-bd-test-section h3 { margin: 0 0 4px; color: var(--ncc-aaa-dark); font-size: 13px; }
            .ncc-aaa-bd-test-section p { margin: 0 0 8px; color: rgba(15,45,69,.72); font-size: 11px; font-weight: 800; }
            .ncc-aaa-bd-test-step-list { display: grid; gap: 7px; }
            .ncc-aaa-bd-test-step { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; border-radius: 12px; border: 1px solid rgba(91,69,166,.12); background: rgba(248,250,255,.82); padding: 8px; }
            .ncc-aaa-bd-test-step strong { display: block; color: var(--ncc-aaa-dark); font-size: 12px; }
            .ncc-aaa-bd-test-step span { display: block; color: rgba(15,45,69,.72); font-size: 11px; font-weight: 800; line-height: 1.35; }
            .ncc-aaa-bd-test-step.is-pass { border-color: rgba(23,100,61,.24); background: rgba(232,255,244,.76); }
            .ncc-aaa-bd-test-step.is-fail { border-color: rgba(125,38,38,.28); background: rgba(255,240,240,.8); }
            .ncc-aaa-bd-test-step.is-skip { border-color: rgba(104,69,13,.26); background: rgba(255,249,226,.82); }
            .ncc-aaa-bd-test-step-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 5px; }
            .ncc-aaa-bd-test-step-button { appearance: none; border: 1px solid rgba(91,69,166,.16); border-radius: 999px; background: #fff; color: #31487a; padding: 5px 7px; font: 900 10px Arial, sans-serif; cursor: pointer; }
            .ncc-aaa-bd-test-step-button.is-pass { color: #17643d; border-color: rgba(23,100,61,.24); }
            .ncc-aaa-bd-test-step-button.is-fail { color: #7d2626; border-color: rgba(125,38,38,.24); }
            .ncc-aaa-bd-test-step-button.is-skip { color: #68450d; border-color: rgba(104,69,13,.24); }
            .ncc-aaa-bd-test-actions { margin-top: 10px; }
            .ncc-aaa-bd-local-summary { margin-top: 12px; padding: 10px; border-radius: 14px; background: rgba(238,244,255,.78); border: 1px solid rgba(91,69,166,.18); }
            .ncc-aaa-bd-local-summary.is-empty { color: #31487a; font-weight: 800; font-size: 12px; }
            .ncc-aaa-bd-local-summary-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; color: #31487a; font-size: 12px; }
            .ncc-aaa-bd-local-summary-head strong { color: var(--ncc-aaa-dark); }
            .ncc-aaa-bd-local-summary-note { color: rgba(15,45,69,.76); font-size: 12px; font-weight: 700; }
            .ncc-aaa-bd-favorite-strip { display: flex; flex-wrap: wrap; gap: 6px; }
            .ncc-aaa-bd-favorite-chip { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; border: 1px solid rgba(217,151,42,.45); background: #fff8d8; color: #3b2a00; border-radius: 999px; padding: 4px 8px; font-size: 11px; font-weight: 900; }
            .ncc-aaa-bd-favorite-chip img, .ncc-aaa-bd-favorite-thumb-fallback { width: 22px; height: 22px; object-fit: cover; border-radius: 999px; background: white; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
            .ncc-aaa-bd-prize-summary { margin-top: 12px; padding: 10px; border-radius: 14px; background: rgba(255,249,234,.86); border: 1px solid rgba(217,151,42,.22); }
            .ncc-aaa-bd-prize-summary.is-paused { background: rgba(255,243,221,.9); border-color: rgba(190,111,36,.28); }
            .ncc-aaa-bd-prize-controls { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 8px 0; }
            .ncc-aaa-bd-prize-controls label { display: grid; gap: 4px; color: #31487a; font-size: 11px; font-weight: 900; }
            .ncc-aaa-bd-prize-controls select, .ncc-aaa-bd-prize-controls input { width: 100%; border: 1px solid rgba(91,69,166,.22); border-radius: 10px; background: rgba(255,255,255,.92); color: var(--ncc-aaa-dark); padding: 7px 8px; font: 800 11px Arial, sans-serif; }
            .ncc-aaa-bd-prize-log { max-height: 176px; overflow: auto; white-space: pre-wrap; margin: 8px 0; padding: 10px; border-radius: 12px; border: 1px solid rgba(91,69,166,.18); background: #f8f6ff; color: #231a4a; font: 700 11px/1.4 Consolas, Monaco, monospace; }
            .ncc-aaa-bd-loadout-summary { margin-top: 12px; padding: 10px; border-radius: 14px; background: rgba(255,250,238,.88); border: 1px solid rgba(217,151,42,.24); }
            .ncc-aaa-bd-loadout-summary.is-disabled { background: rgba(244,247,255,.86); border-color: rgba(91,69,166,.18); }
            .ncc-aaa-bd-loadout-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 7px; margin: 8px 0; }
            .ncc-aaa-bd-loadout-chip { display: inline-flex; align-items: center; gap: 7px; border: 1px solid rgba(217,151,42,.42); background: #fff8d8; color: #3b2a00; border-radius: 12px; padding: 7px 8px; font-size: 11px; font-weight: 900; min-width: 0; }
            .ncc-aaa-bd-loadout-chip img { width: 30px; height: 30px; object-fit: contain; border-radius: 6px; background: white; flex: 0 0 auto; }
            .ncc-aaa-bd-loadout-chip span { min-width: 0; overflow-wrap: anywhere; }
            .ncc-aaa-bd-advanced { margin-top: 8px; }
            .ncc-aaa-bd-advanced summary { cursor: pointer; color: var(--ncc-aaa-dark); font-size: 12px; font-weight: 900; }
            .ncc-aaa-bd-advanced p { margin: 7px 0; color: rgba(15,45,69,.72); font-size: 11px; font-weight: 800; }
            .ncc-aaa-module-links { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px; }
            .ncc-aaa-module-links a { display: inline-flex; align-items: center; justify-content: center; border-radius: 999px; padding: 5px 9px; background: #eef7ff; color: #154870; border: 1px solid rgba(66,167,221,.35); font-weight: 800; text-decoration: none; }
            .ncc-aaa-modules-rulebook { background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(223,246,255,.72)); }
            .ncc-aaa-challenge-gallery-card, .ncc-aaa-support-gallery-card { background: linear-gradient(180deg, #fff9ea, #fff); border-color: rgba(244,197,66,.62); }
            .ncc-aaa-challenge-gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 12px; }
            .ncc-aaa-support-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin-top: 12px; }
            .ncc-aaa-challenge-card { position: relative; min-height: 178px; display: grid; grid-template-columns: 58px minmax(0, 1fr); grid-template-rows: auto auto; gap: 8px 11px; padding: 14px; border-radius: 18px; overflow: hidden; background: radial-gradient(circle at 0 0, rgba(255,229,138,.52), rgba(255,229,138,0) 38%), linear-gradient(180deg, #fff, #dff6ff); border: 2px solid rgba(21,72,112,.24); box-shadow: inset 0 1px 0 rgba(255,255,255,.85), 0 7px 16px rgba(0,43,75,.10); }
            .ncc-aaa-challenge-card::before { content: ""; position: absolute; inset: 8px; border-radius: 14px; border: 1px dashed rgba(21,72,112,.18); pointer-events: none; }
            .ncc-aaa-challenge-card.is-primary { border-color: rgba(244,197,66,.82); background: radial-gradient(circle at 0 0, rgba(244,197,66,.62), rgba(244,197,66,0) 40%), linear-gradient(180deg, #fff9ea, #dff6ff); }
            .ncc-aaa-challenge-card.is-support { min-height: 150px; }
            .ncc-aaa-challenge-number { position: absolute; right: 12px; top: 9px; color: #154870; opacity: .18; font-size: 28px; font-weight: 900; letter-spacing: -.05em; }
            .ncc-aaa-challenge-icon { position: relative; z-index: 1; width: 54px; height: 54px; border-radius: 16px; display: grid; place-items: center; background: linear-gradient(180deg, #fff, #fff5d8); border: 2px solid rgba(244,197,66,.82); font-size: 26px; box-shadow: 0 5px 12px rgba(0,43,75,.12); }
            .ncc-aaa-challenge-main { position: relative; z-index: 1; min-width: 0; }
            .ncc-aaa-challenge-tier { display: inline-flex; padding: 3px 7px; border-radius: 999px; background: #154870; color: #fff; text-transform: uppercase; font-size: 9px; font-weight: 900; letter-spacing: .08em; margin-bottom: 5px; }
            .ncc-aaa-challenge-main h3 { margin: 0 0 5px; color: #073d63; font-size: 17px; padding-right: 26px; }
            .ncc-aaa-challenge-main p { margin: 0; font-size: 11px; line-height: 1.35; color: rgba(7,61,99,.82); }
            .ncc-aaa-challenge-meta { position: relative; z-index: 1; grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 6px; align-self: end; }
            .ncc-aaa-challenge-meta span { padding: 5px 7px; border-radius: 999px; background: rgba(255,255,255,.82); color: #154870; border: 1px solid rgba(21,72,112,.14); font-size: 10px; font-weight: 900; text-transform: none; }
            .ncc-aaa-challenge-card small { position: relative; z-index: 1; grid-column: 1 / -1; color: rgba(0,0,0,.62); font-weight: 800; font-size: 10px; }
            .ncc-aaa-games-rulebook { background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(223,246,255,.72)); }
            .ncc-aaa-rulebook-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 8px; }
            .ncc-aaa-rulebook-grid span { padding: 9px 10px; border-radius: 13px; background: var(--aaa-soft); color: var(--aaa-primary-dark); border: 1px solid rgba(0,0,0,.08); font-weight: 900; text-align: center; font-size: 11px; }
            .ncc-aaa-profile-view-polished { grid-template-columns: 220px minmax(0, 1fr); align-items: start; }
            .ncc-aaa-profile-preview { text-align: center; position: sticky; top: 8px; }
            .ncc-aaa-profile-preview-frame { width: 118px; height: 118px; margin: 0 auto 10px; border-radius: 22px; background: #fff; border: 2px solid var(--aaa-accent); display: grid; place-items: center; overflow: hidden; box-shadow: 0 8px 16px rgba(0,0,0,.13); }
            .ncc-aaa-profile-preview-frame img { width: 118px; height: 118px; object-fit: cover; }
            .ncc-aaa-avatar-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(116px, 1fr)); gap: 10px; }
            .ncc-aaa-avatar-option { appearance: none; display: grid; gap: 7px; justify-items: center; padding: 10px; border-radius: 13px; border: 1px solid rgba(0,0,0,.12); background: #fff; color: var(--aaa-primary-dark); cursor: pointer; font-weight: 800; font-size: 11px; }
            .ncc-aaa-avatar-option img { width: 72px; height: 72px; object-fit: cover; border-radius: 11px; border: 1px solid rgba(0,0,0,.12); }
            .ncc-aaa-avatar-option:hover, .ncc-aaa-avatar-option.is-selected { border-color: var(--aaa-primary-dark); background: var(--aaa-soft); box-shadow: 0 0 0 2px var(--aaa-accent); }
            .ncc-aaa-label { display: block; font-weight: 800; margin-bottom: 6px; color: var(--aaa-primary-dark); }
            .ncc-aaa-select { width: 100%; padding: 8px; border-radius: 10px; border: 1px solid rgba(0,0,0,.18); background: #fff; color: var(--aaa-ink); }
            .ncc-aaa-button { appearance: none; border: 1px solid var(--aaa-primary-dark); background: var(--aaa-primary-dark); color: #fff; border-radius: 999px; padding: 8px 12px; cursor: pointer; font-weight: 800; font-size: 12px; margin-right: 0; box-shadow: 0 4px 10px rgba(0,0,0,.10); }
            .ncc-aaa-button:hover { filter: brightness(1.08); }
            .ncc-aaa-button-muted { background: #fff; color: var(--aaa-primary-dark); }
            .ncc-aaa-backstage-view { display: grid; gap: 14px; }
            .ncc-aaa-backstage-hero { position: relative; overflow: hidden; min-height: 168px; display: grid; grid-template-columns: 145px minmax(0, 1fr); align-items: center; gap: 16px; background: radial-gradient(circle at 12% 18%, rgba(244,197,66,.34), transparent 28%), linear-gradient(135deg, #063b5f, #0b5578 48%, #285758); color: #fff9ea; border: 2px solid rgba(244,197,66,.78); box-shadow: 0 14px 32px rgba(0,35,66,.28); }
            .ncc-aaa-backstage-hero::after { content: ""; position: absolute; inset: 10px; border-radius: 17px; border: 1px solid rgba(255,235,157,.55); pointer-events: none; }
            .ncc-aaa-backstage-host { position: relative; z-index: 1; display: grid; place-items: end center; align-self: stretch; filter: drop-shadow(0 10px 10px rgba(0,0,0,.24)); }
            .ncc-aaa-backstage-host img { max-width: 128px; max-height: 154px; object-fit: contain; align-self: end; }
            .ncc-aaa-backstage-copy { position: relative; z-index: 1; max-width: 780px; }
            .ncc-aaa-backstage-copy .ncc-aaa-kicker { color: #ffe99c; text-shadow: 0 1px 0 rgba(0,0,0,.25); }
            .ncc-aaa-backstage-copy h2 { margin: 0 0 7px; color: #fff5d8; text-shadow: 0 2px 0 rgba(0,0,0,.2); font-size: 27px; }
            .ncc-aaa-backstage-copy p { color: #fff9ea; margin-bottom: 10px; }
            .ncc-aaa-backstage-badges { display: flex; flex-wrap: wrap; gap: 7px; }
            .ncc-aaa-backstage-badges span { padding: 6px 9px; border-radius: 999px; background: rgba(255,249,234,.95); color: #063b5f; border: 1px solid rgba(244,197,66,.72); font-size: 11px; font-weight: 900; box-shadow: 0 3px 8px rgba(0,0,0,.12); }
            .ncc-aaa-backstage-health { background: linear-gradient(180deg, #fff9ea, #fff); border-color: rgba(244,197,66,.62); }
            .ncc-aaa-backstage-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
            .ncc-aaa-backstage-panel { position: relative; overflow: hidden; border-radius: 18px; padding: 13px; background: linear-gradient(180deg, #fff, #dff6ff); border: 2px solid rgba(21,72,112,.22); box-shadow: inset 0 1px 0 rgba(255,255,255,.8), 0 7px 16px rgba(0,43,75,.10); }
            .ncc-aaa-backstage-panel::before { content: ""; position: absolute; inset: 8px; border-radius: 14px; border: 1px dashed rgba(21,72,112,.16); pointer-events: none; }
            .ncc-aaa-backstage-panel.is-ok { border-color: rgba(68,145,118,.42); }
            .ncc-aaa-backstage-panel.is-warn { border-color: rgba(184,125,22,.72); background: linear-gradient(180deg, #fff9ea, #fff); }
            .ncc-aaa-backstage-panel-head { position: relative; z-index: 1; display: flex; align-items: center; gap: 9px; margin-bottom: 10px; }
            .ncc-aaa-backstage-icon { width: 42px; height: 42px; border-radius: 14px; display: grid; place-items: center; background: linear-gradient(180deg, #fff, #fff5d8); border: 2px solid rgba(244,197,66,.78); font-size: 21px; box-shadow: 0 5px 12px rgba(0,43,75,.10); }
            .ncc-aaa-backstage-panel h3 { margin: 0; color: #073d63; font-size: 16px; }
            .ncc-aaa-backstage-panel small { display: inline-flex; margin-top: 3px; padding: 3px 7px; border-radius: 999px; background: #154870; color: #fff; text-transform: uppercase; font-size: 9px; font-weight: 900; letter-spacing: .08em; }
            .ncc-aaa-backstage-list { position: relative; z-index: 1; display: grid; gap: 6px; margin: 0; }
            .ncc-aaa-backstage-list div { display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: 8px; align-items: baseline; padding: 7px 8px; border-radius: 11px; background: rgba(255,255,255,.76); border: 1px solid rgba(21,72,112,.10); }
            .ncc-aaa-backstage-list dt { margin: 0; font-size: 10px; color: rgba(7,61,99,.68); font-weight: 900; text-transform: uppercase; letter-spacing: .04em; }
            .ncc-aaa-backstage-list dd { margin: 0; min-width: 0; color: #073d63; font-weight: 800; font-size: 11px; overflow-wrap: anywhere; }
            .ncc-aaa-backstage-json { background: linear-gradient(180deg, #fff9ea, #fff); border-color: rgba(21,72,112,.20); }
            .ncc-aaa-backstage-json summary { cursor: pointer; font-weight: 900; color: #073d63; font-size: 14px; }
            .ncc-aaa-backstage-json p { margin: 8px 0 10px; color: rgba(7,61,99,.76); }
            .ncc-aaa-diagnostic-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 12px; }
            .ncc-aaa-health-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 9px; margin: 10px 0 12px; }
            .ncc-aaa-health-card { display: grid; gap: 3px; padding: 10px; border-radius: 12px; background: var(--aaa-soft); border: 1px solid rgba(0,0,0,.1); }
            .ncc-aaa-health-card span { font-size: 10px; font-weight: 800; text-transform: uppercase; color: var(--aaa-primary-dark); letter-spacing: .06em; }
            .ncc-aaa-health-card strong { font-size: 18px; color: var(--aaa-primary-dark); }
            .ncc-aaa-health-card small { font-size: 10px; color: rgba(0,0,0,.62); line-height: 1.35; }
            .ncc-aaa-diagnostic { max-height: 440px; overflow: auto; margin: 0; padding: 12px; border-radius: 12px; background: #101827; color: #e8f1ff; font-size: 11px; line-height: 1.45; white-space: pre-wrap; }
            .ncc-aaa-copybox { width: 100%; min-height: 140px; margin-bottom: 12px; padding: 10px; border-radius: 10px; border: 1px solid var(--aaa-primary-dark); font-family: monospace; font-size: 11px; }
            .ncc-aaa-toast { position: fixed; right: 18px; bottom: 18px; z-index: 100000; background: var(--aaa-primary-dark); color: #fff; border-radius: 999px; padding: 10px 14px; box-shadow: 0 10px 25px rgba(0,0,0,.25); font-weight: 800; }
            .ncc-aaa code { background: rgba(0,0,0,.07); border-radius: 5px; padding: 1px 4px; }
            .ncc-aaa-shell::before { content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .38; background: radial-gradient(circle at 8% 6%, rgba(244,197,66,.22), transparent 18%), radial-gradient(circle at 94% 16%, rgba(255,245,216,.18), transparent 22%), linear-gradient(180deg, rgba(255,255,255,.08), transparent 180px); }
            .ncc-aaa-shell::after { content: "AAA"; position: absolute; right: 18px; bottom: 10px; pointer-events: none; font-size: 86px; line-height: 1; font-weight: 900; letter-spacing: -.08em; color: rgba(6,59,95,.045); }
            .ncc-aaa-stage-board { box-shadow: inset 0 1px 0 rgba(255,255,255,.8), 0 12px 30px rgba(0,25,44,.28); }
            .ncc-aaa-stage-board::before { content: ""; position: absolute; inset: 9px; border-radius: 17px; border: 1px solid rgba(244,197,66,.35); pointer-events: none; }
            .ncc-aaa-stage-board > * { position: relative; z-index: 1; }
            .ncc-aaa-card { box-shadow: inset 0 1px 0 rgba(255,255,255,.84), 0 8px 18px rgba(0,43,75,.10); }
            .ncc-aaa-card:not(.ncc-aaa-stage-board):not(.ncc-aaa-games-stage-panel):not(.ncc-aaa-backstage-hero)::before { content: ""; position: absolute; inset: 7px; border-radius: 13px; border: 1px solid rgba(244,197,66,.16); pointer-events: none; }
            .ncc-aaa-button { background: linear-gradient(180deg, #1a6287, #073d63); border-color: rgba(255,245,216,.45); box-shadow: inset 0 1px 0 rgba(255,255,255,.32), 0 5px 12px rgba(0,35,66,.20); }
            .ncc-aaa-button-muted { background: linear-gradient(180deg, #fff9ea, #ffffff); color: #073d63; border-color: rgba(21,72,112,.34); }
            .ncc-aaa-token, .ncc-aaa-badge, .ncc-aaa-home-chips span, .ncc-aaa-games-stage-badges span, .ncc-aaa-backstage-badges span { box-shadow: inset 0 1px 0 rgba(255,255,255,.8), 0 3px 7px rgba(0,43,75,.08); }
            .ncc-aaa-player-pass { overflow: hidden; background: radial-gradient(circle at 50% 0%, rgba(244,197,66,.30), transparent 35%), linear-gradient(180deg, #fff9ea, #ffffff); border-color: rgba(244,197,66,.72); }
            .ncc-aaa-player-pass::after { content: ""; position: absolute; left: -28px; right: -28px; bottom: -30px; height: 70px; background: linear-gradient(90deg, rgba(6,59,95,.06), rgba(244,197,66,.12), rgba(6,59,95,.06)); transform: rotate(-3deg); pointer-events: none; }
            .ncc-aaa-player-pass small { position: relative; z-index: 1; display: block; margin-top: 9px; color: rgba(7,61,99,.64); font-weight: 800; }
            .ncc-aaa-profile-avatar-name { font-weight: 900; color: #073d63; }
            .ncc-aaa-profile-team { display: inline-flex; justify-content: center; align-items: center; gap: 4px; padding: 5px 9px; border-radius: 999px; background: #dff6ff; border: 1px solid rgba(21,72,112,.20); font-weight: 900; color: #073d63; }
            .ncc-aaa-profile-preview-frame { width: 132px; height: 132px; border-radius: 26px; background: linear-gradient(180deg, #fff, #fff5d8); border: 3px solid rgba(244,197,66,.86); box-shadow: inset 0 1px 0 rgba(255,255,255,.9), 0 10px 22px rgba(0,43,75,.18); }
            .ncc-aaa-profile-preview-frame img { width: 132px; height: 132px; }
            .ncc-aaa-avatar-option { position: relative; overflow: hidden; background: linear-gradient(180deg, #fff, #fff9ea); border-color: rgba(21,72,112,.18); box-shadow: 0 5px 12px rgba(0,43,75,.07); }
            .ncc-aaa-avatar-option::before { content: ""; position: absolute; inset: 5px; border-radius: 10px; border: 1px solid rgba(244,197,66,.14); pointer-events: none; }
            .ncc-aaa-avatar-option.is-selected { background: linear-gradient(180deg, #dff6ff, #fff9ea); border-color: #154870; box-shadow: 0 0 0 2px #f4c542, 0 8px 16px rgba(0,43,75,.12); }
            .ncc-aaa-challenge-card { box-shadow: inset 0 1px 0 rgba(255,255,255,.82), 0 9px 18px rgba(0,43,75,.11); }
            .ncc-aaa-challenge-card::after { content: ""; position: absolute; right: -18px; bottom: -22px; width: 74px; height: 74px; border-radius: 999px; background: rgba(244,197,66,.10); pointer-events: none; }
            .ncc-aaa-backstage-panel { box-shadow: inset 0 1px 0 rgba(255,255,255,.84), 0 8px 18px rgba(0,43,75,.10); }


            /* NCC AAA v0.1.14 Step 3 · Implementable Stage Hero */
            .ncc-aaa-hero.ncc-aaa-stage-layout {
                position: relative;
                z-index: 1;
                display: grid;
                grid-template-columns: 218px minmax(0, 1fr) 188px;
                grid-template-areas: "player board progress";
                gap: 20px;
                align-items: end;
                min-height: 388px;
                padding: 118px 42px 28px 132px;
                border-radius: 24px;
                border: 2px solid #d8ae42;
                overflow: hidden;
                color: #fff9df;
                background-image:
                    linear-gradient(90deg, rgba(4, 24, 45, .82), rgba(7, 68, 90, .62), rgba(4, 24, 45, .82)),
                    url("https://images.neopets.com/backgrounds/bg_gamesmaster2.gif"),
                    linear-gradient(135deg, #0b5578, #063b5f);
                background-size: cover;
                background-position: center top;
                box-shadow: 0 14px 34px rgba(0, 32, 58, .35), inset 0 0 0 1px rgba(255, 245, 205, .24);
            }
            .ncc-aaa-hero.ncc-aaa-stage-layout::before {
                content: "";
                position: absolute;
                inset: 0;
                pointer-events: none;
                background:
                    radial-gradient(circle at 28% 18%, rgba(255,255,255,.18), transparent 31%),
                    linear-gradient(180deg, rgba(0,0,0,.02), rgba(0,26,48,.30));
            }
            .ncc-aaa-hero.ncc-aaa-stage-layout::after {
                content: "";
                position: absolute;
                inset: 12px;
                border-radius: 19px;
                border: 1px solid rgba(255, 233, 156, .72);
                box-shadow: inset 0 0 0 1px rgba(255,255,255,.10);
                pointer-events: none;
            }
            .ncc-aaa-stage-host {
                position: absolute;
                left: 2px;
                bottom: 32px;
                width: 150px;
                max-height: 292px;
                object-fit: contain;
                z-index: 3;
                filter: drop-shadow(0 10px 12px rgba(0,0,0,.48));
                pointer-events: none;
            }
            .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2,
            .ncc-aaa-stage-layout .ncc-aaa-stage-board-main,
            .ncc-aaa-stage-layout .ncc-aaa-progress-panel-stage2 {
                position: relative;
                z-index: 4;
            }
            .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2 {
                grid-area: player;
                align-self: end;
                display: grid;
                justify-items: center;
                text-align: center;
                min-height: 286px;
                padding: 18px 16px 16px;
                border-radius: 20px;
                border: 2px solid #d8ae42;
                background: linear-gradient(180deg, rgba(255,251,236,.97), rgba(255,239,200,.95));
                color: #073d63;
                box-shadow: 0 10px 24px rgba(0,26,50,.32), inset 0 0 0 2px rgba(255,255,255,.52);
                overflow: visible;
            }
            .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2::before,
            .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2::after {
                content: "";
                position: absolute;
                top: 50%;
                width: 24px;
                height: 24px;
                border-radius: 50%;
                background: rgba(7, 38, 61, .88);
                transform: translateY(-50%);
                pointer-events: none;
            }
            .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2::before { left: -13px; }
            .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2::after { right: -13px; }
            .ncc-aaa-pass-title {
                width: calc(100% + 10px);
                margin: -6px 0 13px;
                padding: 7px 10px;
                border-radius: 999px;
                background: linear-gradient(180deg, #0f5770, #073d63);
                color: #fff8dd;
                font-size: 10px;
                line-height: 1;
                letter-spacing: .08em;
                text-transform: uppercase;
                font-weight: 900;
                box-shadow: inset 0 1px 0 rgba(255,255,255,.26);
            }
            .ncc-aaa-pass-avatar-wrap {
                width: 104px;
                height: 104px;
                display: grid;
                place-items: center;
                padding: 5px;
                margin: 0 auto 11px;
                border-radius: 22px;
                border: 3px solid rgba(216,174,66,.90);
                background: linear-gradient(180deg, #fff, #fff5d8);
                box-shadow: inset 0 1px 0 rgba(255,255,255,.9), 0 8px 16px rgba(0,43,75,.18);
                overflow: hidden;
            }
            .ncc-aaa-pass-avatar-wrap img { width: 94px; height: 94px; object-fit: cover; display: block; border-radius: 16px; }
            .ncc-aaa-pass-name { max-width: 100%; color: #073d63; font-size: 18px; line-height: 1.05; word-break: break-word; }
            .ncc-aaa-pass-avatar-name { margin-top: 5px; color: rgba(7,61,99,.82); font-weight: 900; font-size: 12px; }
            .ncc-aaa-pass-team { margin-top: 8px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; padding: 5px 10px; border-radius: 999px; background: #dff6ff; border: 1px solid rgba(21,72,112,.22); color: #073d63; font-weight: 900; font-size: 11px; }
            .ncc-aaa-pass-link { appearance: none; border: 0; margin-top: 11px; padding: 0; background: transparent; color: #0f5770; font-weight: 900; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
            .ncc-aaa-pass-avatar-wrap { position: relative; overflow: visible; }
            .ncc-aaa-avatar-edit-button { position: absolute; top: -9px; right: -9px; width: 29px; height: 29px; display: inline-grid; place-items: center; border-radius: 999px; border: 2px solid #f4c542; background: linear-gradient(180deg, #fffbe7, #ffd978); color: #073d63; font-weight: 1000; font-size: 16px; line-height: 1; cursor: pointer; box-shadow: 0 5px 12px rgba(0,43,75,.24), inset 0 1px 0 rgba(255,255,255,.85); z-index: 4; }
            .ncc-aaa-avatar-edit-button:hover, .ncc-aaa-avatar-edit-button:focus-visible { transform: translateY(-1px); box-shadow: 0 0 0 3px rgba(66,167,221,.25), 0 7px 14px rgba(0,43,75,.25); outline: none; }
            .ncc-aaa-home-visual-panel { display: grid; grid-template-columns: minmax(0, 1fr) minmax(260px, .85fr); gap: 16px; align-items: center; }
            .ncc-aaa-home-avatar-edit-wrap { position: relative; width: 78px; height: 78px; flex: 0 0 auto; }
            .ncc-aaa-home-avatar-edit-wrap img { width: 78px; height: 78px; border-radius: 18px; object-fit: cover; border: 2px solid rgba(244,197,66,.86); background: #fff; box-shadow: 0 7px 14px rgba(0,43,75,.16); }
            .ncc-aaa-avatar-edit-button-mini { top: -7px; right: -7px; width: 26px; height: 26px; font-size: 14px; }
            .ncc-aaa-home-visual-controls { display: grid; grid-template-columns: 1fr; gap: 6px; }
            .ncc-aaa-home-visual-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
            .ncc-aaa-avatar-modal[hidden] { display: none !important; }
            .ncc-aaa-avatar-modal { position: fixed; inset: 0; z-index: 2147483000; display: grid; place-items: center; padding: 24px; }
            .ncc-aaa-avatar-modal-backdrop { position: fixed; inset: 0; border: 0; background: rgba(3, 24, 42, .68); backdrop-filter: blur(3px); cursor: pointer; }
            .ncc-aaa-avatar-modal-panel { position: relative; z-index: 2; width: min(780px, calc(100vw - 28px)); max-height: min(720px, calc(100vh - 36px)); overflow: auto; border-radius: 22px; border: 3px solid rgba(244,197,66,.92); background: linear-gradient(180deg, #f7fdff, #fff8df); box-shadow: 0 24px 60px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.9); padding: 18px; color: #073d63; }
            .ncc-aaa-avatar-modal-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; margin-bottom: 14px; }
            .ncc-aaa-avatar-modal-head h2 { margin: 2px 0 4px; color: #073d63; font-size: clamp(24px, 4vw, 38px); line-height: .95; }
            .ncc-aaa-avatar-modal-head p { margin: 0; max-width: 560px; color: rgba(7,61,99,.78); font-weight: 700; }
            .ncc-aaa-avatar-modal-close { appearance: none; width: 38px; height: 38px; flex: 0 0 auto; border-radius: 999px; border: 2px solid rgba(21,72,112,.2); background: #fff; color: #073d63; font-size: 26px; font-weight: 900; line-height: 1; cursor: pointer; box-shadow: 0 4px 10px rgba(0,43,75,.12); }
            .ncc-aaa-avatar-modal-close:hover, .ncc-aaa-avatar-modal-close:focus-visible { border-color: #f4c542; box-shadow: 0 0 0 3px rgba(244,197,66,.22); outline: none; }
            .ncc-aaa-avatar-grid-modal { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); }
            .ncc-aaa-trophy-hero { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 18px; align-items: center; }
            .ncc-aaa-trophy-hero .ncc-aaa-games-announcer { min-height: 100px; display: grid; place-items: center; }
            .ncc-aaa-trophy-hero .ncc-aaa-games-announcer img { width: 96px; height: 96px; object-fit: contain; }

            .ncc-aaa-trophy-hero-operational { background: linear-gradient(135deg, rgba(255,249,234,.98), rgba(231,248,255,.96)); border-color: rgba(244,197,66,.78); }
            .ncc-aaa-trophy-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
            .ncc-aaa-trophy-empty { display: grid; gap: 12px; }
            .ncc-aaa-trophy-empty-steps { display: grid; gap: 8px; margin: 10px 0 12px; }
            .ncc-aaa-trophy-empty-steps span { display: block; padding: 9px 10px; border-radius: 12px; background: #f7fcff; border: 1px solid rgba(21,72,112,.16); font-weight: 800; color: #0f2d45; }
            .ncc-aaa-trophy-summary-card { border-color: rgba(244,197,66,.66); background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,251,236,.96)); }
            .ncc-aaa-trophy-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(126px, 1fr)); gap: 10px; margin: 12px 0; }
            .ncc-aaa-trophy-stat { min-height: 78px; border-radius: 16px; border: 1px solid rgba(21,72,112,.15); background: #f8fdff; display: grid; align-content: center; justify-items: center; text-align: center; padding: 10px; box-shadow: inset 0 1px 0 rgba(255,255,255,.9), 0 4px 12px rgba(0,0,0,.05); }
            .ncc-aaa-trophy-stat strong { display: block; color: var(--aaa-primary-dark); font-size: 28px; line-height: 1; letter-spacing: -.04em; }
            .ncc-aaa-trophy-stat span { display: block; margin-top: 4px; font-weight: 900; font-size: 11px; color: rgba(15,45,69,.78); text-transform: uppercase; letter-spacing: .035em; }
            .ncc-aaa-trophy-stat.is-owned { background: #e9fff4; border-color: rgba(30,132,73,.22); }
            .ncc-aaa-trophy-stat.is-best { background: #fff8dc; border-color: rgba(244,197,66,.42); }
            .ncc-aaa-trophy-stat.is-progress { background: linear-gradient(180deg, #e4f7ff, #f9fdff); border-color: rgba(66,167,221,.30); }
            .ncc-aaa-trophy-stat.is-upgradable { background: #fff8db; border-color: rgba(205,151,0,.28); }
            .ncc-aaa-trophy-stat.is-missing { background: #fff1f1; border-color: rgba(173,67,67,.20); }
            .ncc-aaa-trophy-stat.is-bronze { background: #fff1df; border-color: rgba(166,99,37,.22); }
            .ncc-aaa-trophy-stat.is-silver { background: #f4f8fb; border-color: rgba(98,124,147,.22); }
            .ncc-aaa-trophy-stat.is-gold { background: #fff8dc; border-color: rgba(244,197,66,.42); }
            .ncc-aaa-trophy-progress { height: 12px; border-radius: 999px; overflow: hidden; background: rgba(21,72,112,.10); border: 1px solid rgba(21,72,112,.14); box-shadow: inset 0 1px 2px rgba(0,0,0,.08); }
            .ncc-aaa-trophy-progress span { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, #42a7dd, #f4c542); min-width: 0; }
            .ncc-aaa-trophy-tools { display: grid; gap: 12px; border-color: rgba(66,167,221,.38); background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(241,251,255,.96)); }
            .ncc-aaa-trophy-tool-row { display: grid; grid-template-columns: minmax(220px, 1fr) auto; gap: 12px; align-items: end; }
            .ncc-aaa-trophy-search-label { display: grid; gap: 5px; color: rgba(15,45,69,.78); font-weight: 900; font-size: 11px; text-transform: uppercase; letter-spacing: .035em; }
            .ncc-aaa-trophy-search-label input { width: 100%; min-height: 38px; border-radius: 12px; border: 1px solid rgba(21,72,112,.22); background: #fff; color: #0f2d45; font: 13px Verdana,Arial,sans-serif; font-weight: 800; padding: 8px 11px; box-shadow: inset 0 1px 2px rgba(0,0,0,.05); }
            .ncc-aaa-trophy-search-label input:focus { outline: 3px solid rgba(66,167,221,.22); border-color: rgba(66,167,221,.72); }
            .ncc-aaa-trophy-filter-buttons { display: flex; flex-wrap: wrap; gap: 7px; justify-content: flex-end; }
            .ncc-aaa-trophy-filter-buttons .is-active { background: linear-gradient(180deg, #154870, #0e395a); color: #fff; border-color: #154870; }
            .ncc-aaa-trophy-no-results { margin: 0; padding: 16px; border: 1px dashed rgba(21,72,112,.28); border-radius: 14px; background: #fff; color: rgba(15,45,69,.72); font-weight: 900; text-align: center; }
            .ncc-aaa-trophy-category-list { display: grid; gap: 12px; }
            .ncc-aaa-trophy-category { padding: 0; overflow: hidden; }
            .ncc-aaa-trophy-category > summary { cursor: pointer; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 14px; color: var(--aaa-primary-dark); font-weight: 900; }
            .ncc-aaa-trophy-category > summary::-webkit-details-marker { display: none; }
            .ncc-aaa-trophy-category > summary::before { content: "▸"; font-size: 14px; transition: transform .14s ease; }
            .ncc-aaa-trophy-category[open] > summary::before { transform: rotate(90deg); }
            .ncc-aaa-trophy-category > summary span { flex: 1; }
            .ncc-aaa-trophy-category > summary small { color: rgba(15,45,69,.72); font-weight: 900; font-size: 11px; }
            .ncc-aaa-trophy-group { border-top: 1px solid rgba(21,72,112,.11); padding: 12px 14px; }
            .ncc-aaa-trophy-group h3, .ncc-aaa-trophy-group > summary { color: var(--aaa-primary-dark); margin: 0 0 10px; font-size: 14px; font-weight: 900; cursor: pointer; }
            .ncc-aaa-trophy-group small { color: rgba(15,45,69,.64); font-size: 11px; }
            .ncc-aaa-trophy-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 9px; }
            .ncc-aaa-trophy-item { position: relative; min-height: 124px; border-radius: 13px; border: 1px solid rgba(21,72,112,.13); background: #fbfeff; text-align: center; padding: 9px 7px; display: grid; justify-items: center; align-content: start; gap: 5px; box-shadow: 0 4px 10px rgba(0,0,0,.045); }
            .ncc-aaa-trophy-item.is-missing { background: #fff9f9; }
            .ncc-aaa-trophy-item.is-missing .ncc-aaa-trophy-image-wrap > img { opacity: .42; filter: grayscale(.35); }
            .ncc-aaa-trophy-item.is-upgradable { background: #fffdf3; border-color: rgba(205,151,0,.30); }
            .ncc-aaa-trophy-item.is-owned { background: #f3fff9; border-color: rgba(30,132,73,.18); }
            .ncc-aaa-trophy-image-wrap { position: relative; width: 58px; height: 58px; display: grid; place-items: center; }
            .ncc-aaa-trophy-image-wrap img { max-width: 54px; max-height: 54px; object-fit: contain; }
            .ncc-aaa-trophy-target { position: absolute; right: -9px; bottom: -4px; width: 29px !important; height: 29px !important; padding: 2px; border-radius: 999px; background: #fff; border: 1px solid rgba(205,151,0,.45); box-shadow: 0 2px 6px rgba(0,0,0,.14); }
            .ncc-aaa-trophy-item strong { color: var(--aaa-primary-dark); font-size: 11px; line-height: 1.18; }
            .ncc-aaa-trophy-item span { color: rgba(15,45,69,.68); font-size: 10px; font-weight: 900; text-transform: uppercase; letter-spacing: .03em; }

            .ncc-aaa-trophy-modern { gap: 14px; }
            .ncc-aaa-trophy-modern-hero { grid-template-columns: minmax(0, 1fr); padding: 22px; border-color: rgba(244,197,66,.72); background: radial-gradient(circle at top left, rgba(255,232,151,.40), transparent 34%), linear-gradient(135deg, rgba(10,62,96,.97), rgba(16,124,138,.88)); color: #fff8df; overflow: hidden; }
            .ncc-aaa-trophy-modern-hero::after { content: "TROPHY"; position: absolute; right: 22px; bottom: -5px; color: rgba(255,255,255,.08); font-size: 58px; line-height: 1; font-weight: 1000; letter-spacing: -.08em; pointer-events: none; }
            .ncc-aaa-trophy-hero-medals { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; }
            .ncc-aaa-trophy-hero-medals span { width: 48px; height: 48px; display: grid; place-items: center; border-radius: 15px; border: 1px solid rgba(255,232,151,.76); background: rgba(255,255,255,.92); box-shadow: 0 8px 18px rgba(0,0,0,.22), inset 0 1px 0 rgba(255,255,255,.9); }
            .ncc-aaa-trophy-hero-medals img { max-width: 36px; max-height: 36px; object-fit: contain; }
            .ncc-aaa-trophy-hero-copy { position: relative; z-index: 1; max-width: 930px; }
            .ncc-aaa-trophy-hero-copy .ncc-aaa-kicker, .ncc-aaa-trophy-hero-copy h2, .ncc-aaa-trophy-hero-copy p { color: #fff8df; }
            .ncc-aaa-trophy-hero-copy h2 { margin: 3px 0 8px; font-size: clamp(30px, 4vw, 48px); line-height: .92; letter-spacing: -.055em; text-shadow: 0 3px 5px rgba(0,0,0,.28); }
            .ncc-aaa-trophy-hero-copy p { max-width: 660px; margin: 0; line-height: 1.55; font-weight: 800; }
            .ncc-aaa-trophy-hero-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); gap: 9px; margin: 16px 0 14px; max-width: 760px; }
            .ncc-aaa-trophy-hero-metrics span { min-height: 58px; display: grid; align-content: center; gap: 3px; padding: 9px 11px; border-radius: 15px; border: 1px solid rgba(255,232,151,.50); background: rgba(255,255,255,.13); box-shadow: inset 0 1px 0 rgba(255,255,255,.16); backdrop-filter: blur(2px); }
            .ncc-aaa-trophy-hero-metrics strong { color: #fff8df; font-size: 18px; font-weight: 1000; line-height: 1; }
            .ncc-aaa-trophy-hero-metrics small { color: rgba(255,248,223,.82); font-size: 10px; font-weight: 900; text-transform: uppercase; letter-spacing: .05em; }
            .ncc-aaa-trophy-dashboard-modern { padding: 16px; border-color: rgba(66,167,221,.30); background: linear-gradient(180deg, rgba(255,255,255,.99), rgba(242,251,255,.96)); box-shadow: 0 8px 20px rgba(0,43,75,.055); }
            .ncc-aaa-trophy-dashboard-head, .ncc-aaa-trophy-tools-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 14px; margin-bottom: 12px; }
            .ncc-aaa-trophy-dashboard-head h2, .ncc-aaa-trophy-tools-head h2 { margin: 2px 0 0; color: var(--aaa-primary-dark); font-size: 27px; letter-spacing: -.035em; }
            .ncc-aaa-trophy-stat-grid-modern { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 10px; }
            .ncc-aaa-trophy-stat-grid-modern .ncc-aaa-trophy-stat { min-height: 86px; border-radius: 18px; background: linear-gradient(180deg, #ffffff, #f7fcff); transition: transform .12s ease, box-shadow .12s ease; }
            .ncc-aaa-trophy-stat-grid-modern .ncc-aaa-trophy-stat strong { font-size: 31px; }
            .ncc-aaa-trophy-stat-grid-modern .ncc-aaa-trophy-stat:hover { transform: translateY(-1px); box-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 8px 18px rgba(0,43,75,.10); }
            .ncc-aaa-trophy-progress-modern { height: 9px; margin-top: 12px; background: rgba(21,72,112,.08); border-color: rgba(21,72,112,.10); }
            .ncc-aaa-trophy-tools-modern { padding: 16px; border-color: rgba(66,167,221,.25); background: rgba(255,255,255,.96); }
            .ncc-aaa-trophy-tool-row-modern { grid-template-columns: minmax(280px, 1fr); gap: 12px; }
            .ncc-aaa-trophy-tools-modern .ncc-aaa-trophy-search-label input { min-height: 46px; border-radius: 16px; font-size: 14px; padding: 10px 14px; }
            .ncc-aaa-trophy-filter-buttons { justify-content: flex-start; }
            .ncc-aaa-trophy-filter-buttons .ncc-aaa-btn { min-height: 38px; border-radius: 14px; padding-inline: 14px; }
            .ncc-aaa-trophy-filter-buttons .is-active { box-shadow: 0 8px 18px rgba(14,57,90,.20); }
            .ncc-aaa-trophy-category-list-modern { gap: 10px; }
            .ncc-aaa-trophy-category-modern { border-color: rgba(21,72,112,.12); background: rgba(255,255,255,.98); box-shadow: 0 6px 16px rgba(0,43,75,.045); }
            .ncc-aaa-trophy-category-modern > summary { min-height: 56px; padding: 13px 15px; background: linear-gradient(180deg, #ffffff, #f8fcff); }
            .ncc-aaa-trophy-category-modern .ncc-aaa-trophy-category-title { font-size: 16px; letter-spacing: -.02em; }
            .ncc-aaa-trophy-category-meter { width: 110px; height: 8px; border-radius: 999px; background: rgba(21,72,112,.10); overflow: hidden; flex: 0 0 auto; }
            .ncc-aaa-trophy-category-meter i { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, #42a7dd, #f4c542); }
            .ncc-aaa-trophy-group-modern { padding: 13px 15px 15px; background: linear-gradient(180deg, rgba(255,255,255,.50), rgba(246,251,255,.72)); }
            .ncc-aaa-trophy-group-modern h3, .ncc-aaa-trophy-group-modern > summary { display: flex; align-items: center; gap: 7px; color: var(--aaa-primary-dark); font-size: 13px; letter-spacing: .02em; text-transform: uppercase; }
            .ncc-aaa-trophy-grid { grid-template-columns: repeat(auto-fill, minmax(138px, 1fr)); gap: 10px; }
            .ncc-aaa-trophy-item { min-height: 148px; border-radius: 17px; padding: 12px 9px 10px; background: linear-gradient(180deg, #ffffff, #f8fcff); box-shadow: 0 7px 16px rgba(0,43,75,.065); transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease; }
            .ncc-aaa-trophy-item:hover { transform: translateY(-2px); box-shadow: 0 12px 22px rgba(0,43,75,.11); border-color: rgba(66,167,221,.28); }
            .ncc-aaa-trophy-item-badge { position: absolute; top: 8px; left: 8px; min-height: 18px; padding: 3px 7px; border-radius: 999px; background: rgba(21,72,112,.08); color: rgba(15,45,69,.72); font-size: 9px; font-weight: 1000; text-transform: uppercase; letter-spacing: .045em; }
            .ncc-aaa-trophy-item.is-tier-bronze .ncc-aaa-trophy-item-badge { background: #fff1df; color: #8b5421; }
            .ncc-aaa-trophy-item.is-tier-silver .ncc-aaa-trophy-item-badge { background: #eef4f8; color: #536b7d; }
            .ncc-aaa-trophy-item.is-tier-gold .ncc-aaa-trophy-item-badge { background: #fff5ca; color: #8a6800; }
            .ncc-aaa-trophy-item.is-tier-unique .ncc-aaa-trophy-item-badge { background: #e7f7ff; color: #0d5c86; }
            .ncc-aaa-trophy-image-wrap { width: 68px; height: 68px; margin-top: 14px; }
            .ncc-aaa-trophy-image-wrap img { max-width: 62px; max-height: 62px; }
            .ncc-aaa-trophy-item strong { font-size: 12px; line-height: 1.22; max-width: 118px; }
            .ncc-aaa-trophy-status-text { opacity: .78; }
            .ncc-aaa-trophy-empty-modern { background: linear-gradient(135deg, rgba(255,255,255,.98), rgba(241,251,255,.96)); }
            .ncc-aaa-trophy-empty-modern .ncc-aaa-trophy-empty-steps { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
            .ncc-aaa-trophy-empty-modern .ncc-aaa-trophy-empty-steps span b { display: inline-grid; place-items: center; width: 22px; height: 22px; margin-right: 6px; border-radius: 999px; background: #154870; color: #fff; }
            @media (max-width: 900px) { .ncc-aaa-trophy-stat-grid-modern { grid-template-columns: repeat(2, minmax(0, 1fr)); } .ncc-aaa-trophy-category-meter { display: none; } .ncc-aaa-trophy-category-modern > summary { align-items: flex-start; flex-direction: column; } }

            .ncc-aaa-stage-layout .ncc-aaa-stage-board-main {
                grid-area: board;
                align-self: end;
                min-height: 296px;
                display: grid;
                align-content: center;
                padding: 26px 120px 25px 28px;
                border-radius: 20px;
                border: 2px solid rgba(216,174,66,.92);
                background: linear-gradient(135deg, rgba(5,48,74,.93), rgba(7,88,100,.82));
                color: #fff9df;
                box-shadow: 0 14px 28px rgba(0,26,50,.38), inset 0 0 0 1px rgba(255,255,255,.13), inset 0 -20px 32px rgba(0,0,0,.10);
            }
            .ncc-aaa-stage-layout .ncc-aaa-stage-board-main h1 {
                margin: 7px 0 13px;
                max-width: 650px;
                color: #fff8df;
                font-size: clamp(35px, 4.2vw, 53px);
                line-height: .95;
                font-weight: 1000;
                letter-spacing: -.055em;
                text-shadow: 0 3px 4px rgba(0,0,0,.42);
            }
            .ncc-aaa-stage-layout .ncc-aaa-stage-board-main .ncc-aaa-subtitle {
                max-width: 660px;
                color: #fff8df;
                font-size: 12px;
                line-height: 1.58;
            }
            .ncc-aaa-stage-layout .ncc-aaa-board-badge {
                position: absolute;
                top: 20px;
                right: 22px;
                width: 86px;
                max-width: none;
                height: auto;
                object-fit: contain;
                border-radius: 13px;
                border: 2px solid rgba(216,174,66,.90);
                background: rgba(255,248,220,.92);
                padding: 4px;
                box-shadow: 0 7px 15px rgba(0,0,0,.32);
            }
            .ncc-aaa-status-pills {
                display: flex;
                flex-wrap: wrap;
                gap: 10px;
                margin: 17px 0 18px;
            }
            .ncc-aaa-status-pills span {
                display: inline-flex;
                align-items: center;
                justify-content: center;
                gap: 6px;
                min-height: 31px;
                padding: 7px 12px;
                border-radius: 999px;
                border: 1px solid rgba(216,174,66,.82);
                background: rgba(255,246,215,.96);
                color: #073d63;
                font-weight: 900;
                font-size: 11px;
                box-shadow: 0 3px 8px rgba(0,27,50,.18), inset 0 1px 0 rgba(255,255,255,.82);
            }
            .ncc-aaa-status-pills strong { font-size: 14px; }
            /* NCC AAA v0.1.14 Step 4 · Button System */
            .ncc-aaa-stage-layout .ncc-aaa-stage-actions {
                display: flex;
                flex-wrap: wrap;
                gap: 13px;
                margin-top: 4px;
            }
            .ncc-aaa-btn {
                appearance: none;
                position: relative;
                display: inline-flex;
                align-items: center;
                justify-content: center;
                gap: 9px;
                min-height: 48px;
                padding: 12px 18px;
                border-radius: 15px;
                border: 2px solid transparent;
                font-size: 12px;
                line-height: 1.05;
                font-weight: 1000;
                letter-spacing: .01em;
                text-decoration: none;
                cursor: pointer;
                user-select: none;
                box-shadow: 0 8px 18px rgba(0,24,70,.28), inset 0 1px 0 rgba(255,255,255,.22);
                transition: transform .14s ease, filter .14s ease, box-shadow .14s ease;
            }
            .ncc-aaa-btn::after {
                content: "";
                position: absolute;
                inset: 4px;
                border-radius: 11px;
                border: 1px solid rgba(255,255,255,.16);
                pointer-events: none;
            }
            .ncc-aaa-btn:hover {
                transform: translateY(-1px);
                filter: brightness(1.06);
                box-shadow: 0 10px 20px rgba(0,24,70,.34), inset 0 1px 0 rgba(255,255,255,.28);
            }
            .ncc-aaa-btn:focus-visible {
                outline: 3px solid rgba(255,245,216,.78);
                outline-offset: 3px;
            }
            .ncc-aaa-btn-icon {
                display: inline-grid;
                place-items: center;
                width: 22px;
                height: 22px;
                border-radius: 999px;
                background: rgba(255,255,255,.16);
                box-shadow: inset 0 1px 0 rgba(255,255,255,.24);
                font-size: 13px;
                line-height: 1;
            }
            .ncc-aaa-btn-primary {
                border-color: #d8ae42;
                background: linear-gradient(180deg, #2366e8 0%, #174fd6 46%, #0b3999 100%);
                color: #fff9df;
            }
            .ncc-aaa-btn-secondary {
                border-color: rgba(255,248,220,.95);
                background: linear-gradient(180deg, #fffdf3 0%, #f7edcf 52%, #ead69b 100%);
                color: #073d63;
            }
            .ncc-aaa-btn-secondary .ncc-aaa-btn-icon {
                background: rgba(7,61,99,.10);
            }
            .ncc-aaa-btn-tech {
                border-color: rgba(216,174,66,.68);
                background: linear-gradient(180deg, #11506d 0%, #0d3a52 52%, #082336 100%);
                color: #fff9df;
            }
            .ncc-aaa-stage-layout .ncc-aaa-stage-button { flex: 0 1 auto; }
            @media (max-width: 680px) {
                .ncc-aaa-stage-layout .ncc-aaa-stage-actions { display: grid; grid-template-columns: 1fr; gap: 10px; }
                .ncc-aaa-btn { width: 100%; }
            }
            .ncc-aaa-stage-layout .ncc-aaa-progress-panel-stage2 {
                grid-area: progress;
                align-self: end;
                min-height: 286px;
                display: grid;
                justify-items: center;
                align-content: center;
                padding: 22px 16px;
                border-radius: 18px;
                border: 2px solid #d8ae42;
                background: linear-gradient(180deg, rgba(255,252,239,.97), rgba(255,238,199,.94));
                color: #073d63;
                text-align: center;
                box-shadow: 0 10px 24px rgba(0,26,50,.32), inset 0 0 0 2px rgba(255,255,255,.46);
            }
            .ncc-aaa-progress-kicker { margin: 0 0 8px; color: #073d63; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; font-weight: 1000; }
            .ncc-aaa-level-medal {
                width: 94px;
                height: 94px;
                display: grid;
                place-items: center;
                margin: 4px auto 13px;
                border-radius: 999px;
                border: 8px solid #d8ae42;
                background: radial-gradient(circle at 35% 30%, #fff7cd, #6bc7c5 45%, #0d6b7b 100%);
                color: #073d63;
                font-size: 52px;
                font-weight: 1000;
                line-height: 1;
                text-shadow: 0 2px 0 rgba(255,255,255,.55);
                box-shadow: inset 0 1px 0 rgba(255,255,255,.85), 0 8px 18px rgba(0,43,75,.24);
            }


            @media (max-width: 1040px) {
                .ncc-aaa-hero.ncc-aaa-stage-layout { grid-template-columns: 190px minmax(0,1fr); grid-template-areas: "player board" "progress board"; padding: 96px 24px 24px 102px; }
                .ncc-aaa-stage-host { width: 112px; opacity: .62; }
                .ncc-aaa-stage-layout .ncc-aaa-progress-panel-stage2 { min-height: 170px; }
                .ncc-aaa-level-medal { width: 72px; height: 72px; font-size: 38px; border-width: 6px; }
            }
            @media (max-width: 820px) {
                .ncc-aaa-hero.ncc-aaa-stage-layout { grid-template-columns: 1fr; grid-template-areas: "board" "player" "progress"; padding: 78px 18px 22px; min-height: auto; }
                .ncc-aaa-stage-host { opacity: .20; left: 0; bottom: 10px; width: 130px; }
                .ncc-aaa-stage-layout .ncc-aaa-stage-board-main { padding: 24px 20px; min-height: auto; }
                .ncc-aaa-stage-layout .ncc-aaa-board-badge { display: none; }
                .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2,
                .ncc-aaa-stage-layout .ncc-aaa-progress-panel-stage2 { width: min(320px, 100%); margin: 0 auto; }
            }
            @media (max-width: 760px) {
                .ncc-aaa-arena-school-grid { grid-template-columns: 1fr; }
                .ncc-aaa-arena-feature-grid, .ncc-aaa-arena-future-grid { grid-template-columns: 1fr; }
                .ncc-aaa-arena-school-card { grid-template-columns: auto 1fr; }
                .ncc-aaa-topbar { justify-content: center; border-radius: 18px; padding: 8px; gap: 7px; }
                .ncc-aaa-top-pill { font-size: 10px; padding: 5px 9px; min-height: 24px; letter-spacing: .045em; }
            }
            @media (max-width: 940px) {
                .ncc-aaa-backstage-hero { grid-template-columns: 100px minmax(0, 1fr); }
                .ncc-aaa-backstage-host img { max-width: 92px; max-height: 112px; }
                .ncc-aaa-backstage-grid { grid-template-columns: 1fr; }
                .ncc-aaa-hero { grid-template-columns: 1fr; grid-template-areas: "board" "player" "progress"; min-height: auto; background-position: center top; }
                .ncc-aaa-stage-character { display: none; }
                .ncc-aaa-stage-board { min-height: auto; }
                .ncc-aaa-home-stage-card { grid-template-columns: 1fr; }
                .ncc-aaa-home-announcer { grid-template-columns: 82px 1fr; justify-items: start; text-align: left; }
                .ncc-aaa-home-announcer img { max-width: 82px; max-height: 82px; }
                .ncc-aaa-home-card { grid-template-columns: 1fr; }
                .ncc-aaa-profile-view-polished { grid-template-columns: 1fr; }
                .ncc-aaa-profile-preview { position: relative; top: auto; }
            }
            @media (max-width: 860px) {
                .ncc-aaa-challenge-gallery { grid-template-columns: 1fr; }
                .ncc-aaa-module-grid { grid-template-columns: 1fr; }
                .ncc-aaa-module-meta { grid-template-columns: 1fr; }
                .ncc-aaa-games-stage-panel { grid-template-columns: 92px minmax(0, 1fr); }
                .ncc-aaa-games-announcer img { max-width: 90px; }
            }
            @media (max-width: 720px) {
                .ncc-aaa-backstage-hero { grid-template-columns: 1fr; text-align: center; }
                .ncc-aaa-backstage-host { display: none; }
                .ncc-aaa-backstage-badges { justify-content: center; }
                .ncc-aaa-backstage-list div { grid-template-columns: 1fr; gap: 2px; }
                .ncc-aaa-grid { grid-template-columns: 1fr; }
                .ncc-aaa-quick-board { grid-template-columns: repeat(2, minmax(0, 1fr)); }
                .ncc-aaa-home-badges { grid-template-columns: 1fr; }
                .ncc-aaa-challenge-ribbon { grid-template-columns: repeat(2, minmax(0,1fr)); }
                .ncc-aaa-ribbon-item:nth-child(3) { border-left: 0; }
                .ncc-aaa-mission-board { grid-template-columns: 1fr; }
                .ncc-aaa-metrics { grid-template-columns: 1fr; }
                .ncc-aaa-tabs, .ncc-aaa-tabs-strip { flex-wrap: wrap; border-radius: 18px 18px 0 0; }
                .ncc-aaa-tab { flex: 1 1 140px; min-width: 135px; justify-content: center; }
                .ncc-aaa-stage-board-top { display: block; }
                .ncc-aaa-event-mark { display: none; }
            }
            @media (max-width: 520px) {
                .ncc-aaa-quick-board { grid-template-columns: 1fr; }
                .ncc-aaa-challenge-ribbon { grid-template-columns: 1fr; }
                .ncc-aaa-ribbon-item + .ncc-aaa-ribbon-item { border-left: 0; border-top: 1px solid rgba(255,245,216,.22); }
                .ncc-aaa-home-announcer { grid-template-columns: 1fr; justify-items: center; text-align: center; }
                .ncc-aaa-player-ticket { grid-template-columns: 1fr; justify-items: center; text-align: center; }
                .ncc-aaa-mini-button { text-align: center; }
            }


            /* NCC AAA v0.1.14 Step 6 · Home Refactor */
            .ncc-aaa-home-refactor {
                display: grid;
                grid-template-columns: minmax(280px, 1.06fr) minmax(430px, 1.75fr);
                gap: 18px;
                padding: 20px;
                border-radius: 0 0 20px 20px;
                border: 2px solid #d8ae42;
                border-top: 0;
                background: linear-gradient(180deg, rgba(255,250,233,.98), rgba(255,246,219,.96));
                box-shadow: inset 0 1px 0 rgba(255,255,255,.62);
            }
            .ncc-aaa-home-welcome-panel,
            .ncc-aaa-home-slots-panel,
            .ncc-aaa-home-player-strip {
                position: relative;
                border-radius: 18px;
                border: 1px solid rgba(216,174,66,.52);
                background: linear-gradient(180deg, rgba(255,255,255,.82), rgba(255,246,222,.90));
                box-shadow: inset 0 0 0 1px rgba(255,255,255,.48), 0 8px 18px rgba(0,43,75,.08);
                color: #073d63;
            }
            .ncc-aaa-home-welcome-panel {
                min-height: 250px;
                padding: 24px;
                display: grid;
                grid-template-columns: minmax(0, 1fr) 210px;
                gap: 18px;
                grid-column: 1 / 2;
                grid-row: 1 / span 2;
                overflow: hidden;
            }
            .ncc-aaa-home-welcome-panel::after {
                content: "AAA";
                position: absolute;
                right: 14px;
                bottom: -18px;
                color: rgba(7,61,99,.045);
                font-size: 94px;
                font-weight: 1000;
                letter-spacing: -.08em;
                pointer-events: none;
            }
            .ncc-aaa-home-welcome-copy { position: relative; z-index: 1; }
            .ncc-aaa-home-welcome-copy h2 {
                margin: 0 0 12px;
                font-size: 27px;
                line-height: 1.04;
                color: #073d63;
                letter-spacing: -.035em;
            }
            .ncc-aaa-home-welcome-copy p {
                margin: 0;
                color: rgba(7,61,99,.82);
                font-size: 12px;
                line-height: 1.62;
            }
            .ncc-aaa-home-link {
                appearance: none;
                margin-top: 16px;
                padding: 0;
                border: 0;
                background: transparent;
                color: #0f5770;
                font-weight: 900;
                cursor: pointer;
                text-decoration: underline;
                text-underline-offset: 3px;
            }
            .ncc-aaa-home-status-stack {
                position: relative;
                z-index: 1;
                display: grid;
                gap: 10px;
                align-content: center;
            }
            .ncc-aaa-home-status-item {
                display: grid;
                grid-template-columns: 34px minmax(0, 1fr);
                gap: 10px;
                align-items: center;
                padding: 11px;
                border-radius: 14px;
                border: 1px solid rgba(7,61,99,.15);
                background: rgba(255,255,255,.58);
            }
            .ncc-aaa-home-status-item > span {
                width: 34px;
                height: 34px;
                border-radius: 999px;
                display: grid;
                place-items: center;
                background: linear-gradient(180deg, #e6fbff, #c9ecf6);
                color: #073d63;
                font-weight: 1000;
                box-shadow: inset 0 1px 0 rgba(255,255,255,.7);
            }
            .ncc-aaa-home-status-item.is-ok > span { background: linear-gradient(180deg, #e8ffd7, #c6efb2); color: #245b1e; }
            .ncc-aaa-home-status-item strong { display: block; font-size: 12px; color: #073d63; }
            .ncc-aaa-home-status-item small { display: block; margin-top: 2px; color: rgba(7,61,99,.68); font-weight: 800; }
            .ncc-aaa-home-stat-grid {
                grid-column: 2 / 3;
                display: grid;
                grid-template-columns: repeat(4, minmax(0, 1fr));
                gap: 14px;
            }
            .ncc-aaa-home-stat-card {
                min-height: 148px;
                padding: 18px 12px;
                border-radius: 17px;
                display: grid;
                justify-items: center;
                align-content: center;
                gap: 8px;
                text-align: center;
                border: 1px solid rgba(7,61,99,.16);
                background: rgba(255,255,255,.74);
                box-shadow: inset 0 1px 0 rgba(255,255,255,.76), 0 7px 14px rgba(0,43,75,.07);
                overflow: hidden;
            }
            .ncc-aaa-home-stat-card.is-modules { background: linear-gradient(180deg, #edfaff, #f9ffff); border-color: rgba(75,153,185,.32); }
            .ncc-aaa-home-stat-card.is-alerts { background: linear-gradient(180deg, #fff8df, #fffdf5); border-color: rgba(216,174,66,.42); }
            .ncc-aaa-home-stat-card.is-actions { background: linear-gradient(180deg, #f5efff, #fffaff); border-color: rgba(129,104,178,.30); }
            .ncc-aaa-home-stat-card.is-safe { background: linear-gradient(180deg, #efffe9, #fbfff7); border-color: rgba(97,157,82,.32); }
            .ncc-aaa-home-stat-icon {
                width: 36px;
                height: 36px;
                border-radius: 999px;
                display: grid;
                place-items: center;
                background: rgba(7,61,99,.08);
                color: #073d63;
                font-weight: 1000;
            }
            .ncc-aaa-home-stat-card strong {
                display: block;
                color: #073d63;
                font-size: 30px;
                line-height: .9;
                font-weight: 1000;
            }
            .ncc-aaa-home-stat-card span:last-child {
                color: rgba(7,61,99,.78);
                font-size: 11px;
                line-height: 1.25;
                font-weight: 900;
            }
            .ncc-aaa-home-slots-panel {
                grid-column: 1 / -1;
                padding: 20px;
            }
            .ncc-aaa-home-section-heading {
                align-items: center;
                margin-bottom: 10px;
            }
            .ncc-aaa-home-section-heading h2 {
                margin: 0;
                color: #073d63;
                font-size: 22px;
                letter-spacing: -.025em;
            }
            .ncc-aaa-home-slots-note {
                margin: -2px 0 15px;
                color: rgba(7,61,99,.72);
                font-size: 12px;
                line-height: 1.45;
                font-weight: 700;
            }
            .ncc-aaa-home-empty-slot-row {
                display: grid;
                grid-template-columns: repeat(4, minmax(130px, 1fr));
                gap: 14px;
            }
            .ncc-aaa-home-empty-slot {
                min-height: 98px;
                display: grid;
                place-items: center;
                gap: 6px;
                text-align: center;
                border-radius: 16px;
                border: 2px dashed rgba(7,61,99,.27);
                background: rgba(255,255,255,.58);
                color: #073d63;
                box-shadow: inset 0 1px 0 rgba(255,255,255,.66);
            }
            .ncc-aaa-home-empty-slot-icon {
                width: 34px;
                height: 34px;
                border-radius: 12px;
                display: grid;
                place-items: center;
                background: rgba(216,174,66,.16);
                color: rgba(7,61,99,.64);
                font-weight: 1000;
            }
            .ncc-aaa-home-empty-slot strong {
                font-size: 12px;
                color: rgba(7,61,99,.78);
            }
            .ncc-aaa-home-player-strip {
                grid-column: 1 / -1;
                padding: 14px 16px;
                display: flex;
                align-items: center;
                justify-content: space-between;
                gap: 16px;
            }
            .ncc-aaa-home-player-mini {
                display: flex;
                align-items: center;
                gap: 12px;
                min-width: 0;
            }
            .ncc-aaa-home-player-mini img {
                width: 54px;
                height: 54px;
                border-radius: 14px;
                object-fit: cover;
                border: 2px solid rgba(216,174,66,.84);
                background: #fff;
                box-shadow: 0 5px 12px rgba(0,43,75,.13);
            }
            .ncc-aaa-home-player-mini strong {
                display: block;
                color: #073d63;
                font-size: 15px;
                line-height: 1.1;
            }
            .ncc-aaa-home-player-mini span,
            .ncc-aaa-home-player-mini small {
                display: block;
                color: rgba(7,61,99,.72);
                font-size: 11px;
                font-weight: 800;
                margin-top: 2px;
            }
            .ncc-aaa-home-mini-button { min-height: 38px; padding: 9px 13px; border-radius: 13px; font-size: 11px; }
            @media (max-width: 760px) {
                .ncc-aaa-home-visual-panel { grid-template-columns: 1fr; }
                .ncc-aaa-avatar-modal { padding: 12px; align-items: start; }
                .ncc-aaa-avatar-modal-panel { max-height: calc(100vh - 24px); padding: 14px; }
                .ncc-aaa-avatar-modal-head { align-items: flex-start; }
                .ncc-aaa-avatar-grid-modal { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
                .ncc-aaa-trophy-hero { grid-template-columns: 1fr; text-align: center; }
                .ncc-aaa-trophy-tool-row { grid-template-columns: 1fr; }
                .ncc-aaa-trophy-filter-buttons { justify-content: stretch; }
                .ncc-aaa-trophy-filter-buttons .ncc-aaa-btn { flex: 1 1 130px; }
            }
            @media (max-width: 980px) {
                .ncc-aaa-home-refactor { grid-template-columns: 1fr; }
                .ncc-aaa-home-welcome-panel,
                .ncc-aaa-home-stat-grid,
                .ncc-aaa-home-slots-panel,
                .ncc-aaa-home-player-strip { grid-column: 1 / -1; }
                .ncc-aaa-home-stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
                .ncc-aaa-home-empty-slot-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
            }
            @media (max-width: 640px) {
                .ncc-aaa-home-refactor { padding: 14px; }
                .ncc-aaa-home-welcome-panel { grid-template-columns: 1fr; padding: 18px; }
                .ncc-aaa-home-stat-grid, .ncc-aaa-home-empty-slot-row { grid-template-columns: 1fr; }
                .ncc-aaa-home-player-strip { align-items: stretch; flex-direction: column; }
                .ncc-aaa-home-mini-button { width: 100%; }
            }


            /* Step 7 · Responsive Pass · Implementable Stage Layout */
            .ncc-aaa-shell {
                width: min(100%, 1120px);
                box-sizing: border-box;
            }
            .ncc-aaa-shell *,
            .ncc-aaa-shell *::before,
            .ncc-aaa-shell *::after {
                box-sizing: border-box;
            }
            .ncc-aaa-topbar {
                overflow: hidden;
            }
            .ncc-aaa-top-pill {
                white-space: nowrap;
                max-width: 100%;
            }
            .ncc-aaa-hero.ncc-aaa-stage-layout {
                isolation: isolate;
            }
            .ncc-aaa-stage-layout .ncc-aaa-stage-board-main,
            .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2,
            .ncc-aaa-stage-layout .ncc-aaa-progress-panel-stage2,
            .ncc-aaa-home-welcome-panel,
            .ncc-aaa-home-stat-card,
            .ncc-aaa-home-slots-panel,
            .ncc-aaa-home-player-strip {
                min-width: 0;
            }
            .ncc-aaa-stage-layout .ncc-aaa-stage-board-main h1,
            .ncc-aaa-home-welcome-copy h2,
            .ncc-aaa-home-section-heading h2 {
                overflow-wrap: anywhere;
            }
            .ncc-aaa-stage-layout .ncc-aaa-stage-board-main .ncc-aaa-subtitle,
            .ncc-aaa-home-welcome-copy p,
            .ncc-aaa-home-slots-note {
                overflow-wrap: break-word;
            }
            .ncc-aaa-stage-layout .ncc-aaa-status,
            .ncc-aaa-stage-layout .ncc-aaa-stage-actions {
                max-width: 100%;
            }
            .ncc-aaa-stage-layout .ncc-aaa-status span {
                min-width: 0;
                max-width: 100%;
            }
            .ncc-aaa-home-refactor {
                overflow: hidden;
            }
            .ncc-aaa-home-empty-slot,
            .ncc-aaa-home-stat-card {
                overflow: hidden;
            }
            @media (max-width: 1180px) {
                .ncc-aaa-shell {
                    width: 100%;
                    margin-left: auto;
                    margin-right: auto;
                }
                .ncc-aaa-hero.ncc-aaa-stage-layout {
                    grid-template-columns: 205px minmax(0, 1fr) 185px;
                    gap: 16px;
                    padding-left: 126px;
                    padding-right: 22px;
                }
                .ncc-aaa-stage-host {
                    width: 126px;
                    left: 6px;
                }
                .ncc-aaa-stage-layout .ncc-aaa-stage-board-main h1 {
                    font-size: clamp(30px, 4.2vw, 46px);
                }
            }
            @media (max-width: 1040px) {
                .ncc-aaa-hero.ncc-aaa-stage-layout {
                    grid-template-columns: minmax(185px, 230px) minmax(0, 1fr);
                    grid-template-areas:
                        "player board"
                        "progress board";
                    gap: 16px;
                    padding: 92px 22px 22px 92px;
                    background-position: center top;
                }
                .ncc-aaa-stage-host {
                    width: 102px;
                    opacity: .58;
                    left: 2px;
                    bottom: 22px;
                }
                .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2 {
                    align-self: stretch;
                    min-height: 0;
                }
                .ncc-aaa-stage-layout .ncc-aaa-progress-panel-stage2 {
                    align-self: stretch;
                    min-height: 0;
                    padding: 16px;
                }
                .ncc-aaa-stage-layout .ncc-aaa-level-medal-stage2 {
                    width: 76px;
                    height: 76px;
                    font-size: 42px;
                    margin: 10px auto;
                }
                .ncc-aaa-home-refactor {
                    grid-template-columns: 1fr;
                }
                .ncc-aaa-home-welcome-panel,
                .ncc-aaa-home-stat-grid,
                .ncc-aaa-home-slots-panel,
                .ncc-aaa-home-player-strip {
                    grid-column: 1 / -1;
                }
            }
            @media (max-width: 900px) {
                .ncc-aaa-topbar {
                    justify-content: center;
                    border-radius: 20px;
                    padding: 8px;
                }
                .ncc-aaa-top-pill {
                    flex: 0 1 auto;
                    padding-inline: 10px;
                    font-size: 10px;
                }
                .ncc-aaa-tabs,
                .ncc-aaa-tabs-strip {
                    flex-wrap: wrap;
                }
                .ncc-aaa-tab {
                    flex: 1 1 50%;
                    min-width: 0;
                }
            }
            @media (max-width: 820px) {
                .ncc-aaa-hero.ncc-aaa-stage-layout {
                    grid-template-columns: 1fr;
                    grid-template-areas:
                        "board"
                        "player"
                        "progress";
                    padding: 74px 16px 18px;
                    min-height: auto;
                    background-position: center top;
                }
                .ncc-aaa-stage-host {
                    width: 124px;
                    opacity: .18;
                    left: auto;
                    right: 8px;
                    bottom: 10px;
                }
                .ncc-aaa-stage-layout .ncc-aaa-stage-board-main {
                    min-height: auto;
                    padding: 22px 18px;
                }
                .ncc-aaa-stage-layout .ncc-aaa-stage-board-main h1 {
                    font-size: clamp(30px, 8vw, 42px);
                }
                .ncc-aaa-stage-layout .ncc-aaa-board-badge {
                    display: none;
                }
                .ncc-aaa-stage-layout .ncc-aaa-player-pass-stage2,
                .ncc-aaa-stage-layout .ncc-aaa-progress-panel-stage2 {
                    width: min(340px, 100%);
                    margin: 0 auto;
                }
                .ncc-aaa-stage-layout .ncc-aaa-pass-avatar-stage2 {
                    width: 96px;
                    height: 96px;
                }
                .ncc-aaa-home-welcome-panel {
                    grid-template-columns: 1fr;
                }
                .ncc-aaa-home-stat-grid,
                .ncc-aaa-home-empty-slot-row {
                    grid-template-columns: repeat(2, minmax(0, 1fr));
                }
            }
            @media (max-width: 640px) {
                .ncc-aaa-shell {
                    padding-inline: 0;
                }
                .ncc-aaa-topbar {
                    display: grid;
                    grid-template-columns: repeat(2, minmax(0, 1fr));
                    border-radius: 16px;
                }
                .ncc-aaa-top-pill {
                    justify-content: center;
                    text-align: center;
                    font-size: 9px;
                    padding: 7px 8px;
                }
                .ncc-aaa-top-pill-brand,
                .ncc-aaa-top-pill-gold {
                    grid-column: span 2;
                }
                .ncc-aaa-hero.ncc-aaa-stage-layout {
                    padding: 58px 12px 14px;
                    border-radius: 18px;
                }
                .ncc-aaa-stage-layout .ncc-aaa-stage-board-main {
                    border-radius: 16px;
                    padding: 19px 15px;
                }
                .ncc-aaa-stage-layout .ncc-aaa-stage-board-main h1 {
                    font-size: 32px;
                    letter-spacing: -.035em;
                }
                .ncc-aaa-stage-layout .ncc-aaa-status {
                    display: grid;
                    grid-template-columns: 1fr;
                    gap: 8px;
                }
                .ncc-aaa-stage-layout .ncc-aaa-status span {
                    justify-content: center;
                }
                .ncc-aaa-stage-layout .ncc-aaa-stage-actions {
                    display: grid;
                    grid-template-columns: 1fr;
                    gap: 10px;
                }
                .ncc-aaa-btn,
                .ncc-aaa-stage-layout .ncc-aaa-stage-button {
                    width: 100%;
                }
                .ncc-aaa-tab {
                    flex-basis: 100%;
                    padding: 12px 14px;
                }
                .ncc-aaa-home-refactor {
                    padding: 12px;
                    gap: 12px;
                }
                .ncc-aaa-home-welcome-panel,
                .ncc-aaa-home-slots-panel,
                .ncc-aaa-home-player-strip {
                    padding: 16px;
                    border-radius: 16px;
                }
                .ncc-aaa-home-stat-grid,
                .ncc-aaa-home-empty-slot-row {
                    grid-template-columns: 1fr;
                }
                .ncc-aaa-home-player-strip {
                    flex-direction: column;
                    align-items: stretch;
                }
                .ncc-aaa-home-mini-button {
                    width: 100%;
                }
            }

            /* Step 9 · Full Width Mount Fix · /~Yillcivi + /sketches.phtml */
            html[data-ncc-aaa-fullwidth="true"] body #main[data-ncc-aaa-fullwidth-main="true"] {
                width: min(1240px, calc(100vw - 24px)) !important;
                max-width: none !important;
                min-width: 0 !important;
                margin-left: auto !important;
                margin-right: auto !important;
            }
            html[data-ncc-aaa-fullwidth="true"] #content[data-ncc-aaa-fullwidth-zone="true"] {
                width: 100% !important;
                max-width: none !important;
                min-width: 0 !important;
                overflow: visible !important;
            }
            html[data-ncc-aaa-fullwidth="true"] #content [data-ncc-aaa-content-table="true"] {
                width: 100% !important;
                max-width: none !important;
                min-width: 0 !important;
                table-layout: auto !important;
                margin-left: auto !important;
                margin-right: auto !important;
            }
            html[data-ncc-aaa-fullwidth="true"] #content [data-ncc-aaa-content-row="true"] {
                width: 100% !important;
            }
            html[data-ncc-aaa-fullwidth="true"] #content [data-ncc-aaa-hidden-sidebar="true"] {
                display: none !important;
                width: 0 !important;
                min-width: 0 !important;
                max-width: 0 !important;
                padding: 0 !important;
                margin: 0 !important;
                border: 0 !important;
                overflow: hidden !important;
            }
            html[data-ncc-aaa-fullwidth="true"] #content .ncc-aaa-fullwidth-content {
                width: 100% !important;
                max-width: none !important;
                min-width: 0 !important;
                display: table-cell !important;
                padding-left: 0 !important;
                padding-right: 0 !important;
                vertical-align: top !important;
            }
            html[data-ncc-aaa-fullwidth="true"] #ncc-aaa-page {
                width: 100% !important;
                max-width: none !important;
                min-width: 0 !important;
            }
            html[data-ncc-aaa-fullwidth="true"] #ncc-aaa-page .ncc-aaa-shell {
                width: min(1180px, 100%) !important;
                max-width: none !important;
                min-width: 0 !important;
                margin-left: auto !important;
                margin-right: auto !important;
            }
            html[data-ncc-aaa-fullwidth="true"] #ncc-aaa-page .ncc-aaa-hero.ncc-aaa-stage-layout {
                grid-template-columns: 220px minmax(430px, 1fr) 190px;
                gap: 18px;
                padding-left: 142px;
                padding-right: 26px;
            }
            html[data-ncc-aaa-fullwidth="true"] #ncc-aaa-page .ncc-aaa-stage-layout .ncc-aaa-stage-board-main h1 {
                word-break: normal !important;
                overflow-wrap: normal !important;
                white-space: normal !important;
                hyphens: none !important;
                line-height: .98;
            }
            html[data-ncc-aaa-fullwidth="true"] #ncc-aaa-page .ncc-aaa-home-welcome-copy h2,
            html[data-ncc-aaa-fullwidth="true"] #ncc-aaa-page .ncc-aaa-home-section-heading h2 {
                word-break: normal !important;
                overflow-wrap: normal !important;
                hyphens: none !important;
            }
            @media (max-width: 980px) {
                html[data-ncc-aaa-fullwidth="true"] body #main[data-ncc-aaa-fullwidth-main="true"] {
                    width: calc(100vw - 12px) !important;
                }
                html[data-ncc-aaa-fullwidth="true"] #ncc-aaa-page .ncc-aaa-shell {
                    width: 100% !important;
                }
            }
            @media (max-width: 820px) {
                html[data-ncc-aaa-fullwidth="true"] #ncc-aaa-page .ncc-aaa-hero.ncc-aaa-stage-layout {
                    grid-template-columns: 1fr;
                    padding-left: 16px;
                    padding-right: 16px;
                }
            }

            @media (max-width: 420px) {
                .ncc-aaa-stage-layout .ncc-aaa-stage-board-main h1 {
                    font-size: 28px;
                }
                .ncc-aaa-stage-layout .ncc-aaa-subtitle,
                .ncc-aaa-home-welcome-copy p,
                .ncc-aaa-home-slots-note {
                    font-size: 12px;
                }
                .ncc-aaa-home-status-item {
                    grid-template-columns: 32px 1fr;
                    padding: 9px;
                }
            }

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

    function getPublicWindow() {
        try {
            return typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
        } catch (_) {
            return window;
        }
    }

    function describeElement(element) {
        if (!element || !element.tagName) return "";
        const tag = String(element.tagName || "").toLowerCase();
        const id = element.id ? `#${element.id}` : "";
        const classes = String(element.className || "").trim().split(/\s+/).filter(Boolean).slice(0, 4).map(name => `.${name}`).join("");
        return `${tag}${id}${classes}`;
    }

    function getAvatar(id) {
        return AAA_AVATARS.find(item => item.id === id) || AAA_AVATARS.find(item => item.id === FALLBACK_AVATAR_ID) || AAA_AVATARS[0];
    }

    function getTeam(id) {
        return TEAMS.find(item => item.id === id) || TEAMS.find(item => item.id === FALLBACK_TEAM_ID) || TEAMS[0];
    }

    function getTheme(id) {
        return THEMES.find(item => item.id === id) || THEMES.find(item => item.id === FALLBACK_THEME_ID) || THEMES[0];
    }

    function getThemeTone(id) {
        const themeId = normalizeThemeId(id) || FALLBACK_THEME_ID;
        const rawTone = THEME_TONES[themeId] || THEME_TONES[FALLBACK_THEME_ID] || THEME_TONES.basic || CSS_THEME_COLOR_FALLBACKS;
        return normalizeThemeTone(rawTone);
    }

    function normalizeAvatarId(value) {
        const id = normalizeToken(value);
        return AAA_AVATARS.some(item => item.id === id) ? id : "";
    }

    function normalizeTeamId(value) {
        const id = normalizeToken(value);
        return TEAMS.some(item => item.id === id) ? id : "";
    }

    function normalizeThemeId(value) {
        const id = normalizeToken(value);
        return THEMES.some(item => item.id === id) ? id : "";
    }

    function normalizeViewId(value) {
        const id = normalizeToken(value);
        return VIEWS.some(item => item.id === id) ? id : "";
    }

    function normalizeWhitespace(value) {
        return String(value || "").replace(/\s+/g, " ").trim();
    }

    function normalizeToken(value) {
        return String(value || "").trim().toLowerCase().replace(/[^a-z0-9_-]/g, "").slice(0, 80);
    }

    function normalizeUsername(value) {
        return String(value || "").trim().replace(/[^a-zA-Z0-9_\-.]/g, "").slice(0, 60);
    }

    function normalizePlayerId(value) {
        const id = normalizeUsername(value).toLowerCase();
        return id && id !== "neopiano" ? id : "";
    }

    function extractQueryParam(href, key) {
        try {
            const url = new URL(href, location.origin);
            return url.searchParams.get(key) || "";
        } catch (_) {
            const match = String(href || "").match(new RegExp(`[?&]${key}=([^&]+)`));
            return match ? decodeURIComponent(match[1].replace(/\+/g, " ")) : "";
        }
    }


    function isSafeHexColor(value) {
        return CSS_SAFE_HEX_COLOR_RE.test(String(value || "").trim());
    }

    function safeHexColor(value, fallback) {
        const candidate = String(value || "").trim();
        const safeFallback = isSafeHexColor(fallback) ? String(fallback).trim() : "#000000";
        return isSafeHexColor(candidate) ? candidate : safeFallback;
    }

    function normalizeThemeTone(rawTone) {
        const source = isObject(rawTone) ? rawTone : {};
        const normalized = {};
        for (const key of CSS_THEME_COLOR_KEYS) {
            normalized[key] = safeHexColor(source[key], CSS_THEME_COLOR_FALLBACKS[key]);
        }
        return Object.freeze(normalized);
    }

    function buildThemeStyleAttribute(rawTone) {
        const tone = normalizeThemeTone(rawTone);
        return [
            `--aaa-primary:${tone.primary}`,
            `--aaa-primary-dark:${tone.dark}`,
            `--aaa-accent:${tone.accent}`,
            `--aaa-ink:${tone.text}`,
            `--aaa-soft:${tone.soft}`
        ].map(escapeAttribute).join("; ") + ";";
    }

    function createSessionId() {
        try {
            const random = Math.random().toString(36).slice(2, 10);
            return `ncc-aaa-session-${Date.now().toString(36)}-${random}`;
        } catch (_) {
            return `ncc-aaa-session-${Date.now()}`;
        }
    }

    function normalizeRevision(value) {
        const number = Number(value || 0);
        if (!Number.isFinite(number) || number < 0) return 0;
        return Math.floor(number);
    }

    function scheduleWriteJson(key, value, delay) {
        if (!key) return false;
        const waitMs = Math.max(0, Number(delay || SECONDARY_STORAGE_WRITE_DELAY_MS));
        const previousTimer = debouncedStorageWrites.get(key);
        if (previousTimer) clearTimeout(previousTimer);
        const payload = clonePlain(value);
        const timer = setTimeout(() => {
            writeJson(key, payload);
            debouncedStorageWrites.delete(key);
        }, waitMs);
        debouncedStorageWrites.set(key, timer);
        return true;
    }

    function flushScheduledStorageWrites() {
        for (const [key, timer] of debouncedStorageWrites.entries()) {
            clearTimeout(timer);
            debouncedStorageWrites.delete(key);
        }
    }

    function registerStorageSyncListener() {
        if (storageSyncListenerRegistered || typeof window === "undefined") return;
        storageSyncListenerRegistered = true;
        window.addEventListener("storage", handleStorageSyncEvent, false);
    }

    function handleStorageSyncEvent(event) {
        try {
            if (!event || event.key !== storageKey || !event.newValue) return;
            const incoming = JSON.parse(event.newValue);
            if (!isObject(incoming)) return;
            if (incoming.playerId !== identity?.playerId) return;
            if (incoming.sessionId && incoming.sessionId === SESSION_ID) return;

            const incomingRevision = normalizeRevision(incoming.revision);
            const localRevision = normalizeRevision(state?.revision);
            if (incomingRevision <= localRevision) return;

            const syncedAt = isoNow();
            state = hydrateStateForIdentity({
                ...incoming,
                sessionId: incoming.sessionId || "remote",
                lastCommitReason: "storage-sync",
                updatedAt: incoming.updatedAt || syncedAt
            }, identity, syncedAt, "storage-sync");
            lastStorageSyncAt = syncedAt;
            writeVisualPreferences(state, "storage-sync");
            publishExternalState("storage-sync");
            if (root) render({ reason: "storage-sync", forceFull: true });
        } catch (error) {
            bootErrors.push(`storage-sync:${errorToMessage(error)}`);
        }
    }

    function readJson(key, fallback) {
        try {
            const raw = localStorage.getItem(key);
            if (!raw) return fallback;
            return JSON.parse(raw);
        } catch (_) {
            return fallback;
        }
    }

    function writeJson(key, value) {
        try {
            localStorage.setItem(key, JSON.stringify(clonePlain(value)));
            return true;
        } catch (_) {
            return false;
        }
    }

    function readText(key, fallback) {
        try {
            const raw = localStorage.getItem(key);
            return raw == null ? fallback : String(raw);
        } catch (_) {
            return fallback;
        }
    }

    function writeText(key, value) {
        try {
            localStorage.setItem(key, String(value || ""));
            return true;
        } catch (_) {
            return false;
        }
    }

    function storageAvailable() {
        try {
            const key = "ncc.aaa.storage.test";
            localStorage.setItem(key, "1");
            localStorage.removeItem(key);
            return true;
        } catch (_) {
            return false;
        }
    }

    function clonePlain(value) {
        try {
            return JSON.parse(JSON.stringify(value));
        } catch (_) {
            return value;
        }
    }

    function isObject(value) {
        return Boolean(value && typeof value === "object" && !Array.isArray(value));
    }

    function isoNow() {
        return new Date().toISOString();
    }

    function errorToMessage(error) {
        return error?.message || String(error || "Erro desconhecido");
    }

    function escapeHtml(value) {
        return String(value == null ? "" : value)
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#39;");
    }

    function escapeAttribute(value) {
        return escapeHtml(value).replace(/`/g, "&#96;");
    }
})();