Sleazy Fork is available in English.

auto shield ... let's go

:joy:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         auto shield ... let's go
// @namespace    aa
// @version      39.0.0
// @description  :joy:
// @license      MI
// @match        *://agmaclone.xyz/*
// @match        *://*.agmaclone.xyz/*
// @run-at       document-start
// @grant        unsafeWindow
// ==/UserScript==

(() => {
  'use strict';

  const PAGE = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
  const KEY = '__AGMACLONE_NATIVE_DEFENSE_V38__';

  if (PAGE[KEY]) return;

  const C = {
    autoShield: true,
    autoAntiFreeze: true,

    shieldDelayMs: 0,
    antiFreezeDelayMs: 0,

    shieldRetryMs: 20,
    shieldRetryMax: Infinity,
    shieldRetryCheckMs: 20,
    shieldRetryMinSendGapMs: 100,
    shieldRetryCooldownSafetyMs: 12,
    antiFreezeRetryMs: 90,
    antiFreezeRetryMax: 1,

    antiFreezePacketDirectEnabled: true,
    antiFreezeFastCacheMaxAgeMs: 220,

    statePollMs: 0,

    wsSyncMinMs: 0,
    wsNodeSyncDelayMs: 0,

    uiUpdateMinMs: 140,

    selfDropMaxCellAgeMs: 1800,
    selfDropFailClosed: true,

    shieldWatchdogEnabled: true,
    shieldWatchdogGraceMs: 45,
    shieldWatchdogRetryGapMs: 900,
    shieldPendingStaleMs: 650,

    shieldFastCacheMaxAgeMs: 220,
    shieldRetryScheduleMs: [],
    shieldTimingHistoryMax: 30,

    shieldHotkeyCode: '',
    shieldHotkeyStopGameKey: true,

    fastShieldHotkeyCode: '',
    fastShieldHotkeyStopGameKey: true,
    fastShieldIntervalMs: 50,

    pushBurstEnabled: true,
    pushBurstHotkeyCode: 'Digit3',
    pushBurstCount: 15,
    pushBurstIntervalMs: 10.5,
    pushBurstStopGameKey: true,

    growthHotkeyCode: '',
    growthHotkeyStopGameKey: true,
    growthAmount: 30,

    autoPelletEnabled: false,
    autoPelletAmount: 30,
    autoPelletIntervalMs: 5000,
    autoPelletWorldX: null,
    autoPelletWorldY: null,
    autoPelletSetMouseHotkeyCode: '',
    autoPelletSetMouseHotkeyStopGameKey: true,

    comboHotkeyCode: '',
    comboHotkeyStopGameKey: true,
    comboAtomicFirstStage: false,
    comboSecondPortalExtraMs: 25,
    comboTargetMaxCellAgeMs: 1800,
    comboSecondPortalMinDelayMs: 12,
    comboQueueMax: 3,
    comboSecondPortalWsRetryMs: 100,
    comboSecondPortalWsRetryMax: 10,

    comboPortalConfirmPollMs: 12,
    comboPortalConfirmTimeoutMs: 900,
    comboPortalRetryMax: 2,
    comboPortalRetryExtraMs: 90,

    comboSecondPortalOffsetEnabled: true,
    comboSecondPortalOffsetMin: 60,
    comboSecondPortalOffsetSizeFactor: 0.45,
    comboSecondPortalOffsetMax: 240,

    manualMouseFreshMs: 30000,

    autoViewAntiRec: true,
    autoViewFreeze: true,

    viewTargetMode: 'mouse-hover-center',
    viewCenterDropOnly: true,
    viewScanMs: 0,
    viewRange: 6800,
    viewMaxCellAgeMs: 1800,
    viewMinEnemySize: 1,
    viewMouseTargetExtraRadius: 80,

    viewFreezeCloseExtra: 900,
    viewFreezeCloseMinDist: 1700,

    viewAimDotMin: 0.0,
    viewAimClosingMin: 0.01,
    viewAimMinSpeed: 0.01,
    viewAimLineExtra: 2400,
    viewAimLineForwardMin: 0,
    viewPredictGain: 0,
    viewPredictMs: 1300,
    viewAttackPredictMs: 850,
    viewAttackCloseMargin: 900,
    viewAttackPredictMargin: 1450,
    viewRushHitMargin: 700,
    viewRushMinSpeed: 0.01,
    viewRushMinClosing: 0.01,

    viewEmergencyMaxDist: 6200,
    viewEmergencySpeed: 0.01,
    viewEmergencyClosing: 0.01,
    viewEmergencyAimDot: 0.01,

    viewForwardDropEnabled: false,
    viewForwardDropBase: 120,
    viewForwardDropSizeFactor: 0.75,
    viewForwardDropSpeedFactor: 0.015,
    viewForwardDropMin: 140,
    viewForwardDropMax: 850,


    viewAntiRecFallbackLockMs: 460,
    viewFreezeFallbackLockMs: 2900,

    viewAntiRecPerPlayerLockMs: 460,
    viewFreezePerPlayerLockMs: 2900,
    antiRecContinuousPerPlayer: true,
    antiRecLoopIdleMs: 30,
    antiRecLoopFailureRetryMs: 40,
    antiRecLoopMinTimerMs: 1,

    autoRecEnabled: false,
    autoRecIntervalMs: 1,

    shieldBreakEnabled: false,
    shieldBreakPlayerId: 0,
    shieldBreakRetryMs: 35,

    targetAutoFreezeEnabled: false,
    targetAutoAntiRecEnabled: false,
    targetAntiRecIntervalMs: 460,
    targetActionRetryMs: 35,

    panel: true,
    panelMinimized: false,
    debug: false,
    storageKey: '__AGMACLONE_NATIVE_DEFENSE_V32_PREFS__'
  };

  const S = {
    ws: null,
    nativeSend: null,
    seenSockets: new WeakSet(),

    PacketIds: null,
    EntityTypes: null,
    PlayerFlags: null,
    mapsReady: false,
    mapsError: '',

    mainPlayerId: 0,
    lastAnimations: null,
    lastOwnCount: 0,

    fastOwnCell: null,
    fastOwnCellAt: 0,
    lastShieldClearPacketAt: 0,
    lastShieldReactionMs: null,
    lastShieldReactionUs: null,

    shieldTiming: {
      lastOnAtPerf: 0,
      lastOffAtPerf: 0,
      lastSendAtPerf: 0,
      lastOnAtWall: 0,
      lastOffAtWall: 0,
      lastSendAtWall: 0,

      lastClearToSendMs: null,
      lastSendToOnMs: null,
      lastClearToOnMs: null,
      lastVisibleDurationMs: null,
      lastUnshieldedGapMs: null,
      lastSendIntervalMs: null,
      lastClearIntervalMs: null,
      lastOnIntervalMs: null,

      sendsThisCycle: 0,
      cyclesCompleted: 0,
      lastSendReason: '',
      pendingCycle: null,
      history: []
    },

    lastVerifiedSelfCell: null,
    selfDropBlocks: 0,

    shieldMissingSince: 0,
    shieldLastWatchdogKickAt: 0,
    shieldWatchdogRecoveries: 0,
    shieldPendingCreatedAt: 0,
    lastShieldWatchdogReason: '',

    shieldCycle: 0,
    shieldAttempts: 0,
    shieldPending: null,
    lastShieldSendAt: 0,

    freezeCycle: 0,
    antiFreezeAttempts: 0,
    antiFreezePending: null,
    lastAntiFreezeSendAt: 0,
    lastAntiFreezeFrozenPacketAt: 0,
    lastAntiFreezeReactionMs: null,
    lastAntiFreezeReactionUs: null,
    antiFreezePacketDirectSends: 0,
    antiFreezePacketDirectFallbacks: 0,

    pushBurstRunning: false,
    pushBurstTimer: null,
    pushBurstToken: 0,
    pushBursts: 0,
    pushPacketsSent: 0,
    lastPushBurst: null,

    fastShieldRunning: false,
    fastShieldTimer: null,
    fastShieldAttempts: 0,
    fastShieldSends: 0,
    lastFastShield: null,

    hotkeyCapture: null,
    hotkeyCaptureStartedAt: 0,
    hotkeyMessage: '',

    mouseWorld: null,
    lastNativeMousePacket: null,
    renderTransform: {
      scale: 0,
      nodeX: 0,
      nodeY: 0,
      canvasWidth: 0,
      canvasHeight: 0,
      updatedAt: 0
    },
    outgoingPacketsObserved: 0,
    manualItemLastSend: Object.create(null),

    growthSends: 0,
    lastGrowthSend: null,

    autoPelletTimer: null,
    autoPelletRuns: 0,
    autoPelletPackets: 0,
    autoPelletTotal: 0,
    lastAutoPellet: null,

    comboRunning: false,
    comboToken: 0,
    comboSecondPortalTimer: null,
    comboQueue: [],
    comboSends: 0,
    comboBlocks: 0,
    lastComboSend: null,
    lastComboBlock: null,

    powerCooldowns: Object.create(null),
    inventoryCounts: Object.create(null),
    inventoryPreviousCounts: Object.create(null),
    inventoryItemUpdatedAt: Object.create(null),
    inventoryItemUpdateSeq: Object.create(null),
    inventoryUpdateSeq: 0,
    unlimitedInventory: false,
    inventoryKnown: false,
    viewMotion: new Map(),
    viewGlobalLocks: new Map(),
    viewTargetLocks: new Map(),
    lastViewScanAt: 0,
    viewScans: 0,
    viewDrops: 0,
    viewBlocks: 0,
    lastViewTarget: null,
    lastViewDrop: null,
    lastViewBlock: null,
    viewRoundRobin: {
      antiRec: 0,
      freeze: 0
    },
    visibleEnemyOwnerIds: [],
    visibleEnemyCount: 0,
    visibleEnemyUpdatedAt: 0,

    antiRecLoopTimer: null,
    antiRecLoopDueAt: 0,
    antiRecLoopRuns: 0,
    antiRecLoopDrops: 0,
    antiRecLoopLastRunAt: 0,
    antiRecLoopLastSource: '',
    antiRecLoopLastVisibleCount: 0,
    antiRecLoopLastSentOwners: [],

    autoRecTimer: null,
    autoRecDueAt: 0,
    autoRecRuns: 0,
    autoRecSends: 0,
    autoRecLastSendAt: 0,
    autoRecLastSource: '',

    shieldBreakTargetId: 0,
    shieldBreakLastAnimations: null,
    shieldBreakShieldOn: false,
    shieldBreakPortalSent: false,
    shieldBreakTimer: null,
    shieldBreakAttempts: 0,
    shieldBreakSends: 0,
    shieldBreakDetectedAt: 0,
    shieldBreakLastSend: null,
    shieldBreakLastBlock: null,
    shieldBreakPlayerListSignature: '',

    targetFreezeTimer: null,
    targetFreezeSends: 0,
    targetFreezeAttempts: 0,
    targetFreezeLastSend: null,
    targetFreezeLastBlock: null,

    targetAntiRecTimer: null,
    targetAntiRecSends: 0,
    targetAntiRecAttempts: 0,
    targetAntiRecLastSend: null,
    targetAntiRecLastBlock: null,

    lastDrop: null,
    lastBlock: null,
    lastPacketOp: -1,
    messages: 0,
    socketsSeen: 0,

    early: {
      installed: false,
      captures: 0,
      errors: []
    },

    panel: null,
    poll: null,

    syncTimer: null,
    syncLastAt: 0,
    syncPendingSource: '',
    syncRuns: 0,
    syncRequests: 0,

    uiUpdateTimer: null,
    uiLastUpdateAt: 0,
    uiUpdates: 0,
    uiRequests: 0
  };

  function log(...args) {
    if (C.debug) console.log('[AC Shield Until Effect v37]', ...args);
  }

  function now() {
    return Date.now();
  }

  function loadPrefs() {
    try {
      const raw =
        localStorage.getItem(C.storageKey) ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V31_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V30_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V29_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V28_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V27_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V26_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V25_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V24_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V23_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V22_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V21_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V20_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V19_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V18_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V17_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V16_PREFS__') ||
        localStorage.getItem('__AGMACLONE_NATIVE_DEFENSE_V10_PREFS__') ||
        '{}';
      const p = JSON.parse(raw);
      if (typeof p.autoShield === 'boolean') C.autoShield = p.autoShield;
      if (typeof p.autoAntiFreeze === 'boolean') C.autoAntiFreeze = p.autoAntiFreeze;
      if (typeof p.autoViewAntiRec === 'boolean') C.autoViewAntiRec = p.autoViewAntiRec;
      if (typeof p.autoViewFreeze === 'boolean') C.autoViewFreeze = p.autoViewFreeze;
      if (typeof p.autoRecEnabled === 'boolean') C.autoRecEnabled = p.autoRecEnabled;
      if (typeof p.shieldBreakEnabled === 'boolean') C.shieldBreakEnabled = p.shieldBreakEnabled;
      if (typeof p.targetAutoFreezeEnabled === 'boolean') C.targetAutoFreezeEnabled = p.targetAutoFreezeEnabled;
      if (typeof p.targetAutoAntiRecEnabled === 'boolean') C.targetAutoAntiRecEnabled = p.targetAutoAntiRecEnabled;
      if (Number.isFinite(Number(p.shieldBreakPlayerId))) {
        const id = Math.trunc(Number(p.shieldBreakPlayerId));
        C.shieldBreakPlayerId = id >= 1 && id <= 65535 ? id : 0;
      }
      if (typeof p.panelMinimized === 'boolean') C.panelMinimized = p.panelMinimized;
      if (typeof p.autoPelletEnabled === 'boolean') C.autoPelletEnabled = p.autoPelletEnabled;
      if (p.autoPelletWorldX !== null && p.autoPelletWorldX !== '' && Number.isFinite(Number(p.autoPelletWorldX))) C.autoPelletWorldX = Number(p.autoPelletWorldX);
      if (p.autoPelletWorldY !== null && p.autoPelletWorldY !== '' && Number.isFinite(Number(p.autoPelletWorldY))) C.autoPelletWorldY = Number(p.autoPelletWorldY);
      if (typeof p.autoPelletSetMouseHotkeyCode === 'string') C.autoPelletSetMouseHotkeyCode = p.autoPelletSetMouseHotkeyCode;
      if (typeof p.shieldHotkeyCode === 'string') C.shieldHotkeyCode = p.shieldHotkeyCode;
      if (typeof p.fastShieldHotkeyCode === 'string') C.fastShieldHotkeyCode = p.fastShieldHotkeyCode;
      if (typeof p.pushBurstHotkeyCode === 'string' && p.pushBurstHotkeyCode) {
        C.pushBurstHotkeyCode = p.pushBurstHotkeyCode;
      }
      if (typeof p.growthHotkeyCode === 'string') {
        C.growthHotkeyCode = p.growthHotkeyCode;
      }
      if (typeof p.comboHotkeyCode === 'string') {
        C.comboHotkeyCode = p.comboHotkeyCode;
      }
    } catch (_) {}
  }

  function savePrefs() {
    try {
      localStorage.setItem(C.storageKey, JSON.stringify({
        autoShield: !!C.autoShield,
        autoAntiFreeze: !!C.autoAntiFreeze,
        autoViewAntiRec: !!C.autoViewAntiRec,
        autoViewFreeze: !!C.autoViewFreeze,
        autoRecEnabled: !!C.autoRecEnabled,
        shieldBreakEnabled: !!C.shieldBreakEnabled,
        targetAutoFreezeEnabled: !!C.targetAutoFreezeEnabled,
        targetAutoAntiRecEnabled: !!C.targetAutoAntiRecEnabled,
        shieldBreakPlayerId: Number(C.shieldBreakPlayerId) || 0,
        panelMinimized: !!C.panelMinimized,
        autoPelletEnabled: !!C.autoPelletEnabled,
        autoPelletWorldX: Number.isFinite(Number(C.autoPelletWorldX)) ? Number(C.autoPelletWorldX) : null,
        autoPelletWorldY: Number.isFinite(Number(C.autoPelletWorldY)) ? Number(C.autoPelletWorldY) : null,
        autoPelletSetMouseHotkeyCode: String(C.autoPelletSetMouseHotkeyCode || ''),
        shieldHotkeyCode: String(C.shieldHotkeyCode || ''),
        fastShieldHotkeyCode: String(C.fastShieldHotkeyCode || ''),
        pushBurstHotkeyCode: String(C.pushBurstHotkeyCode || 'Digit3'),
        growthHotkeyCode: String(C.growthHotkeyCode || ''),
        comboHotkeyCode: String(C.comboHotkeyCode || '')
      }));
    } catch (_) {}
  }

  loadPrefs();

  async function fetchJson(path) {
    const fn = PAGE.fetch || fetch;
    const response = await Reflect.apply(fn, PAGE, [path, { cache: 'no-store' }]);
    if (!response.ok) throw new Error(`${path}: HTTP ${response.status}`);
    return await response.json();
  }

  async function loadNativeMaps() {
    try {
      const [packetIds, entityTypes, playerFlags] = await Promise.all([
        fetchJson('/share/PacketIds.json'),
        fetchJson('/share/EntityTypes.json'),
        fetchJson('/share/PlayerFlags.json')
      ]);

      S.PacketIds = packetIds;
      S.EntityTypes = entityTypes;
      S.PlayerFlags = playerFlags;
      S.mapsReady = true;
      S.mapsError = '';

      log('native maps loaded', { packetIds, entityTypes, playerFlags });
      updatePanel();
      return true;
    } catch (e) {
      S.mapsReady = false;
      S.mapsError = e && e.message ? e.message : String(e);
      updatePanel();
      return false;
    }
  }

  function packetBuffer(data) {
    if (data instanceof ArrayBuffer) return data;
    if (ArrayBuffer.isView(data)) {
      return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
    }
    if (data && data.data instanceof ArrayBuffer) return data.data;
    return null;
  }

  function readUtf8Zero(dv, state) {
    const bytes = [];
    while (state.o < dv.byteLength) {
      const b = dv.getUint8(state.o++);
      if (b === 0) break;
      bytes.push(b);
    }
    try {
      return new TextDecoder('utf-8').decode(new Uint8Array(bytes));
    } catch (_) {
      return '';
    }
  }

  function parseHeader(dv) {
    let offset = 0;
    if (dv.byteLength >= 6 && dv.getUint8(0) === 240) offset = 5;
    if (offset >= dv.byteLength) return null;
    return { op: dv.getUint8(offset++), offset };
  }

  function parseInitPlayer(dv, offset) {
    if (offset + 2 > dv.byteLength) return;
    const nextId = dv.getUint16(offset, true);
    if (Number(nextId) !== Number(S.mainPlayerId)) {
      S.mainPlayerId = nextId;
      resetDefenseState('init-player-changed');
    } else {
      S.mainPlayerId = nextId;
    }
    log('main player id', S.mainPlayerId);
  }

  function parseInitServerCooldowns(dv, offset) {
    const E = S.EntityTypes;
    if (!E || offset >= dv.byteLength) return;

    // New protocol: serverConfig byte, then 13 cooldown bytes.
    // MOTHERCELL was inserted before PORTAL in the updated client.
    const serverConfig = dv.getUint8(offset++);
    S.unlimitedInventory = !!(serverConfig & 1);

    const order = [
      E.RECOMBINE,
      E.VIRUS,
      E.MOTHERCELL,
      E.PORTAL,
      E.SPEED,
      E.ANTIRECOMBINE,
      E.ANTIFREEZE,
      E.SHIELD,
      E.FREEZE,
      E.GOLDBLOCK,
      E.PUSH,
      E.GROWTH,
      E.FROZENVIRUS
    ];

    for (const item of order) {
      if (offset >= dv.byteLength) break;
      const raw = dv.getUint8(offset++);
      if (Number.isFinite(Number(item))) {
        S.powerCooldowns[Number(item)] = raw * 10.5;
      }
    }
  }

  function parseUpdateInventory(dv, offset) {
    while (offset + 5 <= dv.byteLength) {
      const item = Number(dv.getUint8(offset++));
      const count = Number(dv.getUint32(offset, true));
      offset += 4;

      const previous = Number(S.inventoryCounts[item]);

      if (Number.isFinite(previous)) {
        S.inventoryPreviousCounts[item] = previous;
      }

      S.inventoryCounts[item] = count;
      S.inventoryUpdateSeq++;
      S.inventoryItemUpdateSeq[item] = S.inventoryUpdateSeq;
      S.inventoryItemUpdatedAt[item] = now();
      S.inventoryKnown = true;
    }
  }

  function parseUpdatePlayers(dv, offset, receivedAtPerf = performance.now()) {
    const F = S.PlayerFlags;
    if (!F) return;

    const st = { o: offset };

    while (st.o + 3 <= dv.byteLength) {
      const id = dv.getUint16(st.o, true);
      st.o += 2;
      const flags = dv.getUint8(st.o++);

      if (flags & F.NICKNAME_CHANGED) readUtf8Zero(dv, st);
      if (flags & F.COLOR_CHANGED) st.o += 3;
      if (flags & F.NICKNAME_COLOR_CHANGED) st.o += 3;
      if (flags & F.NICKNAME_STROKE_COLOR_CHANGED) st.o += 3;
      if (flags & F.SKIN_CHANGED) readUtf8Zero(dv, st);

      if (flags & F.BADGES_CHANGED) {
        if (st.o >= dv.byteLength) return;
        const count = dv.getUint8(st.o++);
        st.o += count;
      }

      if (flags & F.USERNAME_CHANGED) readUtf8Zero(dv, st);

      if (flags & F.ANIMATIONS_CHANGED) {
        if (st.o >= dv.byteLength) return;
        const animations = dv.getUint8(st.o++);

        if (animations & F.ANIMATION_SHIELD) st.o += 1;
        if (animations & F.ANIMATION_PUSH) st.o += 4;

        if (id === S.mainPlayerId) {
          processAnimationState(animations, 'packet-update-players', {
            packetDirect: true,
            packetAt: receivedAtPerf
          });
        }

        if (Number(id) === Number(C.shieldBreakPlayerId)) {
          processShieldBreakAnimation(id, animations, 'packet-update-players');
          processTargetIdAnimation(id, animations, 'packet-update-players');
        }
      }

      if (st.o > dv.byteLength) return;
    }
  }

  function onGameMessage(event, ws) {
    const receivedAtPerf = performance.now();
    const buffer = packetBuffer(event && event.data);
    if (!buffer || buffer.byteLength < 1) return;

    S.messages++;

    try {
      const dv = new DataView(buffer);
      const h = parseHeader(dv);
      if (!h) return;

      S.lastPacketOp = h.op;

      if (!S.mapsReady || !S.PacketIds) {
        // The maps normally load before the server begins sending gameplay packets.
        return;
      }

      let syncSource = '';
      let syncDelayMs = 0;

      if (h.op === S.PacketIds.INIT_PLAYER) {
        parseInitPlayer(dv, h.offset);
        syncSource = 'ws-init-player';
      } else if (h.op === S.PacketIds.INIT_SERVER) {
        parseInitServerCooldowns(dv, h.offset);
        syncSource = 'ws-init-server';
      } else if (h.op === S.PacketIds.UPDATE_PLAYERS) {
        // Shield and Anti-Freeze are already handled synchronously above.
        // The coalesced sync only refreshes caches/watchdog/View afterward.
        parseUpdatePlayers(dv, h.offset, receivedAtPerf);
        syncSource = 'ws-update-players';
      } else if (
        Number.isFinite(Number(S.PacketIds.UPDATE_INVENTORY)) &&
        h.op === Number(S.PacketIds.UPDATE_INVENTORY)
      ) {
        parseUpdateInventory(dv, h.offset);
        updatePanel();
      } else if (
        Number.isFinite(Number(S.PacketIds.UPDATE_NODES)) &&
        h.op === Number(S.PacketIds.UPDATE_NODES)
      ) {
        // UPDATE_NODES is frequent. Never perform a full scan for every packet.
        syncSource = 'ws-update-nodes';
        {
          const configuredDelay = Number(C.wsNodeSyncDelayMs);
          syncDelayMs = Math.max(
            0,
            Number.isFinite(configuredDelay) ? configuredDelay : 0
          );
        }
      } else if (
        Number.isFinite(Number(S.PacketIds.REMOVED_NODES)) &&
        h.op === Number(S.PacketIds.REMOVED_NODES)
      ) {
        syncSource = 'ws-removed-nodes';
        {
          const configuredDelay = Number(C.wsNodeSyncDelayMs);
          syncDelayMs = Math.max(
            0,
            Number.isFinite(configuredDelay) ? configuredDelay : 0
          );
        }
      }

      if (syncSource) {
        // Event listeners finish before the microtask, allowing main_out.js to
        // apply PAGE.players/PAGE.cells first.
        queueMicrotask(() => scheduleClientSync(syncSource, syncDelayMs));
      }
    } catch (e) {
      S.lastBlock = {
        reason: 'message-parse-error',
        detail: e && e.message ? e.message : String(e),
        t: now()
      };
    }
  }

  function updateWorldMouseFromScreen(screenX, screenY, source = 'screen-mouse') {
    const tr = S.renderTransform;
    const scale = Number(tr && tr.scale);
    const width = Number(tr && tr.canvasWidth) || innerWidth;
    const height = Number(tr && tr.canvasHeight) || innerHeight;
    const nodeX = Number(tr && tr.nodeX);
    const nodeY = Number(tr && tr.nodeY);

    if (
      !Number.isFinite(scale) || scale <= 0 ||
      !Number.isFinite(nodeX) || !Number.isFinite(nodeY)
    ) return false;

    const x = (Number(screenX) - width / 2) / scale + nodeX;
    const y = (Number(screenY) - height / 2) / scale + nodeY;
    if (!Number.isFinite(x) || !Number.isFinite(y)) return false;

    S.mouseWorld = { x: x | 0, y: y | 0, source, t: now() };
    return true;
  }

  function installRenderTransformCapture() {
    try {
      const proto = PAGE.CanvasRenderingContext2D && PAGE.CanvasRenderingContext2D.prototype;
      if (!proto || proto.__AC_RENDER_CAPTURE_V38__) return true;

      const nativeScale = proto.scale;
      const nativeTranslate = proto.translate;
      const state = new WeakMap();

      proto.scale = function(x, y) {
        try {
          if (this && this.canvas && this.canvas.id === 'canvas') {
            const rec = state.get(this) || {};
            rec.scale = Number(x);
            rec.sawScale = true;
            rec.canvasWidth = Number(this.canvas.width) || innerWidth;
            rec.canvasHeight = Number(this.canvas.height) || innerHeight;
            state.set(this, rec);
          }
        } catch (_) {}
        return Reflect.apply(nativeScale, this, [x, y]);
      };

      proto.translate = function(x, y) {
        try {
          if (this && this.canvas && this.canvas.id === 'canvas') {
            const rec = state.get(this) || {};
            if (rec.sawScale && Number(x) < 0 && Number(y) < 0) {
              S.renderTransform = {
                scale: Number(rec.scale) || 0,
                nodeX: -Number(x),
                nodeY: -Number(y),
                canvasWidth: Number(this.canvas.width) || innerWidth,
                canvasHeight: Number(this.canvas.height) || innerHeight,
                updatedAt: now()
              };
              rec.sawScale = false;
            }
            state.set(this, rec);
          }
        } catch (_) {}
        return Reflect.apply(nativeTranslate, this, [x, y]);
      };

      Object.defineProperty(proto, '__AC_RENDER_CAPTURE_V38__', { value: true });

      PAGE.addEventListener('mousemove', event => {
        updateWorldMouseFromScreen(event.clientX, event.clientY, 'dom-mouse-render-transform');
      }, true);
      return true;
    } catch (e) {
      S.lastBlock = { reason: 'render-transform-hook-failed', detail: String(e), t: now() };
      return false;
    }
  }

  function observeOutgoingPacket(data) {
    const buffer = packetBuffer(data);
    if (!buffer || buffer.byteLength < 1 || !S.PacketIds) return;

    try {
      const dv = new DataView(buffer);
      const op = dv.getUint8(0);
      const P = S.PacketIds;
      const E = S.EntityTypes || {};
      S.outgoingPacketsObserved++;

      // New client MOUSE packet: [opcode, normalizedScreenX, normalizedScreenY].
      // Actual world coordinates are reconstructed from the latest render transform
      // captured from the game canvas.
      if (
        Number.isFinite(Number(P.MOUSE)) &&
        op === Number(P.MOUSE) &&
        dv.byteLength >= 3
      ) {
        const bx = dv.getUint8(1);
        const by = dv.getUint8(2);
        S.lastNativeMousePacket = new Uint8Array([Number(P.MOUSE) & 255, bx, by]);
        const sx = bx / 255 * innerWidth;
        const sy = by / 255 * innerHeight;
        updateWorldMouseFromScreen(sx, sy, 'native-outgoing-mouse-v2');
        return;
      }

      // New drop packets contain only their own opcode (+ optional Growth amount).
      const pairs = [
        [E.RECOMBINE, P.DROP_RECOMBINE],
        [E.SPEED, P.DROP_SPEED],
        [E.VIRUS, P.DROP_VIRUS],
        [E.MOTHERCELL, P.DROP_MOTHERCELL],
        [E.PORTAL, P.DROP_PORTAL],
        [E.SHIELD, P.DROP_SHIELD],
        [E.FREEZE, P.DROP_FREEZE],
        [E.GROWTH, P.DROP_GROWTH],
        [E.ANTIRECOMBINE, P.DROP_ANTIRECOMBINE],
        [E.ANTIFREEZE, P.DROP_ANTIFREEZE],
        [E.FROZENVIRUS, P.DROP_FROZENVIRUS],
        [E.GOLDBLOCK, P.DROP_GOLDBLOCK]
      ];

      for (const [item, packetId] of pairs) {
        if (!Number.isFinite(Number(item)) || !Number.isFinite(Number(packetId))) continue;
        if (op !== Number(packetId)) continue;
        S.manualItemLastSend[Number(item)] = now();
        return;
      }
    } catch (_) {}
  }

  function attachSocket(ws, source = 'unknown') {
    if (!ws || S.seenSockets.has(ws)) return ws;

    S.seenSockets.add(ws);
    S.socketsSeen++;
    S.ws = ws;

    try { ws.binaryType = 'arraybuffer'; } catch (_) {}

    ws.addEventListener('open', () => {
      S.ws = ws;
      resetDefenseState('ws-open');
      updatePanel();
    });

    ws.addEventListener('message', event => onGameMessage(event, ws));

    ws.addEventListener('close', () => {
      if (S.ws === ws) S.ws = null;
      resetDefenseState('ws-close');
      updatePanel();
    });

    if (ws.readyState === 1) S.ws = ws;

    log('socket attached', source, ws.url);
    updatePanel();
    return ws;
  }

  function installEarlySocketCapture() {
    try {
      const WS = PAGE.WebSocket;
      const proto = WS && WS.prototype;
      if (!WS || !proto) throw new Error('WebSocket unavailable');

      S.nativeSend = proto.send;

      // Observe the original client's outgoing packets. Script-originated
      // packets use S.nativeSend directly and are recorded explicitly below.
      if (!proto.send.__AC_TARGET_OBSERVER_V18__) {
        const originalSend = S.nativeSend;
        const wrappedSend = function(data) {
          observeOutgoingPacket(data);
          return Reflect.apply(originalSend, this, [data]);
        };

        Object.defineProperty(wrappedSend, '__AC_TARGET_OBSERVER_V18__', {
          value: true
        });
        proto.send = wrappedSend;
      }

      const capture = (ws, source) => {
        S.early.captures++;
        attachSocket(ws, source);
      };

      for (const prop of ['onopen', 'onmessage', 'onclose', 'onerror']) {
        try {
          const descriptor = Object.getOwnPropertyDescriptor(proto, prop);
          if (!descriptor || typeof descriptor.get !== 'function' || typeof descriptor.set !== 'function') {
            S.early.errors.push(`${prop}: descriptor missing`);
            continue;
          }

          const get = descriptor.get;
          const set = descriptor.set;

          Object.defineProperty(proto, prop, {
            configurable: true,
            enumerable: descriptor.enumerable,
            get() {
              return Reflect.apply(get, this, []);
            },
            set(value) {
              capture(this, `property-${prop}`);
              return Reflect.apply(set, this, [value]);
            }
          });
        } catch (e) {
          S.early.errors.push(`${prop}: ${e && e.message ? e.message : String(e)}`);
        }
      }

      // Constructor Proxy is a fallback. Property capture remains the main path.
      if (!WS.__AC_NATIVE_DEFENSE_PROXY__) {
        const Wrapped = new Proxy(WS, {
          construct(Target, args, NewTarget) {
            const socket = Reflect.construct(Target, args, NewTarget === Wrapped ? Target : NewTarget);
            return attachSocket(socket, 'constructor-proxy');
          }
        });

        Object.setPrototypeOf(Wrapped, WS);
        Wrapped.prototype = WS.prototype;
        Object.defineProperty(Wrapped, '__AC_NATIVE_DEFENSE_PROXY__', { value: true });
        PAGE.WebSocket = Wrapped;
      }

      S.early.installed = true;
      return true;
    } catch (e) {
      S.early.errors.push(e && e.message ? e.message : String(e));
      return false;
    }
  }

  function pagePlayers() {
    return PAGE.players && typeof PAGE.players === 'object' ? PAGE.players : null;
  }

  function pageCells() {
    return PAGE.cells && typeof PAGE.cells === 'object' ? PAGE.cells : null;
  }

  function ownCells() {
    const cells = pageCells();
    const E = S.EntityTypes;
    if (!cells || !E || !S.mainPlayerId) return [];

    const out = [];

    for (const key of Object.keys(cells)) {
      const c = cells[key];
      if (!c || c.destroyed) continue;
      if (Number(c.owner) !== Number(S.mainPlayerId)) continue;
      if (Number(c.type) !== Number(E.PLAYER)) continue;

      // nx/ny are the newest server-target coordinates. x/y are visually
      // interpolated and can lag behind while the cell is moving quickly.
      const x = Number.isFinite(c.nx) ? c.nx : c.x;
      const y = Number.isFinite(c.ny) ? c.ny : c.y;
      const size = Number.isFinite(c.nSize)
        ? c.nSize
        : Number.isFinite(c.originalSize)
          ? c.originalSize
          : c.size;

      if (!Number.isFinite(x) || !Number.isFinite(y)) continue;

      out.push({
        raw: c,
        id: Number(c.id) || Number(key) || 0,
        x,
        y,
        size: Number(size) || 0
      });
    }

    out.sort((a, b) => b.size - a.size);

    if (out[0]) {
      S.fastOwnCell = {
        raw: out[0].raw,
        id: out[0].id,
        owner: Number(out[0].raw && out[0].raw.owner) || Number(S.mainPlayerId),
        x: out[0].x,
        y: out[0].y,
        size: out[0].size,
        source: 'cached-live-own-nx-ny'
      };
      S.fastOwnCellAt = now();
    }

    return out;
  }

  function strictLiveOwnDropCell() {
    const player = mainPlayer();
    const E = S.EntityTypes;
    const playerType = E && Number(E.PLAYER);

    if (
      !player ||
      !player.cells ||
      typeof player.cells[Symbol.iterator] !== 'function' ||
      !S.mainPlayerId ||
      !Number.isFinite(playerType)
    ) {
      return null;
    }

    const t = now();
    const candidates = [];

    for (const c of player.cells) {
      if (!c || c.destroyed) continue;
      if (Number(c.owner) !== Number(S.mainPlayerId)) continue;
      if (Number(c.type) !== playerType) continue;

      // Use the latest server coordinates, never mouse coordinates and never a
      // visually interpolated position unless nx/ny are unavailable.
      const x = Number.isFinite(c.nx) ? Number(c.nx) : Number(c.x);
      const y = Number.isFinite(c.ny) ? Number(c.ny) : Number(c.y);
      const size = Number.isFinite(c.nSize)
        ? Number(c.nSize)
        : Number.isFinite(c.originalSize)
          ? Number(c.originalSize)
          : Number(c.size);

      if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(size) || size <= 0) {
        continue;
      }

      const updatedAt = Number(c.updateTime || c.time || 0);
      const ageMs = updatedAt > 0 ? Math.max(0, t - updatedAt) : 0;

      if (
        updatedAt > 0 &&
        C.selfDropMaxCellAgeMs > 0 &&
        ageMs > C.selfDropMaxCellAgeMs
      ) {
        continue;
      }

      candidates.push({
        raw: c,
        id: Number(c.id) || 0,
        owner: Number(c.owner) || 0,
        x,
        y,
        size,
        ageMs,
        source: Number.isFinite(c.nx) && Number.isFinite(c.ny)
          ? 'live-own-nx-ny'
          : 'live-own-x-y-fallback'
      });
    }

    if (!candidates.length) return null;

    // Largest cell is the safest target because its center has the widest
    // tolerance and cannot accidentally land outside the player's body.
    candidates.sort((a, b) => (b.size - a.size) || (a.ageMs - b.ageMs));
    const best = candidates[0];

    S.lastVerifiedSelfCell = {
      id: best.id,
      owner: best.owner,
      x: best.x | 0,
      y: best.y | 0,
      size: Math.round(best.size),
      ageMs: Math.round(best.ageMs),
      source: best.source,
      t
    };

    S.fastOwnCell = {
      raw: best.raw,
      id: best.id,
      owner: best.owner,
      x: best.x,
      y: best.y,
      size: best.size,
      ageMs: best.ageMs,
      source: best.source
    };
    S.fastOwnCellAt = t;

    return best;
  }

  function fastOwnCell(maxAgeMs = C.shieldFastCacheMaxAgeMs) {
    const cached = S.fastOwnCell;
    const raw = cached && cached.raw;
    const E = S.EntityTypes;
    const t = now();

    // O(1) hot path: validate the cached raw cell and read its newest nx/ny.
    if (
      cached &&
      raw &&
      !raw.destroyed &&
      Number(raw.owner) === Number(S.mainPlayerId) &&
      E &&
      Number(raw.type) === Number(E.PLAYER) &&
      (!S.fastOwnCellAt || t - S.fastOwnCellAt <= Math.max(0, Number(maxAgeMs) || 0))
    ) {
      const x = Number.isFinite(Number(raw.nx)) ? Number(raw.nx) : Number(raw.x);
      const y = Number.isFinite(Number(raw.ny)) ? Number(raw.ny) : Number(raw.y);
      const size = Number.isFinite(Number(raw.nSize))
        ? Number(raw.nSize)
        : Number.isFinite(Number(raw.originalSize))
          ? Number(raw.originalSize)
          : Number(raw.size);

      const updatedAt = Number(raw.updateTime || raw.time || 0);
      const ageMs = updatedAt > 0 ? Math.max(0, t - updatedAt) : 0;

      if (
        Number.isFinite(x) &&
        Number.isFinite(y) &&
        Number.isFinite(size) &&
        size > 0 &&
        (
          !updatedAt ||
          !C.selfDropMaxCellAgeMs ||
          ageMs <= C.selfDropMaxCellAgeMs
        )
      ) {
        const verified = {
          raw,
          id: Number(raw.id) || Number(cached.id) || 0,
          owner: Number(raw.owner) || 0,
          x,
          y,
          size,
          ageMs,
          source: 'fast-cache-live-own-nx-ny'
        };

        S.lastVerifiedSelfCell = {
          id: verified.id,
          owner: verified.owner,
          x: verified.x | 0,
          y: verified.y | 0,
          size: Math.round(verified.size),
          ageMs: Math.round(verified.ageMs),
          source: verified.source,
          t
        };

        return verified;
      }
    }

    // Safe fallback if cache is absent/stale.
    return strictLiveOwnDropCell();
  }

  function mainPlayer() {
    const players = pagePlayers();
    return players && S.mainPlayerId ? players[S.mainPlayerId] : null;
  }

  function liveAnimations() {
    const player = mainPlayer();
    return player && Number.isFinite(Number(player.animations))
      ? Number(player.animations)
      : null;
  }

  function flagsFromAnimations(animations) {
    const F = S.PlayerFlags || {};
    animations = Number(animations) || 0;

    return {
      animations,
      shield: !!(animations & Number(F.ANIMATION_SHIELD || 0)),
      frozen: !!(animations & Number(F.ANIMATION_FROZEN || 0)),
      antiFreeze: !!(animations & Number(F.ANIMATION_ANTI_FREEZE || 0))
    };
  }

  function cancelTimer(name) {
    if (S[name]) {
      clearTimeout(S[name]);
      S[name] = null;
    }
    if (name === 'shieldPending') S.shieldPendingCreatedAt = 0;
  }

  function dropPacketIdForItem(item) {
    const P = S.PacketIds || {};
    const E = S.EntityTypes || {};
    item = Number(item);

    const map = new Map([
      [Number(E.RECOMBINE), Number(P.DROP_RECOMBINE)],
      [Number(E.SPEED), Number(P.DROP_SPEED)],
      [Number(E.VIRUS), Number(P.DROP_VIRUS)],
      [Number(E.MOTHERCELL), Number(P.DROP_MOTHERCELL)],
      [Number(E.PORTAL), Number(P.DROP_PORTAL)],
      [Number(E.SHIELD), Number(P.DROP_SHIELD)],
      [Number(E.FREEZE), Number(P.DROP_FREEZE)],
      [Number(E.GROWTH), Number(P.DROP_GROWTH)],
      [Number(E.ANTIRECOMBINE), Number(P.DROP_ANTIRECOMBINE)],
      [Number(E.ANTIFREEZE), Number(P.DROP_ANTIFREEZE)],
      [Number(E.FROZENVIRUS), Number(P.DROP_FROZENVIRUS)],
      [Number(E.GOLDBLOCK), Number(P.DROP_GOLDBLOCK)]
    ]);

    const id = map.get(item);
    return Number.isFinite(id) ? id & 255 : null;
  }

  function buildDropPacket(item, x, y, amount = null) {
    // Updated protocol: every droppable power has its own opcode.
    // The server uses the latest MOUSE packet for position, so X/Y are NOT
    // included in the drop packet anymore. Growth optionally carries amount.
    const id = dropPacketIdForItem(item);
    if (id == null) throw new Error(`drop opcode missing for item ${item}`);

    const hasAmount = Number.isFinite(Number(amount));
    if (hasAmount) {
      return new Uint8Array([
        id,
        Math.max(0, Math.min(255, Number(amount) | 0))
      ]);
    }
    return new Uint8Array([id]);
  }


  function worldPointToMousePacket(x, y) {
    const P = S.PacketIds || {};
    const tr = S.renderTransform;
    const scale = Number(tr && tr.scale);
    const width = Number(tr && tr.canvasWidth) || innerWidth;
    const height = Number(tr && tr.canvasHeight) || innerHeight;
    const nodeX = Number(tr && tr.nodeX);
    const nodeY = Number(tr && tr.nodeY);

    if (
      !Number.isFinite(Number(P.MOUSE)) ||
      !Number.isFinite(scale) || scale <= 0 ||
      !Number.isFinite(width) || width <= 0 ||
      !Number.isFinite(height) || height <= 0 ||
      !Number.isFinite(nodeX) || !Number.isFinite(nodeY) ||
      !Number.isFinite(Number(x)) || !Number.isFinite(Number(y))
    ) {
      return null;
    }

    const screenX = (Number(x) - nodeX) * scale + width / 2;
    const screenY = (Number(y) - nodeY) * scale + height / 2;

    if (!Number.isFinite(screenX) || !Number.isFinite(screenY)) return null;

    // Same quantization as the updated native client:
    // byte = screenCoordinate / canvasSize * 255.
    const bx = Math.max(0, Math.min(255, Math.floor(screenX / width * 255)));
    const by = Math.max(0, Math.min(255, Math.floor(screenY / height * 255)));

    return new Uint8Array([Number(P.MOUSE) & 255, bx, by]);
  }

  function currentRealMousePacket() {
    const P = S.PacketIds || {};

    if (
      S.lastNativeMousePacket instanceof Uint8Array &&
      S.lastNativeMousePacket.length >= 3 &&
      Number.isFinite(Number(P.MOUSE)) &&
      S.lastNativeMousePacket[0] === (Number(P.MOUSE) & 255)
    ) {
      return new Uint8Array(S.lastNativeMousePacket);
    }

    const mouse = currentWorldMouse();
    return mouse ? worldPointToMousePacket(mouse.x, mouse.y) : null;
  }

  function sendTargetedPowerPacket(item, x, y, amount = null, reason = 'targeted-power') {
    if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) return false;

    const targetMousePacket = worldPointToMousePacket(x, y);
    if (!targetMousePacket) {
      S.lastBlock = {
        reason: 'target-mouse-transform-not-ready',
        detail: reason,
        item: Number(item),
        x: Number(x),
        y: Number(y),
        t: now()
      };
      return false;
    }

    const restoreMousePacket = currentRealMousePacket();
    const dropPacket = buildDropPacket(item, x, y, amount);

    try {
      // Protocol v2 no longer carries X/Y inside the power packet. The server
      // uses its latest MOUSE state, so temporarily point the server mouse at
      // the requested world target, drop, then restore the user's real mouse.
      Reflect.apply(S.nativeSend, S.ws, [targetMousePacket]);
      Reflect.apply(S.nativeSend, S.ws, [dropPacket]);
      if (restoreMousePacket) {
        Reflect.apply(S.nativeSend, S.ws, [restoreMousePacket]);
      }
      return true;
    } catch (e) {
      S.lastBlock = {
        reason: 'targeted-power-send-failed',
        detail: reason,
        item: Number(item),
        x: Number(x),
        y: Number(y),
        error: e && e.message ? e.message : String(e),
        t: now()
      };
      return false;
    }
  }

  function currentWorldMouse() {
    const m = S.mouseWorld;
    if (!m) return null;

    const ageMs = now() - Number(m.t || 0);
    if (
      ageMs < 0 ||
      ageMs > Math.max(0, Number(C.manualMouseFreshMs) || 30000) ||
      !Number.isFinite(Number(m.x)) ||
      !Number.isFinite(Number(m.y))
    ) {
      return null;
    }

    return {
      x: Number(m.x) | 0,
      y: Number(m.y) | 0,
      ageMs,
      source: m.source || 'unknown'
    };
  }

  function comboReferencePoint() {
    const mouse = currentWorldMouse();
    if (mouse) return mouse;

    // When the panel button is clicked before a recent native MOUSE packet is
    // available, use the user's largest verified own-cell as a safe reference.
    const own = strictLiveOwnDropCell();
    if (!own) return null;

    return {
      x: own.x | 0,
      y: own.y | 0,
      ageMs: 0,
      source: 'largest-live-own-cell-fallback'
    };
  }

  function livePlayerCellsForManualTarget(includeOwn = true) {
    const players = pagePlayers();
    const E = S.EntityTypes;
    const playerType = E && Number(E.PLAYER);
    const t = now();
    const rows = [];

    if (!players || !Number.isFinite(playerType)) return rows;

    for (const key of Object.keys(players)) {
      const player = players[key];
      const owner = Number(player && player.id);

      if (!player || !Number.isFinite(owner)) continue;
      if (!includeOwn && owner === Number(S.mainPlayerId)) continue;
      if (!player.cells || typeof player.cells[Symbol.iterator] !== 'function') continue;

      for (const c of player.cells) {
        if (!c || c.destroyed) continue;
        if (Number(c.owner) !== owner) continue;
        if (Number(c.type) !== playerType) continue;

        const x = Number.isFinite(Number(c.nx)) ? Number(c.nx) : Number(c.x);
        const y = Number.isFinite(Number(c.ny)) ? Number(c.ny) : Number(c.y);
        const size = Number.isFinite(Number(c.nSize))
          ? Number(c.nSize)
          : Number.isFinite(Number(c.originalSize))
            ? Number(c.originalSize)
            : Number(c.size);

        const updatedAt = Number(c.updateTime || c.time || 0);
        const ageMs = updatedAt > 0 ? Math.max(0, t - updatedAt) : 0;

        if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
        if (!Number.isFinite(size) || size <= 0) continue;
        if (
          updatedAt > 0 &&
          C.comboTargetMaxCellAgeMs > 0 &&
          ageMs > C.comboTargetMaxCellAgeMs
        ) {
          continue;
        }

        rows.push({
          raw: c,
          player,
          id: Number(c.id) || 0,
          owner,
          x,
          y,
          size,
          ageMs
        });
      }
    }

    return rows;
  }

  function pickNearestOwnCellToMouse(mouse) {
    if (!mouse || !S.mainPlayerId) return null;

    let best = null;
    let bestDist2 = Infinity;

    for (const cell of livePlayerCellsForManualTarget(true)) {
      // Strict own-cell-only guard. Never allow another player's cell.
      if (Number(cell.owner) !== Number(S.mainPlayerId)) continue;

      const dx = cell.x - mouse.x;
      const dy = cell.y - mouse.y;
      const dist2 = dx * dx + dy * dy;

      if (
        dist2 < bestDist2 ||
        (
          dist2 === bestDist2 &&
          (!best || cell.size > best.size)
        )
      ) {
        best = cell;
        bestDist2 = dist2;
      }
    }

    if (!best) return null;

    return {
      ...best,
      mouseDistance: Math.sqrt(bestDist2),
      selection: 'nearest-live-own-cell-to-mouse',
      ownCellOnly: true
    };
  }

  function pickNearestOpponentCellToMouse(mouse) {
    if (!mouse || !S.mainPlayerId) return null;

    let best = null;
    let bestDist2 = Infinity;

    for (const cell of livePlayerCellsForManualTarget(false)) {
      if (Number(cell.owner) === Number(S.mainPlayerId)) continue;

      const dx = Number(cell.x) - Number(mouse.x);
      const dy = Number(cell.y) - Number(mouse.y);
      const dist2 = dx * dx + dy * dy;

      if (
        dist2 < bestDist2 ||
        (
          dist2 === bestDist2 &&
          best &&
          Number(cell.ageMs || 0) < Number(best.ageMs || 0)
        )
      ) {
        best = cell;
        bestDist2 = dist2;
      }
    }

    if (!best) return null;

    return {
      ...best,
      mouseDistance: Math.sqrt(bestDist2),
      opponentNearestDistance: Math.sqrt(bestDist2),
      selection: 'nearest-live-enemy-cell-to-mouse',
      nearestCellOnly: true
    };
  }

  function largestLiveCellForOwner(owner) {
    owner = Number(owner);
    if (!Number.isFinite(owner) || owner === Number(S.mainPlayerId)) return null;

    const candidates = livePlayerCellsForManualTarget(false)
      .filter(cell => Number(cell.owner) === owner)
      .sort((a, b) => (b.size - a.size) || (a.ageMs - b.ageMs));

    return candidates[0] || null;
  }

  function manualItemCooldownMs(item) {
    const learned = Number(S.powerCooldowns[Number(item)]);
    if (Number.isFinite(learned) && learned >= 0) return learned;
    return 1000;
  }

  function manualItemWaitMs(item) {
    const lastAt = Number(S.manualItemLastSend[Number(item)] || 0);
    const cooldownMs = manualItemCooldownMs(item);

    if (!lastAt || cooldownMs <= 0) return 0;
    return Math.max(0, cooldownMs - (now() - lastAt));
  }

  function recordManualItemSend(item) {
    S.manualItemLastSend[Number(item)] = now();
  }

  function sendManualDrop(item, target, reason, amount = null, ignoreKnownCooldown = false) {
    if (!S.mapsReady || !S.PacketIds || !S.EntityTypes) {
      S.lastBlock = { reason: 'native-maps-not-ready', detail: reason, t: now() };
      return false;
    }

    if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
      S.lastBlock = { reason: 'ws-not-ready-manual-drop', detail: reason, t: now() };
      return false;
    }

    if (
      !target ||
      !Number.isFinite(Number(target.x)) ||
      !Number.isFinite(Number(target.y))
    ) {
      S.lastBlock = { reason: 'manual-target-not-ready', detail: reason, t: now() };
      return false;
    }

    const waitMs = manualItemWaitMs(item);
    if (!ignoreKnownCooldown && waitMs > 0) {
      S.lastBlock = {
        reason: 'manual-item-known-cooldown',
        detail: reason,
        item: Number(item),
        waitMs,
        t: now()
      };
      return false;
    }

    try {
      const ok = sendTargetedPowerPacket(
        item,
        Number(target.x),
        Number(target.y),
        amount,
        reason
      );
      if (!ok) return false;
      recordManualItemSend(item);
      return true;
    } catch (e) {
      S.lastBlock = {
        reason: 'manual-drop-send-failed',
        detail: reason,
        item: Number(item),
        error: e && e.message ? e.message : String(e),
        t: now()
      };
      return false;
    }
  }

  function sendGrowth30Shield(reason = 'growth30-shield-no-mouse') {
    const E = S.EntityTypes;
    const P = S.PacketIds;

    if (!S.mapsReady || !E || !P || !S.nativeSend) {
      S.lastBlock = { reason: 'growth30-shield-runtime-not-ready', detail: reason, t: now() };
      updatePanel();
      return false;
    }

    if (!S.ws || S.ws.readyState !== 1) {
      S.lastBlock = { reason: 'growth30-shield-ws-not-ready', detail: reason, t: now() };
      updatePanel();
      return false;
    }

    const growthItem = Number(E.GROWTH);
    const shieldItem = Number(E.SHIELD);

    if (!Number.isFinite(growthItem) || !Number.isFinite(shieldItem)) {
      S.lastBlock = {
        reason: 'growth30-shield-item-map-missing',
        detail: reason,
        growthItem,
        shieldItem,
        t: now()
      };
      updatePanel();
      return false;
    }

    const growthAvailable = autoItemAvailable(growthItem);
    const shieldAvailable = autoItemAvailable(shieldItem);

    let growthOk = false;
    let shieldOk = false;

    try {
      // Deliberately NO mouse targeting:
      // - no currentWorldMouse()
      // - no worldPointToMousePacket()
      // - no temporary MOUSE packet
      // - no mouse restore
      // Only the two native power-drop packets below.
      if (growthAvailable) {
        const growthPacket = buildDropPacket(growthItem, 0, 0, 30);
        Reflect.apply(S.nativeSend, S.ws, [growthPacket]);
        recordManualItemSend(growthItem);
        growthOk = true;
      }

      if (shieldAvailable) {
        const shieldPacket = buildDropPacket(shieldItem, 0, 0, null);
        Reflect.apply(S.nativeSend, S.ws, [shieldPacket]);
        recordManualItemSend(shieldItem);
        shieldOk = true;
      }

      S.growthSends++;

      S.lastGrowthSend = {
        combo: 'growth30+shield',
        amount: 30,
        growthSent: growthOk,
        shieldSent: shieldOk,
        growthAvailable,
        shieldAvailable,
        mouseRead: false,
        mousePacketSent: false,
        mouseRestoreSent: false,
        aimed: false,
        target: null,
        reason,
        t: now()
      };

      S.lastDrop = {
        combo: 'growth30+shield',
        growthAmount: 30,
        growthSent: growthOk,
        shieldSent: shieldOk,
        coordinateSource: 'none-no-mouse',
        targetLocked: false,
        mouseTargeted: false,
        reason,
        t: now()
      };

      if (!growthOk && !shieldOk) {
        S.lastBlock = {
          reason: 'growth30-shield-inventory-empty',
          detail: reason,
          growthInventory: knownInventoryCount(growthItem),
          shieldInventory: knownInventoryCount(shieldItem),
          t: now()
        };
        updatePanel();
        return false;
      }

      updatePanel();
      return true;
    } catch (e) {
      S.lastBlock = {
        reason: 'growth30-shield-send-failed',
        detail: reason,
        error: e && e.message ? e.message : String(e),
        t: now()
      };
      updatePanel();
      return false;
    }
  }

  function configuredAutoPelletWorld() {
    const x = Number(C.autoPelletWorldX);
    const y = Number(C.autoPelletWorldY);
    if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
    return { x: x | 0, y: y | 0, ageMs: 0, source: 'configured-auto-pellet-world' };
  }

  function setAutoPelletWorld(x, y, source = 'manual-world-input') {
    const nx = Number(x);
    const ny = Number(y);
    if (!Number.isFinite(nx) || !Number.isFinite(ny)) return false;
    C.autoPelletWorldX = nx | 0;
    C.autoPelletWorldY = ny | 0;
    savePrefs();
    S.lastAutoPellet = {
      ok: true,
      configuredOnly: true,
      reason: 'auto-pellet-world-set',
      x: C.autoPelletWorldX,
      y: C.autoPelletWorldY,
      coordinateSource: source,
      t: now()
    };
    updatePanel(true);
    return { x: C.autoPelletWorldX, y: C.autoPelletWorldY };
  }

  function captureAutoPelletWorldFromMouse(source = 'capture-world-from-mouse') {
    const mouse = currentWorldMouse();
    if (!mouse) {
      S.lastAutoPellet = { ok: false, reason: 'world-mouse-not-ready', detail: source, t: now() };
      updatePanel(true);
      return false;
    }
    return setAutoPelletWorld(mouse.x, mouse.y, source);
  }

  function clearAutoPelletTimer() {
    if (S.autoPelletTimer) {
      clearTimeout(S.autoPelletTimer);
      S.autoPelletTimer = null;
    }
  }

  function scheduleAutoPellet(delayMs = C.autoPelletIntervalMs) {
    clearAutoPelletTimer();
    if (!C.autoPelletEnabled) return false;

    const delay = Math.max(0, Number(delayMs) || 0);
    S.autoPelletTimer = setTimeout(() => {
      S.autoPelletTimer = null;
      autoPelletTick('timer');
    }, Math.ceil(delay));
    return true;
  }

  function sendAutoPellet30(reason = 'auto-pellet-x30') {
    const E = S.EntityTypes;
    const fixedWorld = configuredAutoPelletWorld();
    S.autoPelletRuns++;

    if (!fixedWorld) {
      S.lastAutoPellet = {
        ok: false,
        reason: 'auto-pellet-world-not-set',
        detail: reason,
        t: now()
      };
      updatePanel();
      return false;
    }

    if (!E || !Number.isFinite(Number(E.GROWTH))) {
      S.lastAutoPellet = {
        ok: false,
        reason: 'growth-item-map-missing',
        detail: reason,
        t: now()
      };
      updatePanel();
      return false;
    }

    const item = Number(E.GROWTH);
    const target = {
      x: fixedWorld.x | 0,
      y: fixedWorld.y | 0,
      source: fixedWorld.source || 'configured-auto-pellet-world'
    };
    const amount = Math.max(1, Math.min(255, Number(C.autoPelletAmount) | 0));
    const ok = sendManualDrop(item, target, reason, amount);

    if (ok) {
      S.autoPelletPackets++;
      S.autoPelletTotal += amount;
      S.lastAutoPellet = {
        ok: true,
        amount,
        total: S.autoPelletTotal,
        packets: S.autoPelletPackets,
        x: target.x,
        y: target.y,
        mouseAgeMs: null,
        coordinateSource: target.source,
        fixedWorldTarget: true,
        reason,
        t: now()
      };
      S.lastDrop = {
        item,
        amount,
        x: target.x,
        y: target.y,
        mouseTargeted: true,
        autoPellet: true,
        reason,
        t: now()
      };
    } else {
      S.lastAutoPellet = {
        ok: false,
        reason: S.lastBlock && S.lastBlock.reason || 'send-blocked',
        detail: reason,
        x: target.x,
        y: target.y,
        t: now()
      };
    }

    updatePanel();
    return ok;
  }

  function autoPelletTick(source = 'timer') {
    if (!C.autoPelletEnabled) {
      clearAutoPelletTimer();
      return false;
    }

    const ok = sendAutoPellet30(`auto-pellet-${source}`);
    scheduleAutoPellet(C.autoPelletIntervalMs);
    return ok;
  }

  function setAutoPellet(value) {
    C.autoPelletEnabled = !!value;
    savePrefs();
    clearAutoPelletTimer();

    if (C.autoPelletEnabled) {
      // If X/Y are still empty, capture the current world mouse ONCE.
      if (!configuredAutoPelletWorld()) {
        const captured = captureAutoPelletWorldFromMouse('auto-pellet-enable-capture');
        if (!captured) {
          C.autoPelletEnabled = false;
          savePrefs();
          updatePanel(true);
          return false;
        }
      }
      // Later mouse movement does not change the Auto Pellet target.
      scheduleAutoPellet(0);
    }

    updatePanel(true);
    return C.autoPelletEnabled;
  }


  function portalInventoryCount() {
    const E = S.EntityTypes;
    if (!E || !Number.isFinite(Number(E.PORTAL))) return null;
    if (S.unlimitedInventory) return Infinity;

    const count = Number(S.inventoryCounts[Number(E.PORTAL)]);
    return Number.isFinite(count) ? count : null;
  }

  function livePortalNodeIds() {
    const cells = pageCells();
    const E = S.EntityTypes;
    const out = new Set();

    if (!cells || !E || !Number.isFinite(Number(E.PORTAL))) return out;

    for (const key of Object.keys(cells)) {
      const cell = cells[key];
      if (!cell || cell.destroyed) continue;
      if (Number(cell.type) !== Number(E.PORTAL)) continue;

      const id = Number(cell.id);
      out.add(Number.isFinite(id) && id ? id : String(key));
    }

    return out;
  }

  function countNewPortalNodes(baselineIds) {
    const current = livePortalNodeIds();
    let count = 0;

    for (const id of current) {
      if (!baselineIds.has(id)) count++;
    }

    return {
      count,
      current
    };
  }

  function portalAcceptanceEvidence(startInventory, baselineIds, requiredCount) {
    const currentInventory = portalInventoryCount();
    const nodeEvidence = countNewPortalNodes(baselineIds);

    const inventoryAccepted = (
      Number.isFinite(startInventory) &&
      Number.isFinite(currentInventory) &&
      currentInventory <= startInventory - requiredCount
    );

    const nodeAccepted = nodeEvidence.count >= requiredCount;

    return {
      accepted: inventoryAccepted || nodeAccepted,
      requiredCount,
      startInventory,
      currentInventory,
      inventoryDelta: (
        Number.isFinite(startInventory) &&
        Number.isFinite(currentInventory)
      ) ? startInventory - currentInventory : null,
      newPortalNodes: nodeEvidence.count,
      inventoryAccepted,
      nodeAccepted,
      t: now()
    };
  }

  function waitForPortalAcceptance(
    token,
    startInventory,
    baselineIds,
    requiredCount,
    timeoutMs,
    callback
  ) {
    const startedAt = now();
    const pollMs = Math.max(4, Number(C.comboPortalConfirmPollMs) || 12);

    const poll = () => {
      if (token !== S.comboToken) return;

      const evidence = portalAcceptanceEvidence(
        startInventory,
        baselineIds,
        requiredCount
      );

      if (evidence.accepted) {
        callback(true, evidence);
        return;
      }

      if (now() - startedAt >= timeoutMs) {
        callback(false, evidence);
        return;
      }

      S.comboSecondPortalTimer = setTimeout(poll, pollMs);
    };

    poll();
  }

  function targetWithSecondPortalOffset(target, reference) {
    if (!target || !C.comboSecondPortalOffsetEnabled) return target;

    const size = Math.max(0, Number(target.size) || 0);
    const maxInside = Math.max(0, size * 0.62);
    const requested = Math.max(
      Number(C.comboSecondPortalOffsetMin) || 60,
      size * (Number(C.comboSecondPortalOffsetSizeFactor) || 0.45)
    );
    const distance = Math.min(
      Number(C.comboSecondPortalOffsetMax) || 240,
      maxInside,
      requested
    );

    if (!(distance > 0)) return target;

    let dx = 0;
    let dy = 0;
    const raw = target.raw;

    if (raw) {
      const nx = Number(raw.nx);
      const ny = Number(raw.ny);
      const ox = Number(raw.ox);
      const oy = Number(raw.oy);

      if (
        Number.isFinite(nx) &&
        Number.isFinite(ny) &&
        Number.isFinite(ox) &&
        Number.isFinite(oy)
      ) {
        dx = nx - ox;
        dy = ny - oy;
      }
    }

    // If the opponent is stationary, offset away from the reference point.
    if (dx * dx + dy * dy < 1e-6 && reference) {
      dx = Number(target.x) - Number(reference.x);
      dy = Number(target.y) - Number(reference.y);
    }

    if (dx * dx + dy * dy < 1e-6) {
      dx = 1;
      dy = 0;
    }

    const length = Math.hypot(dx, dy) || 1;
    const ux = dx / length;
    const uy = dy / length;

    return {
      ...target,
      x: Number(target.x) + ux * distance,
      y: Number(target.y) + uy * distance,
      secondPortalOffset: distance,
      secondPortalOffsetX: ux * distance,
      secondPortalOffsetY: uy * distance
    };
  }

  function comboInventoryStatus(needsPortal = false) {
    const E = S.EntityTypes;

    if (!E) {
      return {
        known: false,
        enough: false,
        needsPortal: !!needsPortal,
        missing: ['entity-maps']
      };
    }

    if (S.unlimitedInventory) {
      return {
        known: true,
        unlimited: true,
        enough: true,
        needsPortal: !!needsPortal,
        portal: Infinity,
        virus: Infinity,
        antiRec: Infinity,
        freeze: Infinity,
        missing: []
      };
    }

    const portal = Number(S.inventoryCounts[Number(E.PORTAL)]);
    const virus = Number(S.inventoryCounts[Number(E.VIRUS)]);
    const antiRec = Number(S.inventoryCounts[Number(E.ANTIRECOMBINE)]);
    const freeze = Number(S.inventoryCounts[Number(E.FREEZE)]);
    const requiredValues = [virus, antiRec, freeze];
    if (needsPortal) requiredValues.push(portal);
    const known = requiredValues.every(Number.isFinite);

    if (!known) {
      return {
        known: false,
        unlimited: false,
        enough: true,
        needsPortal: !!needsPortal,
        portal: Number.isFinite(portal) ? portal : null,
        virus: Number.isFinite(virus) ? virus : null,
        antiRec: Number.isFinite(antiRec) ? antiRec : null,
        freeze: Number.isFinite(freeze) ? freeze : null,
        missing: []
      };
    }

    const missing = [];
    if (needsPortal && portal < 1) missing.push(`PORTAL need 1, have ${portal}`);
    if (virus < 1) missing.push(`VIRUS need 1, have ${virus}`);
    if (antiRec < 1) missing.push(`ANTI-REC need 1, have ${antiRec}`);
    if (freeze < 1) missing.push(`FREEZE need 1, have ${freeze}`);

    return {
      known: true,
      unlimited: false,
      enough: missing.length === 0,
      needsPortal: !!needsPortal,
      portal,
      virus,
      antiRec,
      freeze,
      missing
    };
  }

  function runNextQueuedCombo() {
    if (S.comboRunning || !S.comboQueue.length) return false;

    const next = S.comboQueue.shift();
    setTimeout(() => {
      sendTargetedCombo(next.reason || 'queued-targeted-combo');
    }, 0);

    return true;
  }

  function finishComboCycle() {
    S.comboRunning = false;
    S.comboSecondPortalTimer = null;
    updatePanel();
    runNextQueuedCombo();
  }

  function cancelTargetedCombo(message = '') {
    S.comboToken++;

    if (S.comboSecondPortalTimer) {
      clearTimeout(S.comboSecondPortalTimer);
      S.comboSecondPortalTimer = null;
    }

    S.comboRunning = false;
    S.comboQueue.length = 0;

    if (message) {
      S.lastComboBlock = {
        reason: message,
        t: now()
      };
    }

    updatePanel();
    return true;
  }

  function sendTargetedCombo(reason = 'smart-drop') {
    if (S.comboRunning) {
      if (S.comboQueue.length >= Math.max(1, Number(C.comboQueueMax) || 3)) {
        S.comboBlocks++;
        S.lastComboBlock = {
          reason: 'smart-drop-queue-full',
          detail: reason,
          queueLength: S.comboQueue.length,
          t: now()
        };
        updatePanel();
        return false;
      }

      S.comboQueue.push({ reason, t: now() });
      S.hotkeyMessage = `Smart Drop queued (${S.comboQueue.length})`;
      updatePanel();
      return true;
    }

    const E = S.EntityTypes;
    const reference = comboReferencePoint();

    if (!reference) {
      S.comboBlocks++;
      S.lastComboBlock = {
        reason: 'smart-drop-reference-not-ready',
        detail: reason,
        hint: 'Move the mouse in-game or spawn an own cell first',
        t: now()
      };
      updatePanel();
      return false;
    }

    if (
      !E ||
      !Number.isFinite(Number(E.VIRUS)) ||
      !Number.isFinite(Number(E.ANTIRECOMBINE)) ||
      !Number.isFinite(Number(E.FREEZE))
    ) {
      S.comboBlocks++;
      S.lastComboBlock = {
        reason: 'smart-drop-item-map-missing',
        detail: reason,
        t: now()
      };
      updatePanel();
      return false;
    }

    const selected = pickNearestOpponentCellToMouse(reference);

    if (!selected) {
      S.comboBlocks++;
      S.lastComboBlock = {
        reason: 'no-live-opponent-smart-drop',
        detail: reason,
        reference,
        t: now()
      };
      updatePanel();
      return false;
    }

    if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
      S.comboBlocks++;
      S.lastComboBlock = {
        reason: 'ws-not-ready-smart-drop',
        detail: reason,
        t: now()
      };
      updatePanel();
      return false;
    }

    const token = ++S.comboToken;
    S.comboRunning = true;
    S.lastComboBlock = null;

    // Keep the exact enemy cell nearest to the mouse.
    // Never replace it with the owner's largest cell.
    const latestTarget = selected;
    const effectsAtSend = playerEffectsByOwner(selected.owner);
    const usePortal = !!effectsAtSend.shield;

    if (usePortal && !Number.isFinite(Number(E.PORTAL))) {
      S.comboBlocks++;
      S.lastComboBlock = {
        reason: 'smart-drop-portal-map-missing',
        detail: reason,
        targetOwner: selected.owner,
        effectsAtSend,
        t: now()
      };
      S.lastBlock = S.lastComboBlock;
      finishComboCycle();
      return false;
    }

    const inventory = comboInventoryStatus(usePortal);

    if (inventory.known && !inventory.enough) {
      S.comboBlocks++;
      S.lastComboBlock = {
        reason: 'smart-drop-insufficient-inventory',
        detail: reason,
        targetOwner: selected.owner,
        usePortal,
        inventory,
        t: now()
      };
      S.lastBlock = S.lastComboBlock;
      S.hotkeyMessage = inventory.missing.join(' · ');
      finishComboCycle();
      return false;
    }

    const requestedOrder = usePortal
      ? ['PORTAL', 'ANTIRECOMBINE', 'FREEZE', 'VIRUS']
      : ['ANTIRECOMBINE', 'FREEZE', 'VIRUS'];

    const comboRecord = {
      smartDrop: true,
      shieldAtSend: usePortal,
      effectsAtSend,
      requestedOrder,
      sentOrder: [],
      packetsSent: 0,
      portalPacketsSent: 0,
      portalAcceptedCount: 0,
      firstStagePacketsSent: 0,
      targetOwner: latestTarget.owner,
      targetId: latestTarget.id,
      targetX: latestTarget.x | 0,
      targetY: latestTarget.y | 0,
      targetSize: Math.round(latestTarget.size || 0),
      opponentNearestDistance: Math.round(selected.opponentNearestDistance || selected.mouseDistance || 0),
      nearestCellMouseDistance: Math.round(selected.mouseDistance || 0),
      targetSelection: selected.selection || 'nearest-live-enemy-cell-to-mouse',
      referenceX: reference.x,
      referenceY: reference.y,
      referenceSource: reference.source,
      inventoryAtStart: inventory,
      reason,
      startedAt: now(),
      completedAt: 0
    };

    S.lastComboSend = comboRecord;
    S.comboSends++;

    const failSmartDrop = (blockReason, extra = {}) => {
      S.comboBlocks++;
      S.lastComboBlock = {
        reason: blockReason,
        detail: reason,
        targetOwner: latestTarget.owner,
        shieldAtSend: usePortal,
        sentOrder: comboRecord.sentOrder.slice(),
        ...extra,
        t: now()
      };
      S.lastBlock = S.lastComboBlock;
      finishComboCycle();
      return false;
    };

    if (token !== S.comboToken) {
      return failSmartDrop('smart-drop-cancelled-before-send');
    }

    // Shield is checked immediately before this branch. Portal is never sent
    // when the target's live Shield animation is OFF.
    if (usePortal) {
      const portalOk = sendManualDrop(
        E.PORTAL,
        latestTarget,
        `${reason}-shield-portal`,
        null,
        true
      );

      if (!portalOk) return failSmartDrop('smart-drop-portal-send-failed');

      comboRecord.sentOrder.push('PORTAL');
      comboRecord.portalPacketsSent = 1;
      comboRecord.packetsSent++;
    }

    const antiRecOk = sendManualDrop(
      E.ANTIRECOMBINE,
      latestTarget,
      `${reason}-antirecombine`,
      null,
      true
    );

    if (!antiRecOk) return failSmartDrop('smart-drop-anti-rec-send-failed');

    comboRecord.sentOrder.push('ANTIRECOMBINE');
    comboRecord.packetsSent++;

    const freezeOk = sendManualDrop(
      E.FREEZE,
      latestTarget,
      `${reason}-freeze`,
      null,
      true
    );

    if (!freezeOk) return failSmartDrop('smart-drop-freeze-send-failed');

    comboRecord.sentOrder.push('FREEZE');
    comboRecord.packetsSent++;

    const virusOk = sendManualDrop(
      E.VIRUS,
      latestTarget,
      `${reason}-virus`,
      null,
      true
    );

    if (!virusOk) return failSmartDrop('smart-drop-virus-send-failed');

    comboRecord.sentOrder.push('VIRUS');
    comboRecord.packetsSent++;
    comboRecord.firstStagePacketsSent = comboRecord.packetsSent;
    comboRecord.completedAt = now();

    S.lastDrop = {
      item: usePortal
        ? 'SMART:PORTAL+ANTIRECOMBINE+FREEZE+VIRUS'
        : 'SMART:ANTIRECOMBINE+FREEZE+VIRUS',
      x: latestTarget.x | 0,
      y: latestTarget.y | 0,
      targetId: latestTarget.id,
      targetOwner: latestTarget.owner,
      targetLocked: true,
      targetSelection: 'nearest-live-enemy-cell-to-mouse',
      mouseDistance: Math.round(selected.mouseDistance || 0),
      shieldAtSend: usePortal,
      effectsAtSend,
      reason,
      t: now()
    };

    S.hotkeyMessage = usePortal
      ? 'Smart: nearest cell → Portal + Anti-Rec + Freeze + Virus'
      : 'Smart: nearest cell → Anti-Rec + Freeze + Virus';

    finishComboCycle();
    return true;
  }

  function sendNativeUint8(op, reason = 'native-u8') {
    if (!S.ws || S.ws.readyState !== 1) {
      S.lastBlock = { reason: 'ws-not-open', detail: reason, t: now() };
      return false;
    }

    if (!S.nativeSend) {
      S.lastBlock = { reason: 'native-send-missing', detail: reason, t: now() };
      return false;
    }

    if (!Number.isFinite(Number(op))) {
      S.lastBlock = { reason: 'invalid-native-op', detail: reason, op, t: now() };
      return false;
    }

    try {
      Reflect.apply(S.nativeSend, S.ws, [new Uint8Array([Number(op) & 255])]);
      return true;
    } catch (e) {
      S.lastBlock = {
        reason: 'native-u8-send-failed',
        detail: reason,
        error: e && e.message ? e.message : String(e),
        t: now()
      };
      return false;
    }
  }

  function sendPushBurst(count = C.pushBurstCount, reason = 'hotkey-push15') {
    if (!C.pushBurstEnabled || S.pushBurstRunning) return false;

    const P = S.PacketIds;
    const op = P && Number(P.USE_PUSH);
    const burstCount = Math.max(1, Math.min(200, Number(count) | 0));
    const intervalMs = Math.max(0, Number(C.pushBurstIntervalMs) || 10.5);

    if (!S.mapsReady || !Number.isFinite(op)) {
      S.lastBlock = {
        reason: 'use-push-op-missing',
        detail: reason,
        t: now()
      };
      updatePanel();
      return false;
    }

    if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
      S.lastBlock = {
        reason: 'push-burst-ws-not-ready',
        detail: reason,
        t: now()
      };
      updatePanel();
      return false;
    }

    S.pushBurstRunning = true;
    S.pushBurstToken++;
    const token = S.pushBurstToken;
    const startedAt = performance.now();

    let sent = 0;
    let error = '';

    const finish = () => {
      if (token !== S.pushBurstToken) return;

      if (S.pushBurstTimer) {
        clearTimeout(S.pushBurstTimer);
        S.pushBurstTimer = null;
      }

      const endedAt = performance.now();

      S.pushBurstRunning = false;
      S.pushBursts++;
      S.pushPacketsSent += sent;
      S.lastPushBurst = {
        requested: burstCount,
        sent,
        op,
        intervalMs,
        reason,
        elapsedMs: Math.max(0, endedAt - startedAt),
        error,
        t: now()
      };

      updatePanel();
    };

    const sendNext = () => {
      if (token !== S.pushBurstToken) return;

      if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
        error = 'ws-closed-during-burst';
        S.lastBlock = {
          reason: error,
          detail: reason,
          sent,
          t: now()
        };
        finish();
        return;
      }

      try {
        Reflect.apply(S.nativeSend, S.ws, [new Uint8Array([op & 255])]);
        sent++;
      } catch (e) {
        error = e && e.message ? e.message : String(e);
        S.lastBlock = {
          reason: 'push-burst-send-failed',
          detail: reason,
          sent,
          error,
          t: now()
        };
        finish();
        return;
      }

      if (sent >= burstCount) {
        finish();
        return;
      }

      S.pushBurstTimer = setTimeout(sendNext, intervalMs);
    };

    // First Push is immediate; the remaining 14 are spaced by 10.5 ms.
    sendNext();
    return true;
  }

  function isTypingTarget(target) {
    if (!target) return false;

    const tag = String(target.tagName || '').toUpperCase();
    return (
      tag === 'INPUT' ||
      tag === 'TEXTAREA' ||
      tag === 'SELECT' ||
      !!target.isContentEditable ||
      !!target.closest?.('[contenteditable="true"]')
    );
  }

  function hotkeyLabel(code) {
    code = String(code || '');
    if (!code) return 'SET';

    if (/^Key[A-Z]$/.test(code)) return code.slice(3);
    if (/^Digit[0-9]$/.test(code)) return code.slice(5);
    if (/^Numpad[0-9]$/.test(code)) return `Num${code.slice(6)}`;
    if (/^F([1-9]|1[0-2])$/.test(code)) return code;

    const labels = {
      Space: 'SPACE',
      Enter: 'ENTER',
      Tab: 'TAB',
      Escape: 'ESC',
      Backspace: 'BACK',
      Delete: 'DEL',
      ArrowUp: '↑',
      ArrowDown: '↓',
      ArrowLeft: '←',
      ArrowRight: '→',
      ShiftLeft: 'L-SHIFT',
      ShiftRight: 'R-SHIFT',
      ControlLeft: 'L-CTRL',
      ControlRight: 'R-CTRL',
      AltLeft: 'L-ALT',
      AltRight: 'R-ALT',
      BracketLeft: '[',
      BracketRight: ']',
      Semicolon: ';',
      Quote: "'",
      Comma: ',',
      Period: '.',
      Slash: '/',
      Backslash: '\\',
      Minus: '-',
      Equal: '=',
      Backquote: '`'
    };

    return labels[code] || code;
  }

  function isModifierOnlyCode(code) {
    return (
      code === 'ShiftLeft' ||
      code === 'ShiftRight' ||
      code === 'ControlLeft' ||
      code === 'ControlRight' ||
      code === 'AltLeft' ||
      code === 'AltRight' ||
      code === 'MetaLeft' ||
      code === 'MetaRight'
    );
  }

  function hotkeyMatches(event, code) {
    return !!(
      event &&
      code &&
      String(event.code || '') === String(code)
    );
  }

  function stopHotkeyEvent(event) {
    if (!event) return;
    event.preventDefault();
    event.stopPropagation();
    event.stopImmediatePropagation();
  }


  function fastShieldDropOnce(reason = 'fast-shield-main-cell') {
    S.fastShieldAttempts++;

    const E = S.EntityTypes;
    const item = E && Number(E.SHIELD);

    if (!Number.isFinite(item)) {
      S.lastBlock = { reason: 'fast-shield-item-id-missing', detail: reason, t: now() };
      return false;
    }

    if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
      S.lastBlock = { reason: 'fast-shield-ws-not-ready', detail: reason, t: now() };
      return false;
    }

    // If inventory is known and empty, fail closed instead of sending useless packets.
    if (!autoItemAvailable(item)) {
      S.lastBlock = {
        reason: 'fast-shield-inventory-empty',
        detail: reason,
        inventory: knownInventoryCount(item),
        t: now()
      };
      return false;
    }

    // strictLiveOwnDropCell() filters owner === mainPlayerId, PLAYER type only,
    // then sorts by size descending. candidates[0] is therefore the current
    // main/largest own cell, never a secondary/smaller own cell and never mouse.
    const cell = strictLiveOwnDropCell();
    if (!cell) {
      S.lastBlock = { reason: 'fast-shield-main-cell-missing', detail: reason, t: now() };
      return false;
    }

    const ok = sendTargetedPowerPacket(
      item,
      cell.x | 0,
      cell.y | 0,
      null,
      reason
    );

    if (!ok) return false;

    S.fastShieldSends++;
    S.lastFastShield = {
      cellId: Number(cell.id) || 0,
      owner: Number(cell.owner) || Number(S.mainPlayerId) || 0,
      x: cell.x | 0,
      y: cell.y | 0,
      size: Math.round(Number(cell.size) || 0),
      intervalMs: Math.max(1, Number(C.fastShieldIntervalMs) || 50),
      target: 'largest-live-own-cell',
      mouseTargetUsed: false,
      t: now()
    };

    S.lastDrop = {
      item,
      x: cell.x | 0,
      y: cell.y | 0,
      cellId: Number(cell.id) || 0,
      owner: Number(cell.owner) || Number(S.mainPlayerId) || 0,
      cellSize: Math.round(Number(cell.size) || 0),
      coordinateSource: 'fast-shield-largest-own-cell',
      selfLocked: true,
      reason,
      t: now()
    };

    return true;
  }

  function stopFastShieldDrop(reason = 'release') {
    if (S.fastShieldTimer) {
      clearInterval(S.fastShieldTimer);
      S.fastShieldTimer = null;
    }

    const wasRunning = S.fastShieldRunning;
    S.fastShieldRunning = false;

    if (wasRunning) {
      S.hotkeyMessage = `Fast Shield stopped (${reason})`;
      updatePanel(true);
    }

    return wasRunning;
  }

  function startFastShieldDrop(reason = 'hotkey-hold') {
    if (S.fastShieldRunning) return true;

    S.fastShieldRunning = true;
    S.hotkeyMessage = 'Fast Shield ON · MAIN CELL · 50ms';
    fastShieldDropOnce(`${reason}-immediate`);

    const ms = Math.max(1, Math.round(Number(C.fastShieldIntervalMs) || 50));
    S.fastShieldTimer = setInterval(
      () => fastShieldDropOnce(`${reason}-repeat`),
      ms
    );

    updatePanel(true);
    return true;
  }

  function toggleFastShieldDrop(reason = 'hotkey-toggle') {
    if (S.fastShieldRunning) {
      stopFastShieldDrop(`${reason}-off`);
      return false;
    }

    startFastShieldDrop(`${reason}-on`);
    return true;
  }

  function hotkeyKindLabel(kind) {
    const labels = {
      shield: 'Shield',
      fastShield: 'Fast Shield Main 50ms',
      push: 'Push ×15',
      growth: 'Growth ×30 + Shield',
      pelletMouse: 'Auto Pellet Set @ Mouse',
      combo: 'Ptl+Vrs+ARec+Ptl'
    };
    return labels[kind] || kind;
  }

  function hotkeyCodesByKind() {
    return {
      shield: String(C.shieldHotkeyCode || ''),
      fastShield: String(C.fastShieldHotkeyCode || ''),
      push: String(C.pushBurstHotkeyCode || ''),
      growth: String(C.growthHotkeyCode || ''),
      pelletMouse: String(C.autoPelletSetMouseHotkeyCode || ''),
      combo: String(C.comboHotkeyCode || '')
    };
  }

  function setHotkeyCode(kind, code) {
    if (kind === 'shield') C.shieldHotkeyCode = code;
    else if (kind === 'fastShield') C.fastShieldHotkeyCode = code;
    else if (kind === 'push') C.pushBurstHotkeyCode = code;
    else if (kind === 'growth') C.growthHotkeyCode = code;
    else if (kind === 'pelletMouse') C.autoPelletSetMouseHotkeyCode = code;
    else if (kind === 'combo') C.comboHotkeyCode = code;
    else return false;
    return true;
  }

  function beginHotkeyCapture(kind) {
    if (!['shield', 'fastShield', 'push', 'growth', 'pelletMouse', 'combo'].includes(kind)) return false;

    S.hotkeyCapture = kind;
    S.hotkeyCaptureStartedAt = now();
    S.hotkeyMessage = `Press a key for ${hotkeyKindLabel(kind)}…`;
    updatePanel();
    return true;
  }

  function cancelHotkeyCapture(message = '') {
    S.hotkeyCapture = null;
    S.hotkeyCaptureStartedAt = 0;
    S.hotkeyMessage = message;
    updatePanel();
    return true;
  }

  function assignCapturedHotkey(kind, code) {
    code = String(code || '');

    if (!code || isModifierOnlyCode(code)) {
      S.hotkeyMessage = 'Modifier-only key is not supported';
      updatePanel();
      return false;
    }

    const duplicate = Object.entries(hotkeyCodesByKind()).find(
      ([otherKind, otherCode]) =>
        otherKind !== kind &&
        otherCode &&
        otherCode === code
    );

    if (duplicate) {
      S.hotkeyMessage =
        `Key ${hotkeyLabel(code)} is already used by ${hotkeyKindLabel(duplicate[0])}`;
      updatePanel();
      return false;
    }

    if (!setHotkeyCode(kind, code)) return false;

    S.hotkeyMessage = `${hotkeyKindLabel(kind)} key: ${hotkeyLabel(code)}`;
    S.hotkeyCapture = null;
    S.hotkeyCaptureStartedAt = 0;
    savePrefs();
    updatePanel();
    return true;
  }

  function clearHotkey(kind) {
    if (!setHotkeyCode(kind, '')) return false;

    S.hotkeyMessage = `${hotkeyKindLabel(kind)} key cleared`;
    savePrefs();
    updatePanel();
    return true;
  }

  function onGlobalHotkey(event) {
    if (!event || event.repeat) return;

    // Key assignment mode always has first priority.
    if (S.hotkeyCapture) {
      stopHotkeyEvent(event);

      if (event.code === 'Escape') {
        cancelHotkeyCapture('Key setting cancelled');
        return;
      }

      if (event.code === 'Backspace' || event.code === 'Delete') {
        const kind = S.hotkeyCapture;
        S.hotkeyCapture = null;
        clearHotkey(kind);
        return;
      }

      assignCapturedHotkey(S.hotkeyCapture, event.code);
      return;
    }

    if (event.ctrlKey || event.altKey || event.metaKey) return;
    if (isTypingTarget(event.target) || isTypingTarget(document.activeElement)) return;

    if (hotkeyMatches(event, C.fastShieldHotkeyCode)) {
      if (C.fastShieldHotkeyStopGameKey) stopHotkeyEvent(event);
      const enabled = toggleFastShieldDrop('hotkey-fast-shield');
      S.hotkeyMessage = `Fast Shield ${enabled ? 'ON' : 'OFF'} · MAIN · ${C.fastShieldIntervalMs}ms`;
      updatePanel(true);
      return;
    }

    if (hotkeyMatches(event, C.shieldHotkeyCode)) {
      if (C.shieldHotkeyStopGameKey) stopHotkeyEvent(event);

      const enabled = setAutoShield(!C.autoShield);
      S.hotkeyMessage = `Auto Shield ${enabled ? 'ON' : 'OFF'}`;

      // setAutoShield already schedules an immediate Shield when enabled and
      // the player currently has no Shield.
      updatePanel();
      return;
    }

    if (
      C.pushBurstEnabled &&
      hotkeyMatches(event, C.pushBurstHotkeyCode)
    ) {
      if (C.pushBurstStopGameKey) stopHotkeyEvent(event);
      sendPushBurst(C.pushBurstCount, 'hotkey-push15');
      return;
    }

    if (hotkeyMatches(event, C.growthHotkeyCode)) {
      if (C.growthHotkeyStopGameKey) stopHotkeyEvent(event);
      sendGrowth30Shield('hotkey-growth30-shield');
      return;
    }

    if (hotkeyMatches(event, C.autoPelletSetMouseHotkeyCode)) {
      if (C.autoPelletSetMouseHotkeyStopGameKey) stopHotkeyEvent(event);
      const captured = captureAutoPelletWorldFromMouse('hotkey-set-at-mouse');
      if (captured) {
        S.hotkeyMessage = `Auto Pellet target: X ${captured.x} · Y ${captured.y}`;
        updatePanel(true);
      }
      return;
    }

    if (hotkeyMatches(event, C.comboHotkeyCode)) {
      if (C.comboHotkeyStopGameKey) stopHotkeyEvent(event);
      sendTargetedCombo('hotkey-smart-drop');
    }
  }

  function itemCooldownMs(item) {
    const learned = Number(S.powerCooldowns[Number(item)]);
    if (Number.isFinite(learned) && learned > 0) return learned;

    if (S.EntityTypes && Number(item) === Number(S.EntityTypes.ANTIRECOMBINE)) {
      return C.viewAntiRecFallbackLockMs;
    }
    if (S.EntityTypes && Number(item) === Number(S.EntityTypes.FREEZE)) {
      return C.viewFreezeFallbackLockMs;
    }
    return 1000;
  }

  function liveOwnCellsForView() {
    const player = mainPlayer();
    const E = S.EntityTypes;
    const out = [];

    if (!player || !player.cells || !E) return out;

    for (const c of player.cells) {
      if (!c || c.destroyed) continue;
      if (Number(c.owner) !== Number(S.mainPlayerId)) continue;
      if (Number(c.type) !== Number(E.PLAYER)) continue;

      const x = Number.isFinite(c.nx) ? Number(c.nx) : Number(c.x);
      const y = Number.isFinite(c.ny) ? Number(c.ny) : Number(c.y);
      const size = Number.isFinite(c.nSize)
        ? Number(c.nSize)
        : Number.isFinite(c.originalSize)
          ? Number(c.originalSize)
          : Number(c.size);

      if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(size) || size <= 0) {
        continue;
      }

      out.push({
        raw: c,
        id: Number(c.id) || 0,
        owner: Number(c.owner) || 0,
        x,
        y,
        size
      });
    }

    return out;
  }

  function playerEffectsByOwner(owner) {
    const players = pagePlayers();
    const player = players && players[owner];
    const F = S.PlayerFlags || {};
    const animations = player && Number.isFinite(Number(player.animations))
      ? Number(player.animations)
      : 0;

    return {
      animations,
      antiRec: !!(animations & Number(F.ANIMATION_ANTI_RECOMBINE || 0)),
      frozen: !!(animations & Number(F.ANIMATION_FROZEN || 0)),
      antiFreeze: !!(animations & Number(F.ANIMATION_ANTI_FREEZE || 0)),
      shield: !!(animations & Number(F.ANIMATION_SHIELD || 0))
    };
  }

  function liveEnemyCells() {
    const players = pagePlayers();
    const E = S.EntityTypes;
    const playerType = E && Number(E.PLAYER);
    const t = now();
    const rows = [];

    if (!players || !Number.isFinite(playerType) || !S.mainPlayerId) return rows;

    for (const key of Object.keys(players)) {
      const player = players[key];
      if (!player || Number(player.id) === Number(S.mainPlayerId)) continue;
      if (!player.cells || typeof player.cells[Symbol.iterator] !== 'function') continue;

      for (const c of player.cells) {
        if (!c || c.destroyed) continue;
        if (Number(c.owner) === Number(S.mainPlayerId)) continue;
        if (Number(c.owner) !== Number(player.id)) continue;
        if (Number(c.type) !== playerType) continue;

        const x = Number.isFinite(c.nx) ? Number(c.nx) : Number(c.x);
        const y = Number.isFinite(c.ny) ? Number(c.ny) : Number(c.y);
        const size = Number.isFinite(c.nSize)
          ? Number(c.nSize)
          : Number.isFinite(c.originalSize)
            ? Number(c.originalSize)
            : Number(c.size);
        const updatedAt = Number(c.updateTime || 0);
        const ageMs = updatedAt > 0 ? Math.max(0, t - updatedAt) : 0;

        if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
        if (!Number.isFinite(size) || size < C.viewMinEnemySize) continue;
        if (updatedAt > 0 && ageMs > C.viewMaxCellAgeMs) continue;

        rows.push({
          raw: c,
          player,
          id: Number(c.id) || 0,
          owner: Number(c.owner) || 0,
          x,
          y,
          size,
          ageMs,
          effects: playerEffectsByOwner(c.owner)
        });
      }
    }

    return rows;
  }

  function sampleEnemyMotion(cell, sampleAt = now()) {
    const key = `${cell.owner}:${cell.id}`;
    const prev = S.viewMotion.get(key);

    let vx = 0;
    let vy = 0;
    let source = 'none';

    const raw = cell.raw;

    // main_out.js stores the previous rendered/server position in ox/oy before
    // assigning the new nx/ny from UPDATE_NODES. This gives us a direction on
    // the very first scan after the packet, without waiting for a second sample.
    if (
      raw &&
      Number.isFinite(Number(raw.nx)) &&
      Number.isFinite(Number(raw.ny)) &&
      Number.isFinite(Number(raw.ox)) &&
      Number.isFinite(Number(raw.oy))
    ) {
      const dx = Number(raw.nx) - Number(raw.ox);
      const dy = Number(raw.ny) - Number(raw.oy);
      const mag = Math.hypot(dx, dy);

      if (mag >= 0.05) {
        // Scale to a velocity-like value. Only direction is critical; thresholds
        // in this sensitive build are deliberately very low.
        vx = dx * 60;
        vy = dy * 60;
        source = 'server-nx-ox';
      }
    }

    // Fallback to two-sample velocity if the current server delta is zero.
    if (source === 'none' && prev) {
      const dt = sampleAt - prev.t;
      if (dt >= 4 && dt <= 1200) {
        vx = (cell.x - prev.x) * 1000 / dt;
        vy = (cell.y - prev.y) * 1000 / dt;
        if (Math.hypot(vx, vy) >= 0.05) source = 'sample-delta';
      }
    }

    S.viewMotion.set(key, { x: cell.x, y: cell.y, t: sampleAt });

    if (S.viewMotion.size > 600) {
      for (const [k, rec] of S.viewMotion) {
        if (!rec || sampleAt - rec.t > 5000) S.viewMotion.delete(k);
      }
    }

    return { vx, vy, source };
  }

  function strictEnemyDropTarget(target) {
    if (!target || !target.raw || !S.mainPlayerId) return null;

    const c = target.raw;
    const players = pagePlayers();
    const E = S.EntityTypes;
    const owner = Number(c.owner);
    const player = players && players[owner];

    if (!player || owner === Number(S.mainPlayerId)) return null;
    if (!player.cells || !player.cells.has(c)) return null;
    if (c.destroyed || Number(c.type) !== Number(E.PLAYER)) return null;

    const x = Number.isFinite(c.nx) ? Number(c.nx) : Number(c.x);
    const y = Number.isFinite(c.ny) ? Number(c.ny) : Number(c.y);
    const size = Number.isFinite(c.nSize)
      ? Number(c.nSize)
      : Number.isFinite(c.originalSize)
        ? Number(c.originalSize)
        : Number(c.size);

    if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(size) || size <= 0) {
      return null;
    }

    return {
      raw: c,
      player,
      id: Number(c.id) || 0,
      owner,
      x,
      y,
      size,
      effects: playerEffectsByOwner(owner)
    };
  }

  function isAntiRecViewItem(item) {
    return !!(
      S.EntityTypes &&
      Number(item) === Number(S.EntityTypes.ANTIRECOMBINE)
    );
  }

  function isFreezeViewItem(item) {
    return !!(
      S.EntityTypes &&
      Number(item) === Number(S.EntityTypes.FREEZE)
    );
  }

  function viewLockKey(item, target) {
    const itemId = Number(item);
    const owner = Number(target && target.owner) || 0;

    // Locks follow the opponent owner, not an individual split cell.
    if (isAntiRecViewItem(itemId)) return `anti-rec-player:${owner}`;
    if (isFreezeViewItem(itemId)) return `freeze-player:${owner}`;

    return `${itemId}:${owner}:${Number(target && target.id) || 0}`;
  }

  function viewItemIntervalMs(item) {
    if (isAntiRecViewItem(item)) {
      return Math.max(1, Number(C.viewAntiRecPerPlayerLockMs) || 460);
    }

    if (isFreezeViewItem(item)) {
      return Math.max(1, Number(C.viewFreezePerPlayerLockMs) || 2900);
    }

    return itemCooldownMs(item);
  }

  function viewTargetLockMs(item) {
    return viewItemIntervalMs(item);
  }

  function viewItemReady(item, target) {
    const t = now();
    const intervalMs = viewItemIntervalMs(item);
    const key = viewLockKey(item, target);
    const targetAt = Number(S.viewTargetLocks.get(key) || 0);
    const globalAt = Number(S.viewGlobalLocks.get(Number(item)) || 0);

    // The global lock guarantees the requested overall packet rate even when
    // the mouse moves quickly between different opponents.
    return (
      t - globalAt >= intervalMs &&
      t - targetAt >= intervalMs
    );
  }

  function viewItemRemainingMs(item, target, at = now()) {
    const intervalMs = viewItemIntervalMs(item);
    const key = viewLockKey(item, target);
    const targetAt = Number(S.viewTargetLocks.get(key) || 0);
    const globalAt = Number(S.viewGlobalLocks.get(Number(item)) || 0);

    return Math.max(
      0,
      intervalMs - (at - globalAt),
      intervalMs - (at - targetAt)
    );
  }

  function setViewItemLock(item, target) {
    const t = now();
    const key = viewLockKey(item, target);

    S.viewGlobalLocks.set(Number(item), t);
    if (key) S.viewTargetLocks.set(key, t);
  }

  function clampNumber(value, min, max) {
    value = Number(value);
    min = Number(min);
    max = Number(max);
    if (!Number.isFinite(value)) value = min;
    if (!Number.isFinite(min)) min = 0;
    if (!Number.isFinite(max)) max = min;
    return Math.max(min, Math.min(max, value));
  }

  function newestEnemyDirection(verified, threat = null) {
    const c = verified && verified.raw;
    let dx = 0;
    let dy = 0;
    let source = 'none';

    // Prefer the current UPDATE_NODES delta at send time.
    if (
      c &&
      Number.isFinite(Number(c.nx)) &&
      Number.isFinite(Number(c.ny)) &&
      Number.isFinite(Number(c.ox)) &&
      Number.isFinite(Number(c.oy))
    ) {
      dx = Number(c.nx) - Number(c.ox);
      dy = Number(c.ny) - Number(c.oy);
      if (Math.hypot(dx, dy) >= 0.05) source = 'live-nx-minus-ox';
    }

    // Fallback to the vector captured when this threat was selected.
    if (
      source === 'none' &&
      threat &&
      Number.isFinite(Number(threat.moveVx)) &&
      Number.isFinite(Number(threat.moveVy))
    ) {
      dx = Number(threat.moveVx);
      dy = Number(threat.moveVy);
      if (Math.hypot(dx, dy) >= 0.05) source = 'selected-threat-vector';
    }

    const magnitude = Math.hypot(dx, dy);
    if (!Number.isFinite(magnitude) || magnitude < 0.05) {
      return {
        ok: false,
        dx: 0,
        dy: 0,
        ux: 0,
        uy: 0,
        magnitude: 0,
        source: 'no-live-motion'
      };
    }

    return {
      ok: true,
      dx,
      dy,
      ux: dx / magnitude,
      uy: dy / magnitude,
      magnitude,
      source
    };
  }

  function centerEnemyDropPoint(verified) {
    const centerX = Number(verified && verified.x);
    const centerY = Number(verified && verified.y);

    return {
      x: Number.isFinite(centerX) ? Math.round(centerX) : 0,
      y: Number.isFinite(centerY) ? Math.round(centerY) : 0,
      centerX: Number.isFinite(centerX) ? Math.round(centerX) : 0,
      centerY: Number.isFinite(centerY) ? Math.round(centerY) : 0,
      leadDistance: 0,
      forward: false,
      direction: {
        ok: false,
        ux: 0,
        uy: 0,
        magnitude: 0,
        source: 'exact-target-center'
      }
    };
  }

  function forwardEnemyDropPoint(verified, threat = null) {
    const centerX = Number(verified.x);
    const centerY = Number(verified.y);
    const direction = newestEnemyDirection(verified, threat);

    if (!C.viewForwardDropEnabled || !direction.ok) {
      return {
        x: centerX | 0,
        y: centerY | 0,
        centerX: centerX | 0,
        centerY: centerY | 0,
        leadDistance: 0,
        forward: false,
        direction
      };
    }

    const speed = threat && Number.isFinite(Number(threat.speed))
      ? Math.max(0, Number(threat.speed))
      : direction.magnitude * 60;

    const leadDistance = clampNumber(
      Number(C.viewForwardDropBase || 0) +
      Number(verified.size || 0) * Number(C.viewForwardDropSizeFactor || 0) +
      speed * Number(C.viewForwardDropSpeedFactor || 0),
      Number(C.viewForwardDropMin || 0),
      Number(C.viewForwardDropMax || 0)
    );

    const x = centerX + direction.ux * leadDistance;
    const y = centerY + direction.uy * leadDistance;

    if (
      !Number.isFinite(x) ||
      !Number.isFinite(y) ||
      Math.abs(x) > 200000 ||
      Math.abs(y) > 200000
    ) {
      return {
        x: centerX | 0,
        y: centerY | 0,
        centerX: centerX | 0,
        centerY: centerY | 0,
        leadDistance: 0,
        forward: false,
        direction: { ...direction, source: 'invalid-forward-point-fallback' }
      };
    }

    return {
      x: Math.round(x),
      y: Math.round(y),
      centerX: centerX | 0,
      centerY: centerY | 0,
      leadDistance: Math.round(leadDistance),
      forward: true,
      direction
    };
  }

  function sendViewItem(item, target, kind, threat, source) {
    const verified = strictEnemyDropTarget(target);

    if (!verified) {
      S.viewBlocks++;
      S.lastViewBlock = {
        reason: 'view-target-not-live-enemy',
        kind,
        source,
        t: now()
      };
      return false;
    }

    if (
      isFreezeViewItem(item) &&
      verified.effects &&
      verified.effects.antiFreeze
    ) {
      S.viewBlocks++;
      S.lastViewBlock = {
        reason: 'freeze-target-has-antifreeze',
        kind,
        source,
        item: Number(item),
        targetId: verified.id,
        targetOwner: verified.owner,
        effects: verified.effects,
        t: now()
      };
      return false;
    }

    if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
      S.viewBlocks++;
      S.lastViewBlock = {
        reason: 'view-ws-not-ready',
        kind,
        source,
        t: now()
      };
      return false;
    }

    if (!autoItemAvailable(item)) {
      S.viewBlocks++;
      S.lastViewBlock = {
        reason: 'view-item-inventory-empty',
        kind,
        source,
        item: Number(item),
        inventory: knownInventoryCount(item),
        targetId: verified.id,
        targetOwner: verified.owner,
        t: now()
      };
      return false;
    }

    if (!viewItemReady(item, verified)) {
      S.lastViewBlock = {
        reason: 'view-item-cooldown-lock',
        kind,
        source,
        item,
        globalCooldownMs: viewItemIntervalMs(item),
        targetCooldownMs: viewTargetLockMs(item),
        targetLockKey: viewLockKey(item, verified),
        targetId: verified.id,
        targetOwner: verified.owner,
        t: now()
      };
      return false;
    }

    try {
      const dropPoint = C.viewCenterDropOnly
        ? centerEnemyDropPoint(verified)
        : forwardEnemyDropPoint(verified, threat);
      const ok = sendTargetedPowerPacket(
        item,
        dropPoint.x | 0,
        dropPoint.y | 0,
        null,
        `view-${kind}-${source}`
      );
      if (!ok) {
        S.viewBlocks++;
        S.lastViewBlock = {
          reason: 'view-targeted-send-failed',
          kind,
          source,
          item: Number(item),
          targetId: verified.id,
          targetOwner: verified.owner,
          t: now()
        };
        return false;
      }
      setViewItemLock(item, verified);

      const rec = {
        kind,
        item: Number(item),
        x: dropPoint.x | 0,
        y: dropPoint.y | 0,
        targetCenterX: dropPoint.centerX | 0,
        targetCenterY: dropPoint.centerY | 0,
        forwardDrop: !!dropPoint.forward,
        leadDistance: dropPoint.leadDistance | 0,
        directionSource: dropPoint.direction && dropPoint.direction.source || '',
        directionX: dropPoint.direction ? Number(dropPoint.direction.ux.toFixed(4)) : 0,
        directionY: dropPoint.direction ? Number(dropPoint.direction.uy.toFixed(4)) : 0,
        targetId: verified.id,
        targetOwner: verified.owner,
        targetSize: Math.round(verified.size),
        targetLocked: true,
        source,
        threat,
        effectsBefore: verified.effects,
        cooldownMs: viewItemIntervalMs(item),
        targetCooldownMs: viewTargetLockMs(item),
        targetLockKey: viewLockKey(item, verified),
        t: now()
      };

      S.viewDrops++;
      S.lastViewDrop = rec;
      S.lastDrop = rec;
      queueMicrotask(updatePanel);
      return true;
    } catch (e) {
      S.viewBlocks++;
      S.lastViewBlock = {
        reason: 'view-send-failed',
        kind,
        source,
        error: e && e.message ? e.message : String(e),
        t: now()
      };
      return false;
    }
  }

  function threatAgainstOwn(enemy, own, motion) {
    const dx = own.x - enemy.x;
    const dy = own.y - enemy.y;
    const d = Math.sqrt(dx * dx + dy * dy);
    if (!Number.isFinite(d) || d < 1 || d > C.viewRange) return null;

    const vx = Number(motion.vx) || 0;
    const vy = Number(motion.vy) || 0;
    const speed = Math.sqrt(vx * vx + vy * vy);
    const closing = speed > 0 ? (vx * dx + vy * dy) / d : 0;
    const aimDot = speed > 0 ? closing / speed : 0;

    // Distance from the own-cell center to the enemy's forward movement ray.
    // This approximates "the opponent moved their mouse toward/near me".
    const forwardProjection = speed > 0 ? (vx * dx + vy * dy) / speed : 0;
    const perpendicularSq = Math.max(0, d * d - forwardProjection * forwardProjection);
    const perpendicularDist = Math.sqrt(perpendicularSq);
    const aimLineLimit = own.size + enemy.size + C.viewAimLineExtra;

    const aimLineNear =
      speed >= C.viewAimMinSpeed &&
      closing >= C.viewAimClosingMin &&
      aimDot >= C.viewAimDotMin &&
      forwardProjection >= C.viewAimLineForwardMin &&
      perpendicularDist <= aimLineLimit;

    const predictAttackX = enemy.x + vx * (C.viewAttackPredictMs / 1000);
    const predictAttackY = enemy.y + vy * (C.viewAttackPredictMs / 1000);
    const predictApproachX = enemy.x + vx * (C.viewPredictMs / 1000);
    const predictApproachY = enemy.y + vy * (C.viewPredictMs / 1000);

    const predictedDist = Math.hypot(predictAttackX - own.x, predictAttackY - own.y);
    const approachPredictedDist = Math.hypot(predictApproachX - own.x, predictApproachY - own.y);

    const closeDist = own.size + enemy.size + C.viewAttackCloseMargin;
    const hitDist = own.size + enemy.size + C.viewRushHitMargin;

    const aiming =
      aimLineNear ||
      (
        speed >= C.viewAimMinSpeed &&
        closing >= C.viewAimClosingMin &&
        aimDot >= C.viewAimDotMin &&
        approachPredictedDist <= d - C.viewPredictGain
      );

    const tooClose = d <= closeDist;

    const predictedHit =
      speed >= C.viewRushMinSpeed &&
      closing >= C.viewRushMinClosing &&
      predictedDist <= hitDist + C.viewAttackPredictMargin;

    const emergency =
      d <= C.viewEmergencyMaxDist &&
      speed >= C.viewEmergencySpeed &&
      closing >= C.viewEmergencyClosing &&
      aimDot >= C.viewEmergencyAimDot &&
      (
        aimLineNear ||
        predictedDist <= hitDist + C.viewEmergencyPredictMargin
      );

    const freezeCloseLimit = Math.max(
      C.viewFreezeCloseMinDist,
      own.size + enemy.size + C.viewFreezeCloseExtra
    );

    const freezeClose = d <= freezeCloseLimit;

    // Extreme mode:
    // Any positive movement component toward an own-cell counts as aim intent.
    // A wide forward corridor also catches slight diagonal/nearby aiming.
    const anyTowardOwn =
      speed >= C.viewAimMinSpeed &&
      closing > 0 &&
      forwardProjection >= 0 &&
      aimDot >= -0.02;

    const wideAimCorridor =
      anyTowardOwn &&
      perpendicularDist <= aimLineLimit * 1.35;

    const instantAimDanger =
      aimLineNear ||
      wideAimCorridor ||
      emergency ||
      (anyTowardOwn && d <= C.viewEmergencyMaxDist);

    return {
      dist: d,
      speed,
      closing,
      aimDot,
      motionSource: motion.source || '',
      moveVx: vx,
      moveVy: vy,
      forwardProjection,
      perpendicularDist,
      aimLineLimit,
      aimLineNear,
      anyTowardOwn,
      wideAimCorridor,
      predictedDist,
      approachPredictedDist,
      closeDist,
      hitDist,
      aiming,
      tooClose,
      predictedHit,
      emergency,

      // Extreme: both powers fire on the first positive toward-own movement.
      antiRecDanger: instantAimDanger || aiming || predictedHit || tooClose,
      freezeDanger: instantAimDanger || aiming || predictedHit || freezeClose,
      freezeClose,
      freezeCloseLimit,
      ownId: own.id,
      ownSize: own.size
    };
  }

  function visibleEnemyTargetsByOwner() {
    const cells = liveEnemyCells();
    const byOwner = new Map();

    for (const cell of cells) {
      const current = byOwner.get(cell.owner);

      // One representative target per visible player ID: use their largest
      // current live cell, then freshest cell when sizes are equal.
      if (
        !current ||
        cell.size > current.size ||
        (
          cell.size === current.size &&
          cell.ageMs < current.ageMs
        )
      ) {
        byOwner.set(cell.owner, cell);
      }
    }

    const targets = Array.from(byOwner.values()).sort(
      (a, b) => (a.owner - b.owner) || (b.size - a.size)
    );

    S.visibleEnemyOwnerIds = targets.map(target => Number(target.owner));
    S.visibleEnemyCount = S.visibleEnemyOwnerIds.length;
    S.visibleEnemyUpdatedAt = now();

    return targets;
  }

  function visibleTargetAllowed(kind, enemy) {
    if (!enemy || !enemy.effects) return false;

    if (kind === 'anti-rec') return true;

    if (kind === 'freeze') {
      // User rule: Freeze is allowed only while the selected opponent does NOT
      // have the Anti-Freeze animation. Anti-Rec/Frozen do not block this loop.
      return !enemy.effects.antiFreeze;
    }

    return false;
  }

  function pickMouseHoverViewTarget(kind, item, requireReady = true) {
    const mouse = currentWorldMouse();
    if (!mouse) return null;

    const extraRadius = Math.max(
      0,
      Number(C.viewMouseTargetExtraRadius) || 0
    );

    let best = null;

    for (const enemy of liveEnemyCells()) {
      if (!visibleTargetAllowed(kind, enemy)) continue;

      const dx = Number(enemy.x) - Number(mouse.x);
      const dy = Number(enemy.y) - Number(mouse.y);
      const distance = Math.hypot(dx, dy);
      const radius = Math.max(1, Number(enemy.size) + extraRadius);

      // Do not target a random nearest player elsewhere on screen. The cursor
      // must actually be inside or very close to this live cell.
      if (!Number.isFinite(distance) || distance > radius) continue;

      const normalizedDistance = distance / Math.max(1, Number(enemy.size));

      if (
        !best ||
        normalizedDistance < best.normalizedDistance ||
        (
          normalizedDistance === best.normalizedDistance &&
          distance < best.distance
        )
      ) {
        best = {
          target: enemy,
          distance,
          normalizedDistance,
          radius
        };
      }
    }

    if (!best) return null;
    if (requireReady && !viewItemReady(item, best.target)) return null;

    return {
      target: best.target,
      nextCursor: 0,
      threat: {
        mode: 'mouse-hover-center',
        visible: true,
        cursorLocked: true,
        exactCenter: true,
        mouseX: mouse.x,
        mouseY: mouse.y,
        mouseAgeMs: Math.round(mouse.ageMs),
        mouseDistance: Math.round(best.distance),
        mouseRadius: Math.round(best.radius),
        intervalMs: viewItemIntervalMs(item),
        targetOwner: best.target.owner,
        targetId: best.target.id,
        targetSize: Math.round(best.target.size),
        effects: best.target.effects
      }
    };
  }


  function clearAutoRecTimer() {
    if (S.autoRecTimer) {
      clearTimeout(S.autoRecTimer);
      S.autoRecTimer = null;
    }
    S.autoRecDueAt = 0;
  }

  function sendAutoRecPacket(source = 'auto-rec') {
    S.autoRecRuns++;
    S.autoRecLastSource = source;

    if (
      !C.autoRecEnabled ||
      !S.mapsReady ||
      !S.PacketIds ||
      !S.ws ||
      S.ws.readyState !== 1 ||
      !S.nativeSend ||
      !S.mainPlayerId
    ) {
      return false;
    }

    // EXACT native E-key behavior from main_out.js:
    //   sendUint8(PacketIds.USE_RECOMBINE)
    // Do NOT use DROP_RECOMBINE here; DROP_RECOMBINE is the inventory
    // drag/drop power packet and is a different action.
    const opcode = Number(S.PacketIds.USE_RECOMBINE);
    if (!Number.isFinite(opcode)) {
      S.lastBlock = {
        reason: 'auto-rec-use-recombine-opcode-missing',
        detail: 'PacketIds.USE_RECOMBINE is unavailable',
        t: now()
      };
      return false;
    }

    const packet = new Uint8Array([opcode & 255]);

    try {
      Reflect.apply(S.nativeSend, S.ws, [packet]);
      S.autoRecSends++;
      S.autoRecLastSendAt = now();
      return true;
    } catch (e) {
      S.lastBlock = {
        reason: 'auto-rec-use-recombine-send-failed',
        detail: e && e.message ? e.message : String(e),
        t: now()
      };
      return false;
    }
  }

  function scheduleAutoRecLoop(delayMs = C.autoRecIntervalMs, source = 'auto-rec-schedule') {
    if (!C.autoRecEnabled) {
      clearAutoRecTimer();
      return false;
    }

    const delay = Math.max(1, Number(delayMs) || 1);
    const dueAt = now() + delay;

    if (
      S.autoRecTimer &&
      Number(S.autoRecDueAt) > 0 &&
      S.autoRecDueAt <= dueAt
    ) {
      return false;
    }

    clearAutoRecTimer();
    S.autoRecDueAt = dueAt;
    S.autoRecLastSource = source;

    S.autoRecTimer = setTimeout(() => {
      S.autoRecTimer = null;
      S.autoRecDueAt = 0;

      sendAutoRecPacket(source);

      if (C.autoRecEnabled) {
        scheduleAutoRecLoop(C.autoRecIntervalMs, 'auto-rec-next');
      }
    }, Math.ceil(delay));

    return true;
  }

  function setAutoRec(value) {
    C.autoRecEnabled = !!value;
    savePrefs();

    if (C.autoRecEnabled) {
      scheduleAutoRecLoop(1, 'auto-rec-enabled');
    } else {
      clearAutoRecTimer();
    }

    updatePanel(true);
    return C.autoRecEnabled;
  }

  function clearAntiRecLoopTimer() {
    if (S.antiRecLoopTimer) {
      clearTimeout(S.antiRecLoopTimer);
      S.antiRecLoopTimer = null;
    }
    S.antiRecLoopDueAt = 0;
  }

  function scheduleAntiRecLoop(delayMs = 0, source = 'schedule') {
    if (!C.autoViewAntiRec || !C.antiRecContinuousPerPlayer) {
      clearAntiRecLoopTimer();
      return false;
    }

    const delay = Math.max(
      Math.max(0, Number(C.antiRecLoopMinTimerMs) || 1),
      Math.max(0, Number(delayMs) || 0)
    );
    const dueAt = now() + delay;

    // Keep an already scheduled earlier run.
    if (
      S.antiRecLoopTimer &&
      Number(S.antiRecLoopDueAt) > 0 &&
      S.antiRecLoopDueAt <= dueAt
    ) {
      return false;
    }

    clearAntiRecLoopTimer();
    S.antiRecLoopDueAt = dueAt;
    S.antiRecLoopLastSource = source;

    S.antiRecLoopTimer = setTimeout(() => {
      S.antiRecLoopTimer = null;
      S.antiRecLoopDueAt = 0;
      antiRecContinuousTick(source);
    }, Math.ceil(delay));

    return true;
  }

  function antiRecContinuousTick(source = 'timer') {
    S.antiRecLoopRuns++;
    S.antiRecLoopLastRunAt = now();
    S.antiRecLoopLastSource = source;

    if (!C.autoViewAntiRec || !C.antiRecContinuousPerPlayer) {
      clearAntiRecLoopTimer();
      return false;
    }

    if (
      !S.mapsReady ||
      !S.mainPlayerId ||
      !S.ws ||
      S.ws.readyState !== 1 ||
      !S.nativeSend ||
      !liveOwnCellsForView().length
    ) {
      S.antiRecLoopLastVisibleCount = 0;
      S.antiRecLoopLastSentOwners = [];
      scheduleAntiRecLoop(C.antiRecLoopIdleMs, 'not-ready');
      return false;
    }

    const visibleTargets = visibleEnemyTargetsByOwner();
    S.antiRecLoopLastVisibleCount = visibleTargets.length;

    const item = S.EntityTypes && S.EntityTypes.ANTIRECOMBINE;
    if (!Number.isFinite(Number(item))) {
      S.antiRecLoopLastSentOwners = [];
      scheduleAntiRecLoop(C.antiRecLoopFailureRetryMs, 'anti-rec-map-missing');
      return false;
    }

    const picked = pickMouseHoverViewTarget(
      'anti-rec',
      Number(item),
      false
    );

    if (!picked) {
      S.antiRecLoopLastSentOwners = [];
      scheduleAntiRecLoop(C.antiRecLoopIdleMs, 'no-mouse-hover-target');
      updatePanel();
      return false;
    }

    S.lastViewTarget = {
      kind: 'anti-rec',
      targetId: picked.target.id,
      targetOwner: picked.target.owner,
      threat: picked.threat,
      source,
      t: now()
    };

    if (!viewItemReady(Number(item), picked.target)) {
      S.antiRecLoopLastSentOwners = [];
      scheduleAntiRecLoop(
        Math.max(
          Number(C.antiRecLoopMinTimerMs) || 1,
          viewItemRemainingMs(Number(item), picked.target)
        ),
        'mouse-target-anti-rec-due'
      );
      updatePanel();
      return false;
    }

    const ok = sendViewItem(
      Number(item),
      picked.target,
      'anti-rec-mouse-target',
      picked.threat,
      source
    );

    if (ok) {
      S.antiRecLoopDrops++;
      S.antiRecLoopLastSentOwners = [Number(picked.target.owner)];
      scheduleAntiRecLoop(
        viewItemIntervalMs(Number(item)),
        'mouse-target-anti-rec-next'
      );
    } else {
      S.antiRecLoopLastSentOwners = [];
      scheduleAntiRecLoop(
        C.antiRecLoopFailureRetryMs,
        'mouse-target-anti-rec-retry'
      );
    }

    updatePanel();
    return ok;
  }

  function viewDefenseTick(source = 'poll', force = false) {
    if (!C.autoViewAntiRec && !C.autoViewFreeze && !force) return false;
    if (!S.mapsReady || !S.mainPlayerId || !S.ws || S.ws.readyState !== 1) return false;

    // Keep automatic drops disabled while dead/spectating.
    if (!liveOwnCellsForView().length) return false;

    const t = now();
    if (!force && t - S.lastViewScanAt < C.viewScanMs) return false;
    S.lastViewScanAt = t;
    S.viewScans++;

    let sent = false;
    visibleEnemyTargetsByOwner(); // refresh diagnostics only

    if ((C.autoViewAntiRec || force) && !S.antiRecLoopTimer) {
      scheduleAntiRecLoop(0, `${source}-mouse-update`);
    }

    if (C.autoViewFreeze || force) {
      const item = S.EntityTypes && S.EntityTypes.FREEZE;

      if (Number.isFinite(Number(item))) {
        const picked = pickMouseHoverViewTarget(
          'freeze',
          Number(item),
          true
        );

        if (picked) {
          S.lastViewTarget = {
            kind: 'freeze',
            targetId: picked.target.id,
            targetOwner: picked.target.owner,
            threat: picked.threat,
            source,
            t
          };

          const ok = sendViewItem(
            Number(item),
            picked.target,
            'freeze-mouse-target-no-antifreeze',
            picked.threat,
            source
          );

          if (ok) sent = true;
        }
      }
    }

    return sent;
  }

  function setAutoViewAntiRec(value) {
    const previous = !!C.autoViewAntiRec;
    C.autoViewAntiRec = !!value;
    savePrefs();

    if (C.autoViewAntiRec) {
      if (!previous) {
        for (const key of Array.from(S.viewTargetLocks.keys())) {
          if (String(key).startsWith('anti-rec-player:')) {
            S.viewTargetLocks.delete(key);
          }
        }
        if (S.EntityTypes) {
          S.viewGlobalLocks.delete(Number(S.EntityTypes.ANTIRECOMBINE));
        }
      }
      scheduleAntiRecLoop(0, 'auto-anti-rec-enabled');
    } else {
      clearAntiRecLoopTimer();
      S.antiRecLoopLastSentOwners = [];
    }

    if (!C.autoViewAntiRec && !C.autoViewFreeze) S.viewMotion.clear();
    updatePanel(true);
    return C.autoViewAntiRec;
  }

  function setAutoViewFreeze(value) {
    const previous = !!C.autoViewFreeze;
    C.autoViewFreeze = !!value;
    savePrefs();

    if (C.autoViewFreeze && !previous) {
      for (const key of Array.from(S.viewTargetLocks.keys())) {
        if (String(key).startsWith('freeze-player:')) {
          S.viewTargetLocks.delete(key);
        }
      }
      if (S.EntityTypes) {
        S.viewGlobalLocks.delete(Number(S.EntityTypes.FREEZE));
      }
    }

    if (!C.autoViewAntiRec && !C.autoViewFreeze) S.viewMotion.clear();
    updatePanel();
    return C.autoViewFreeze;
  }

  function trimShieldTimingHistory() {
    const h = S.shieldTiming.history;
    const max = Math.max(1, Number(C.shieldTimingHistoryMax) || 30);
    if (h.length > max) h.splice(0, h.length - max);
  }

  function beginShieldOffCycle(atPerf, source = 'unknown') {
    const st = S.shieldTiming;
    const wall = now();
    const perf = Number.isFinite(Number(atPerf)) ? Number(atPerf) : performance.now();

    if (st.lastOffAtPerf > 0) {
      st.lastClearIntervalMs = Math.max(0, perf - st.lastOffAtPerf);
    }
    if (st.lastOnAtPerf > 0) {
      st.lastVisibleDurationMs = Math.max(0, perf - st.lastOnAtPerf);
    }

    st.lastOffAtPerf = perf;
    st.lastOffAtWall = wall;
    st.sendsThisCycle = 0;
    st.lastClearToSendMs = null;
    st.lastSendToOnMs = null;
    st.lastClearToOnMs = null;
    st.lastUnshieldedGapMs = null;

    st.pendingCycle = {
      offAtPerf: perf,
      offAtWall: wall,
      source,
      firstSendAtPerf: 0,
      firstSendAtWall: 0,
      lastSendAtPerf: 0,
      sends: 0
    };
  }

  function recordShieldSend(atPerf, reason = '') {
    const st = S.shieldTiming;
    const perf = Number.isFinite(Number(atPerf)) ? Number(atPerf) : performance.now();
    const wall = now();

    if (st.lastSendAtPerf > 0) {
      st.lastSendIntervalMs = Math.max(0, perf - st.lastSendAtPerf);
    }

    st.lastSendAtPerf = perf;
    st.lastSendAtWall = wall;
    st.lastSendReason = String(reason || '');

    const cycle = st.pendingCycle;
    if (cycle) {
      cycle.sends++;
      cycle.lastSendAtPerf = perf;
      st.sendsThisCycle = cycle.sends;

      if (!cycle.firstSendAtPerf) {
        cycle.firstSendAtPerf = perf;
        cycle.firstSendAtWall = wall;
        st.lastClearToSendMs = Math.max(0, perf - cycle.offAtPerf);
      }
    }
  }

  function recordShieldOn(atPerf, source = 'unknown') {
    const st = S.shieldTiming;
    const wall = now();
    const perf = Number.isFinite(Number(atPerf)) ? Number(atPerf) : performance.now();

    if (st.lastOnAtPerf > 0) {
      st.lastOnIntervalMs = Math.max(0, perf - st.lastOnAtPerf);
    }

    st.lastOnAtPerf = perf;
    st.lastOnAtWall = wall;

    if (st.lastOffAtPerf > 0) {
      st.lastUnshieldedGapMs = Math.max(0, perf - st.lastOffAtPerf);
    }

    const cycle = st.pendingCycle;
    if (cycle) {
      const rec = {
        source,
        offAtWall: cycle.offAtWall,
        firstSendAtWall: cycle.firstSendAtWall || 0,
        onAtWall: wall,
        clearToSendMs: cycle.firstSendAtPerf
          ? Math.max(0, cycle.firstSendAtPerf - cycle.offAtPerf)
          : null,
        sendToOnMs: cycle.firstSendAtPerf
          ? Math.max(0, perf - cycle.firstSendAtPerf)
          : null,
        clearToOnMs: Math.max(0, perf - cycle.offAtPerf),
        sends: cycle.sends,
        sendReason: st.lastSendReason
      };

      st.lastClearToSendMs = rec.clearToSendMs;
      st.lastSendToOnMs = rec.sendToOnMs;
      st.lastClearToOnMs = rec.clearToOnMs;
      st.lastUnshieldedGapMs = rec.clearToOnMs;
      st.cyclesCompleted++;
      st.history.push(rec);
      trimShieldTimingHistory();
      st.pendingCycle = null;
      st.sendsThisCycle = 0;
    }
  }

  function resetShieldTiming() {
    const fresh = {
      lastOnAtPerf: 0,
      lastOffAtPerf: 0,
      lastSendAtPerf: 0,
      lastOnAtWall: 0,
      lastOffAtWall: 0,
      lastSendAtWall: 0,
      lastClearToSendMs: null,
      lastSendToOnMs: null,
      lastClearToOnMs: null,
      lastVisibleDurationMs: null,
      lastUnshieldedGapMs: null,
      lastSendIntervalMs: null,
      lastClearIntervalMs: null,
      lastOnIntervalMs: null,
      sendsThisCycle: 0,
      cyclesCompleted: 0,
      lastSendReason: '',
      pendingCycle: null,
      history: []
    };
    S.shieldTiming = fresh;
    return fresh;
  }

  function knownInventoryCount(item) {
    if (S.unlimitedInventory) return Infinity;

    const count = Number(S.inventoryCounts[Number(item)]);
    return Number.isFinite(count) ? count : null;
  }

  function autoItemAvailable(item) {
    const count = knownInventoryCount(item);

    // Unknown inventory: fail open because INIT/UPDATE_INVENTORY may not have
    // arrived yet. Once known, never send automatic packets with count 0.
    return count == null || count > 0;
  }

  function sendSelfItemAtVerifiedCell(item, verifiedCell, reason) {
    if (!S.ws || S.ws.readyState !== 1) {
      S.lastBlock = { reason: 'ws-not-open', detail: reason, t: now() };
      return false;
    }

    if (!S.mapsReady || !S.PacketIds || !S.EntityTypes || !S.nativeSend) {
      S.lastBlock = { reason: 'self-item-runtime-not-ready', detail: reason, t: now() };
      return false;
    }

    if (
      !verifiedCell ||
      Number(verifiedCell.owner) !== Number(S.mainPlayerId) ||
      !Number.isFinite(verifiedCell.x) ||
      !Number.isFinite(verifiedCell.y)
    ) {
      S.selfDropBlocks++;
      S.lastBlock = {
        reason: 'self-lock-verified-cell-invalid',
        detail: reason,
        verifiedCell,
        t: now()
      };
      return false;
    }

    try {
      const sentAtPerf = performance.now();
      const targetedOk = sendTargetedPowerPacket(
        item,
        verifiedCell.x | 0,
        verifiedCell.y | 0,
        null,
        reason
      );
      if (!targetedOk) {
        S.selfDropBlocks++;
        return false;
      }

      if (
        S.EntityTypes &&
        Number(item) === Number(S.EntityTypes.SHIELD)
      ) {
        recordShieldSend(sentAtPerf, reason);
      }

      S.lastDrop = {
        item,
        x: verifiedCell.x | 0,
        y: verifiedCell.y | 0,
        cellId: verifiedCell.id,
        owner: verifiedCell.owner,
        cellSize: Math.round(verifiedCell.size),
        cellAgeMs: Math.round(verifiedCell.ageMs || 0),
        coordinateSource: verifiedCell.source || 'verified-own-cell',
        selfLocked: true,
        reason,
        sentAtPerf,
        t: now()
      };

      // UI work is deliberately deferred until after the packet event finishes.
      queueMicrotask(updatePanel);
      return true;
    } catch (e) {
      S.lastBlock = {
        reason: 'self-item-send-failed',
        detail: reason,
        error: e && e.message ? e.message : String(e),
        t: now()
      };
      return false;
    }
  }

  function sendDrop(item, cell, reason) {
    // Fail closed and verify once. The supplied cell is never trusted.
    const verifiedCell = strictLiveOwnDropCell();
    if (!verifiedCell) {
      S.selfDropBlocks++;
      S.lastBlock = {
        reason: 'self-lock-no-live-own-cell',
        detail: reason,
        suppliedCell: cell
          ? { id: cell.id || 0, x: cell.x, y: cell.y }
          : null,
        t: now()
      };
      queueMicrotask(updatePanel);
      return false;
    }

    return sendSelfItemAtVerifiedCell(item, verifiedCell, reason);
  }

  function sendShield(reason = 'auto-shield', cellOverride = null) {
    const item = S.EntityTypes && S.EntityTypes.SHIELD;
    if (!Number.isFinite(Number(item))) {
      S.lastBlock = { reason: 'shield-item-id-missing', detail: reason, t: now() };
      return false;
    }

    if (!autoItemAvailable(item)) {
      S.lastBlock = {
        reason: 'shield-inventory-empty',
        detail: reason,
        inventory: knownInventoryCount(item),
        t: now()
      };
      return false;
    }

    const cell = cellOverride || strictLiveOwnDropCell();
    if (!cell) {
      S.lastBlock = { reason: 'shield-own-cell-missing', detail: reason, t: now() };
      return false;
    }

    const ok = sendSelfItemAtVerifiedCell(Number(item), cell, reason);
    if (ok) {
      S.lastShieldSendAt = now();
      S.shieldAttempts++;
    }
    return ok;
  }

  function sendAntiFreeze(reason = 'auto-anti-freeze', cellOverride = null) {
    const item = S.EntityTypes && S.EntityTypes.ANTIFREEZE;
    if (!Number.isFinite(Number(item))) {
      S.lastBlock = { reason: 'anti-freeze-item-id-missing', detail: reason, t: now() };
      return false;
    }

    if (!autoItemAvailable(item)) {
      S.lastBlock = {
        reason: 'anti-freeze-inventory-empty',
        detail: reason,
        inventory: knownInventoryCount(item),
        t: now()
      };
      return false;
    }

    const cell = cellOverride || strictLiveOwnDropCell();
    if (!cell) {
      S.lastBlock = { reason: 'anti-freeze-own-cell-missing', detail: reason, t: now() };
      return false;
    }

    const ok = sendSelfItemAtVerifiedCell(Number(item), cell, reason);
    if (ok) {
      S.lastAntiFreezeSendAt = now();
      S.antiFreezeAttempts++;
    }
    return ok;
  }

  function shieldRetrySendGapMs() {
    const item = S.EntityTypes && S.EntityTypes.SHIELD;
    const learned = Number.isFinite(Number(item))
      ? Number(itemCooldownMs(Number(item)))
      : 0;

    return Math.max(
      Math.max(20, Number(C.shieldRetryMinSendGapMs) || 100),
      learned > 0
        ? learned + Math.max(0, Number(C.shieldRetryCooldownSafetyMs) || 12)
        : 0
    );
  }

  function queueShieldVerification(cycle, reason, delayMs = C.shieldRetryCheckMs) {
    cancelTimer('shieldPending');

    if (!C.autoShield || cycle !== S.shieldCycle) return false;

    const delay = Math.max(1, Number(delayMs) || 20);
    S.shieldPendingCreatedAt = now();

    S.shieldPending = setTimeout(() => {
      S.shieldPending = null;
      S.shieldPendingCreatedAt = 0;
      runShieldUntilEffect(cycle, reason);
    }, delay);

    return true;
  }

  function runShieldUntilEffect(cycle, reason) {
    if (!C.autoShield || cycle !== S.shieldCycle) {
      cancelTimer('shieldPending');
      return false;
    }

    const current = flagsFromAnimations(liveAnimations());

    // This is the only successful stop condition while Auto Shield is ON.
    if (current.shield) {
      cancelTimer('shieldPending');
      S.shieldMissingSince = 0;
      S.lastShieldWatchdogReason = 'shield-effect-confirmed';
      return true;
    }

    const item = S.EntityTypes && S.EntityTypes.SHIELD;
    const cell = fastOwnCell(C.shieldFastCacheMaxAgeMs);

    if (
      !Number.isFinite(Number(item)) ||
      !cell ||
      !S.ws ||
      S.ws.readyState !== 1 ||
      !S.mapsReady
    ) {
      queueShieldVerification(
        cycle,
        `${reason}-waiting-runtime`,
        Math.max(20, Number(C.shieldRetryCheckMs) || 20)
      );
      return false;
    }

    // Keep checking even when inventory is currently zero. The accepted Shield
    // packet may have consumed the last item before its animation packet arrives.
    if (!autoItemAvailable(Number(item))) {
      S.lastShieldWatchdogReason = 'waiting-shield-effect-or-inventory';
      queueShieldVerification(
        cycle,
        `${reason}-inventory-wait`,
        Math.max(35, Number(C.shieldRetryCheckMs) || 20)
      );
      return false;
    }

    const t = now();
    const sendGap = shieldRetrySendGapMs();
    const elapsed = t - Number(S.lastShieldSendAt || 0);

    if (S.shieldAttempts === 0 || elapsed >= sendGap) {
      sendShield(
        S.shieldAttempts === 0
          ? `${reason}-initial`
          : `${reason}-retry-${S.shieldAttempts + 1}`,
        cell
      );
    }

    const remaining = Math.max(
      1,
      sendGap - (now() - Number(S.lastShieldSendAt || 0))
    );

    queueShieldVerification(
      cycle,
      reason,
      Math.min(
        Math.max(1, Number(C.shieldRetryCheckMs) || 20),
        remaining
      )
    );

    return false;
  }

  function fireShieldPacketDirect(reason, packetAt = now()) {
    if (!C.autoShield) return false;

    cancelTimer('shieldPending');

    const cell = fastOwnCell(C.shieldFastCacheMaxAgeMs);
    if (!cell) {
      S.lastBlock = {
        reason: 'packet-direct-own-cell-missing',
        detail: reason,
        t: now()
      };
      queueShieldVerification(S.shieldCycle, `${reason}-cell-fallback`, 1);
      return false;
    }

    const sentAtPerf = performance.now();
    const ok = sendShield(`${reason}-packet-direct`, cell);

    if (ok) {
      const reaction = Math.max(0, sentAtPerf - Number(packetAt || sentAtPerf));
      S.lastShieldClearPacketAt = now();
      S.lastShieldReactionMs = reaction;
      S.lastShieldReactionUs = Math.round(reaction * 1000);
    }

    // Continue verification until the server reports ANIMATION_SHIELD.
    queueShieldVerification(
      S.shieldCycle,
      `${reason}-until-effect`,
      C.shieldRetryCheckMs
    );

    return ok;
  }

  function scheduleShield(reason) {
    cancelTimer('shieldPending');

    const cycle = S.shieldCycle;
    const delay = Math.max(0, Number(C.shieldDelayMs) || 0);

    if (delay > 0) {
      queueShieldVerification(cycle, reason, delay);
    } else {
      runShieldUntilEffect(cycle, reason);
    }
  }

  function fireAntiFreezePacketDirect(reason, packetAt = performance.now()) {
    if (!C.autoAntiFreeze || !C.antiFreezePacketDirectEnabled) return false;

    cancelTimer('antiFreezePending');

    // O(1) validated own-cell hot path; strict synchronous scan is fallback.
    const cell = fastOwnCell(C.antiFreezeFastCacheMaxAgeMs);

    if (!cell) {
      S.antiFreezePacketDirectFallbacks++;
      S.lastBlock = {
        reason: 'packet-direct-anti-freeze-own-cell-missing',
        detail: reason,
        t: now()
      };

      // Exceptional fallback only. Normal first send has no timer.
      S.antiFreezePending = setTimeout(() => {
        S.antiFreezePending = null;
        scheduleAntiFreeze(`${reason}-cell-fallback`);
      }, 0);

      return false;
    }

    const sendAtPerf = performance.now();
    const ok = sendAntiFreeze(`${reason}-packet-direct`, cell);

    if (ok) {
      const sourceAt = Number.isFinite(Number(packetAt))
        ? Number(packetAt)
        : sendAtPerf;
      const reactionMs = Math.max(0, sendAtPerf - sourceAt);

      S.lastAntiFreezeFrozenPacketAt = now();
      S.lastAntiFreezeReactionMs = reactionMs;
      S.lastAntiFreezeReactionUs = Math.round(reactionMs * 1000);
      S.antiFreezePacketDirectSends++;
    }

    // Retry does not delay the first packet; it only verifies later.
    const cycle = S.freezeCycle;

    if (ok && C.antiFreezeRetryMax > 0) {
      S.antiFreezePending = setTimeout(() => {
        S.antiFreezePending = null;

        if (!C.autoAntiFreeze || cycle !== S.freezeCycle) return;

        const verify = flagsFromAnimations(liveAnimations());
        if (verify.frozen && !verify.antiFreeze) {
          sendAntiFreeze(
            `${reason}-packet-direct-verify-retry-${S.antiFreezeAttempts}`,
            fastOwnCell(C.antiFreezeFastCacheMaxAgeMs)
          );
        }
      }, Math.max(0, Number(C.antiFreezeRetryMs) || 0));
    }

    return ok;
  }

  function scheduleAntiFreeze(reason) {
    cancelTimer('antiFreezePending');

    const cycle = S.freezeCycle;
    const run = () => {
      if (!C.autoAntiFreeze || cycle !== S.freezeCycle) return;

      const current = flagsFromAnimations(liveAnimations());
      if (!current.frozen || current.antiFreeze || ownCells().length === 0) return;

      sendAntiFreeze(reason);

      if (S.antiFreezeAttempts <= C.antiFreezeRetryMax) {
        S.antiFreezePending = setTimeout(() => {
          const verify = flagsFromAnimations(liveAnimations());
          if (verify.frozen && !verify.antiFreeze && cycle === S.freezeCycle) {
            sendAntiFreeze(`${reason}-retry-${S.antiFreezeAttempts}`);
          }
        }, C.antiFreezeRetryMs);
      }
    };

    if (C.antiFreezeDelayMs > 0) S.antiFreezePending = setTimeout(run, C.antiFreezeDelayMs);
    else run();
  }

  function normalizeShieldBreakPlayerId(value) {
    const id = Math.trunc(Number(value));
    return Number.isFinite(id) && id >= 1 && id <= 65535 ? id : 0;
  }

  function clearShieldBreakTimer() {
    if (S.shieldBreakTimer) {
      clearTimeout(S.shieldBreakTimer);
      S.shieldBreakTimer = null;
    }
  }

  function resetShieldBreakCycle(reason = 'reset-shield-break') {
    clearShieldBreakTimer();
    S.shieldBreakTargetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    S.shieldBreakLastAnimations = null;
    S.shieldBreakShieldOn = false;
    S.shieldBreakPortalSent = false;
    S.shieldBreakAttempts = 0;
    S.shieldBreakDetectedAt = 0;
    S.shieldBreakLastBlock = reason ? { reason, t: now() } : null;
  }

  function configuredShieldBreakPlayer() {
    const id = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    if (!id) return null;
    const players = pagePlayers();
    return players && players[id] ? players[id] : null;
  }

  function scheduleShieldBreakAttempt(delayMs = 0, reason = 'shield-break-schedule') {
    clearShieldBreakTimer();
    if (!C.shieldBreakEnabled || !S.shieldBreakShieldOn || S.shieldBreakPortalSent) return false;

    const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    if (!targetId || targetId === Number(S.mainPlayerId)) return false;

    const wait = Math.max(0, Number(delayMs) || 0);
    S.shieldBreakTimer = setTimeout(() => {
      S.shieldBreakTimer = null;
      attemptShieldBreakPortal(reason);
    }, Math.ceil(wait));
    return true;
  }

  function shieldBreakEffectStillActive(targetId) {
    if (!S.shieldBreakShieldOn) return false;

    // The packet listener can run before main_out.js applies the same UPDATE_PLAYERS
    // packet to PAGE.players. Trust the packet briefly; after that, require the
    // client's live player animation state so a dead/disconnected target cannot
    // leave a stale retry loop running forever.
    const age = S.shieldBreakDetectedAt ? now() - S.shieldBreakDetectedAt : 0;
    if (age < 120) return true;

    const players = pagePlayers();
    const player = players && players[targetId];
    if (!player) {
      if (age < 700) return true;
      S.shieldBreakShieldOn = false;
      S.shieldBreakLastBlock = { reason: 'shield-break-target-disappeared', targetId, t: now() };
      return false;
    }

    if (Number.isFinite(Number(player.animations))) {
      const stillOn = flagsFromAnimations(Number(player.animations)).shield;
      if (!stillOn) {
        S.shieldBreakShieldOn = false;
        S.shieldBreakPortalSent = false;
        S.shieldBreakAttempts = 0;
        S.shieldBreakDetectedAt = 0;
        S.shieldBreakLastBlock = { reason: 'shield-break-live-shield-ended', targetId, t: now() };
        return false;
      }
    }

    return true;
  }

  function attemptShieldBreakPortal(reason = 'shield-break') {
    const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    const E = S.EntityTypes || {};
    const portalItem = Number(E.PORTAL);

    if (!C.shieldBreakEnabled || !targetId || !S.shieldBreakShieldOn || S.shieldBreakPortalSent) {
      return false;
    }

    if (!shieldBreakEffectStillActive(targetId)) {
      clearShieldBreakTimer();
      updatePanel();
      return false;
    }

    if (targetId === Number(S.mainPlayerId)) {
      S.shieldBreakLastBlock = { reason: 'shield-break-target-is-self', targetId, t: now() };
      return false;
    }

    if (!S.mapsReady || !Number.isFinite(portalItem)) {
      S.shieldBreakLastBlock = { reason: 'shield-break-portal-map-not-ready', targetId, t: now() };
      scheduleShieldBreakAttempt(C.shieldBreakRetryMs, `${reason}-map-retry`);
      return false;
    }

    if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
      S.shieldBreakLastBlock = { reason: 'shield-break-ws-not-ready', targetId, t: now() };
      scheduleShieldBreakAttempt(C.shieldBreakRetryMs, `${reason}-ws-retry`);
      return false;
    }

    if (!autoItemAvailable(portalItem)) {
      S.shieldBreakLastBlock = {
        reason: 'shield-break-no-portal',
        targetId,
        inventory: knownInventoryCount(portalItem),
        t: now()
      };
      scheduleShieldBreakAttempt(Math.max(80, C.shieldBreakRetryMs), `${reason}-inventory-retry`);
      updatePanel();
      return false;
    }

    const target = largestLiveCellForOwner(targetId);
    if (!target) {
      S.shieldBreakLastBlock = { reason: 'shield-break-target-cell-not-ready', targetId, t: now() };
      scheduleShieldBreakAttempt(C.shieldBreakRetryMs, `${reason}-cell-retry`);
      updatePanel();
      return false;
    }

    const cooldownWait = manualItemWaitMs(portalItem);
    if (cooldownWait > 0) {
      S.shieldBreakLastBlock = {
        reason: 'shield-break-portal-cooldown',
        targetId,
        waitMs: cooldownWait,
        t: now()
      };
      scheduleShieldBreakAttempt(Math.max(C.shieldBreakRetryMs, cooldownWait + 1), `${reason}-cooldown-retry`);
      updatePanel();
      return false;
    }

    S.shieldBreakAttempts++;
    const ok = sendTargetedPowerPacket(
      portalItem,
      Number(target.x) | 0,
      Number(target.y) | 0,
      null,
      `${reason}-player-${targetId}-shield`
    );

    if (!ok) {
      S.shieldBreakLastBlock = {
        reason: 'shield-break-targeted-send-failed',
        targetId,
        cellId: Number(target.id) || 0,
        t: now()
      };
      scheduleShieldBreakAttempt(C.shieldBreakRetryMs, `${reason}-send-retry`);
      updatePanel();
      return false;
    }

    recordManualItemSend(portalItem);
    S.shieldBreakPortalSent = true;
    S.shieldBreakSends++;
    S.shieldBreakLastBlock = null;
    S.shieldBreakLastSend = {
      targetId,
      cellId: Number(target.id) || 0,
      x: Number(target.x) | 0,
      y: Number(target.y) | 0,
      size: Math.round(Number(target.size) || 0),
      source: reason,
      t: now()
    };
    S.lastDrop = {
      item: portalItem,
      x: Number(target.x) | 0,
      y: Number(target.y) | 0,
      reason: `${reason}-auto-break-shield`,
      targetOwner: targetId,
      targetCellId: Number(target.id) || 0,
      t: now()
    };
    S.hotkeyMessage = `Break Shield ID ${targetId}: Portal @ (${Number(target.x) | 0}, ${Number(target.y) | 0})`;
    updatePanel(true);
    return true;
  }

  function processShieldBreakAnimation(id, animations, source = 'shield-break-animation') {
    const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    if (!targetId || Number(id) !== targetId) return;

    if (S.shieldBreakTargetId !== targetId) {
      resetShieldBreakCycle('shield-break-target-changed-packet');
      S.shieldBreakTargetId = targetId;
    }

    const shieldMask = Number((S.PlayerFlags || {}).ANIMATION_SHIELD || 0);
    const shieldOn = !!(Number(animations) & shieldMask);
    const previousShieldOn = S.shieldBreakLastAnimations == null
      ? false
      : !!(Number(S.shieldBreakLastAnimations) & shieldMask);

    S.shieldBreakLastAnimations = Number(animations) || 0;
    S.shieldBreakShieldOn = shieldOn;

    if (!shieldOn) {
      clearShieldBreakTimer();
      S.shieldBreakPortalSent = false;
      S.shieldBreakAttempts = 0;
      S.shieldBreakDetectedAt = 0;
      updatePanel();
      return;
    }

    if (!previousShieldOn) {
      S.shieldBreakPortalSent = false;
      S.shieldBreakAttempts = 0;
      S.shieldBreakDetectedAt = now();
      S.shieldBreakLastBlock = null;
    }

    if (C.shieldBreakEnabled && !S.shieldBreakPortalSent && !S.shieldBreakTimer) {
      // UPDATE_PLAYERS carries animation state, while the target cell's newest
      // nx/ny is already kept by UPDATE_NODES. Send on the same event turn when
      // possible; retry only if the live cell/cooldown/runtime is not ready yet.
      attemptShieldBreakPortal(`${source}-shield-on`);
    }

    updatePanel();
  }

  function clearTargetFreezeTimer() {
    if (S.targetFreezeTimer) {
      clearTimeout(S.targetFreezeTimer);
      S.targetFreezeTimer = null;
    }
  }

  function clearTargetAntiRecTimer() {
    if (S.targetAntiRecTimer) {
      clearTimeout(S.targetAntiRecTimer);
      S.targetAntiRecTimer = null;
    }
  }

  function targetIdPlayer() {
    const id = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    if (!id) return null;
    const players = pagePlayers();
    return players && players[id] ? players[id] : null;
  }

  function targetIdAnimationFlags() {
    const player = targetIdPlayer();
    if (!player || !Number.isFinite(Number(player.animations))) return null;
    return flagsFromAnimations(Number(player.animations));
  }

  function scheduleTargetFreezeCheck(delayMs = 0, reason = 'target-freeze-check') {
    clearTargetFreezeTimer();
    if (!C.targetAutoFreezeEnabled) return false;

    const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    if (!targetId || targetId === Number(S.mainPlayerId)) return false;

    S.targetFreezeTimer = setTimeout(() => {
      S.targetFreezeTimer = null;
      targetAutoFreezeTick(reason);
    }, Math.ceil(Math.max(0, Number(delayMs) || 0)));
    return true;
  }

  function targetAutoFreezeTick(source = 'target-freeze-tick', packetAnimations = null) {
    const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    const E = S.EntityTypes || {};
    const freezeItem = Number(E.FREEZE);

    if (!C.targetAutoFreezeEnabled || !targetId || targetId === Number(S.mainPlayerId)) {
      clearTargetFreezeTimer();
      return false;
    }

    let state = null;
    if (packetAnimations != null) {
      state = flagsFromAnimations(Number(packetAnimations) || 0);
    } else {
      state = targetIdAnimationFlags();
    }

    // Fail closed until the target's real animation state is known. This avoids
    // throwing Freeze at an Anti-Freeze target just because player data is stale.
    if (!state) {
      S.targetFreezeLastBlock = { reason: 'target-freeze-animation-unknown', targetId, t: now() };
      scheduleTargetFreezeCheck(Math.max(50, Number(C.targetActionRetryMs) || 35), `${source}-state-retry`);
      updatePanel();
      return false;
    }

    if (state.frozen || state.antiFreeze) {
      clearTargetFreezeTimer();
      S.targetFreezeLastBlock = {
        reason: state.antiFreeze ? 'target-has-antifreeze' : 'target-already-frozen',
        targetId,
        frozen: !!state.frozen,
        antiFreeze: !!state.antiFreeze,
        t: now()
      };
      updatePanel();
      return false;
    }

    if (!S.mapsReady || !Number.isFinite(freezeItem)) {
      S.targetFreezeLastBlock = { reason: 'target-freeze-map-not-ready', targetId, t: now() };
      scheduleTargetFreezeCheck(C.targetActionRetryMs, `${source}-map-retry`);
      return false;
    }

    if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
      S.targetFreezeLastBlock = { reason: 'target-freeze-ws-not-ready', targetId, t: now() };
      scheduleTargetFreezeCheck(C.targetActionRetryMs, `${source}-ws-retry`);
      return false;
    }

    if (!autoItemAvailable(freezeItem)) {
      S.targetFreezeLastBlock = {
        reason: 'target-freeze-no-item',
        targetId,
        inventory: knownInventoryCount(freezeItem),
        t: now()
      };
      scheduleTargetFreezeCheck(Math.max(100, C.targetActionRetryMs), `${source}-inventory-retry`);
      updatePanel();
      return false;
    }

    const target = largestLiveCellForOwner(targetId);
    if (!target) {
      S.targetFreezeLastBlock = { reason: 'target-freeze-cell-not-ready', targetId, t: now() };
      scheduleTargetFreezeCheck(C.targetActionRetryMs, `${source}-cell-retry`);
      updatePanel();
      return false;
    }

    // Cooldown only prevents rejected duplicate packets; the DECISION to Freeze
    // is still based on FROZEN / ANTI_FREEZE state, not a blind 2.9s interval.
    const cooldownWait = manualItemWaitMs(freezeItem);
    if (cooldownWait > 0) {
      S.targetFreezeLastBlock = {
        reason: 'target-freeze-item-cooldown',
        targetId,
        waitMs: cooldownWait,
        t: now()
      };
      scheduleTargetFreezeCheck(cooldownWait + 1, `${source}-cooldown-recheck`);
      updatePanel();
      return false;
    }

    S.targetFreezeAttempts++;
    const ok = sendTargetedPowerPacket(
      freezeItem,
      Number(target.x) | 0,
      Number(target.y) | 0,
      null,
      `${source}-target-id-${targetId}`
    );

    if (!ok) {
      S.targetFreezeLastBlock = { reason: 'target-freeze-send-failed', targetId, t: now() };
      scheduleTargetFreezeCheck(C.targetActionRetryMs, `${source}-send-retry`);
      updatePanel();
      return false;
    }

    recordManualItemSend(freezeItem);
    S.targetFreezeSends++;
    S.targetFreezeLastBlock = null;
    S.targetFreezeLastSend = {
      targetId,
      cellId: Number(target.id) || 0,
      x: Number(target.x) | 0,
      y: Number(target.y) | 0,
      size: Math.round(Number(target.size) || 0),
      source,
      t: now()
    };
    S.lastDrop = {
      item: freezeItem,
      x: Number(target.x) | 0,
      y: Number(target.y) | 0,
      reason: `${source}-auto-freeze-id`,
      targetOwner: targetId,
      targetCellId: Number(target.id) || 0,
      t: now()
    };

    // Re-check after the server-provided Freeze cooldown. If the target has
    // become Frozen or gained Anti-Freeze, this check stops immediately.
    scheduleTargetFreezeCheck(
      Math.max(1, manualItemCooldownMs(freezeItem) + 1),
      `${source}-post-send-state-recheck`
    );
    updatePanel(true);
    return true;
  }

  function processTargetIdAnimation(id, animations, source = 'target-id-animation') {
    const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    if (!targetId || Number(id) !== targetId) return;

    const state = flagsFromAnimations(Number(animations) || 0);

    if (C.targetAutoFreezeEnabled) {
      if (state.frozen || state.antiFreeze) {
        clearTargetFreezeTimer();
        S.targetFreezeLastBlock = {
          reason: state.antiFreeze ? 'target-has-antifreeze' : 'target-already-frozen',
          targetId,
          frozen: !!state.frozen,
          antiFreeze: !!state.antiFreeze,
          t: now()
        };
      } else {
        // Run after the native client has had a chance to apply the same packet,
        // while still using the packet's animation bits as the source of truth.
        queueMicrotask(() => targetAutoFreezeTick(`${source}-effect-clear`, animations));
      }
    }

    updatePanel();
  }

  function scheduleTargetAntiRec(delayMs = 0, reason = 'target-antirec-schedule') {
    clearTargetAntiRecTimer();
    if (!C.targetAutoAntiRecEnabled) return false;

    const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    if (!targetId || targetId === Number(S.mainPlayerId)) return false;

    S.targetAntiRecTimer = setTimeout(() => {
      S.targetAntiRecTimer = null;
      targetAutoAntiRecTick(reason);
    }, Math.ceil(Math.max(0, Number(delayMs) || 0)));
    return true;
  }

  function targetAutoAntiRecTick(source = 'target-antirec-tick') {
    const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    const E = S.EntityTypes || {};
    const antiRecItem = Number(E.ANTIRECOMBINE);
    const baseInterval = Math.max(1, Number(C.targetAntiRecIntervalMs) || 460);

    if (!C.targetAutoAntiRecEnabled || !targetId || targetId === Number(S.mainPlayerId)) {
      clearTargetAntiRecTimer();
      return false;
    }

    if (!S.mapsReady || !Number.isFinite(antiRecItem)) {
      S.targetAntiRecLastBlock = { reason: 'target-antirec-map-not-ready', targetId, t: now() };
      scheduleTargetAntiRec(Math.max(baseInterval, C.targetActionRetryMs), `${source}-map-retry`);
      return false;
    }

    if (!S.ws || S.ws.readyState !== 1 || !S.nativeSend) {
      S.targetAntiRecLastBlock = { reason: 'target-antirec-ws-not-ready', targetId, t: now() };
      scheduleTargetAntiRec(Math.max(baseInterval, C.targetActionRetryMs), `${source}-ws-retry`);
      return false;
    }

    if (!autoItemAvailable(antiRecItem)) {
      S.targetAntiRecLastBlock = {
        reason: 'target-antirec-no-item',
        targetId,
        inventory: knownInventoryCount(antiRecItem),
        t: now()
      };
      scheduleTargetAntiRec(baseInterval, `${source}-inventory-retry`);
      updatePanel();
      return false;
    }

    const target = largestLiveCellForOwner(targetId);
    if (!target) {
      S.targetAntiRecLastBlock = { reason: 'target-antirec-cell-not-ready', targetId, t: now() };
      scheduleTargetAntiRec(baseInterval, `${source}-cell-retry`);
      updatePanel();
      return false;
    }

    const cooldownWait = manualItemWaitMs(antiRecItem);
    if (cooldownWait > 0) {
      S.targetAntiRecLastBlock = {
        reason: 'target-antirec-item-cooldown',
        targetId,
        waitMs: cooldownWait,
        t: now()
      };
      scheduleTargetAntiRec(Math.max(1, cooldownWait + 1), `${source}-cooldown`);
      updatePanel();
      return false;
    }

    S.targetAntiRecAttempts++;
    const ok = sendTargetedPowerPacket(
      antiRecItem,
      Number(target.x) | 0,
      Number(target.y) | 0,
      null,
      `${source}-target-id-${targetId}`
    );

    if (ok) {
      recordManualItemSend(antiRecItem);
      S.targetAntiRecSends++;
      S.targetAntiRecLastBlock = null;
      S.targetAntiRecLastSend = {
        targetId,
        cellId: Number(target.id) || 0,
        x: Number(target.x) | 0,
        y: Number(target.y) | 0,
        source,
        t: now()
      };
      S.lastDrop = {
        item: antiRecItem,
        x: Number(target.x) | 0,
        y: Number(target.y) | 0,
        reason: `${source}-auto-antirec-id`,
        targetOwner: targetId,
        targetCellId: Number(target.id) || 0,
        t: now()
      };
    } else {
      S.targetAntiRecLastBlock = { reason: 'target-antirec-send-failed', targetId, t: now() };
    }

    scheduleTargetAntiRec(baseInterval, `${source}-next`);
    updatePanel();
    return ok;
  }

  function setTargetAutoFreeze(value) {
    C.targetAutoFreezeEnabled = !!value;
    clearTargetFreezeTimer();
    savePrefs();

    if (C.targetAutoFreezeEnabled) {
      targetAutoFreezeTick('target-auto-freeze-enabled');
    }

    updatePanel(true);
    return C.targetAutoFreezeEnabled;
  }

  function setTargetAutoAntiRec(value) {
    C.targetAutoAntiRecEnabled = !!value;
    clearTargetAntiRecTimer();
    savePrefs();

    if (C.targetAutoAntiRecEnabled) {
      scheduleTargetAntiRec(0, 'target-auto-antirec-enabled');
    }

    updatePanel(true);
    return C.targetAutoAntiRecEnabled;
  }

  function shieldBreakSelectablePlayers() {
    const players = pagePlayers();
    if (!players) return [];

    const rows = [];
    const seen = new Set();

    for (const key of Object.keys(players)) {
      const player = players[key];
      if (!player) continue;

      const id = normalizeShieldBreakPlayerId(
        Number.isFinite(Number(player.id)) ? Number(player.id) : Number(key)
      );
      if (!id || seen.has(id)) continue;
      seen.add(id);

      const username = typeof player.username === 'string'
        ? player.username.trim()
        : '';

      rows.push({
        id,
        username,
        isSelf: id === Number(S.mainPlayerId)
      });
    }

    rows.sort((a, b) => a.id - b.id);
    return rows;
  }

  function refreshShieldBreakPlayerSelect(select, force = false) {
    if (!select) return;

    const rows = shieldBreakSelectablePlayers();
    const selectedId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    const signature = rows
      .map(row => `${row.id}:${row.username}:${row.isSelf ? 1 : 0}`)
      .join('|');

    if (!force && signature === S.shieldBreakPlayerListSignature) {
      const wanted = selectedId ? String(selectedId) : '';
      if (select.value !== wanted && [...select.options].some(option => option.value === wanted)) {
        select.value = wanted;
      }
      return;
    }

    S.shieldBreakPlayerListSignature = signature;
    const fragment = document.createDocumentFragment();

    const placeholder = document.createElement('option');
    placeholder.value = '';
    placeholder.textContent = rows.length
      ? `Select player… (${rows.length})`
      : 'No players available';
    fragment.appendChild(placeholder);

    let selectedExists = false;
    for (const row of rows) {
      const option = document.createElement('option');
      option.value = String(row.id);
      option.textContent = `ID ${row.id}${row.username ? ` — ${row.username}` : ''}${row.isSelf ? ' (YOU)' : ''}`;
      option.disabled = row.isSelf;
      if (row.id === selectedId) selectedExists = true;
      fragment.appendChild(option);
    }

    // Keep the saved selection visible across a short disconnect/reconnect,
    // but mark it unavailable until that player appears again.
    if (selectedId && !selectedExists) {
      const missing = document.createElement('option');
      missing.value = String(selectedId);
      missing.textContent = `ID ${selectedId} — not currently available`;
      missing.disabled = true;
      fragment.appendChild(missing);
    }

    select.replaceChildren(fragment);
    select.value = selectedId ? String(selectedId) : '';
  }

  function setShieldBreakPlayerId(value) {
    const id = normalizeShieldBreakPlayerId(value);
    C.shieldBreakPlayerId = id;
    resetShieldBreakCycle('shield-break-player-id-set');
    clearTargetFreezeTimer();
    clearTargetAntiRecTimer();
    S.targetFreezeLastBlock = null;
    S.targetAntiRecLastBlock = null;
    savePrefs();

    if (id) {
      const player = configuredShieldBreakPlayer();
      if (player && Number.isFinite(Number(player.animations))) {
        if (C.shieldBreakEnabled) {
          processShieldBreakAnimation(id, Number(player.animations), 'shield-break-id-set-live-state');
        }
        if (C.targetAutoFreezeEnabled) {
          processTargetIdAnimation(id, Number(player.animations), 'target-id-set-live-state');
        }
      }
      if (C.targetAutoAntiRecEnabled) {
        scheduleTargetAntiRec(0, 'target-id-set-antirec');
      }
    }

    if (S.panel) {
      refreshShieldBreakPlayerSelect(S.panel.querySelector('#acndBreakPlayerId'), true);
    }
    updatePanel(true);
    return id;
  }

  function setShieldBreakEnabled(value) {
    C.shieldBreakEnabled = !!value;
    clearShieldBreakTimer();
    savePrefs();

    const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    if (!C.shieldBreakEnabled) {
      S.shieldBreakLastBlock = { reason: 'shield-break-disabled', t: now() };
      updatePanel(true);
      return false;
    }

    if (targetId) {
      const player = configuredShieldBreakPlayer();
      if (player && Number.isFinite(Number(player.animations))) {
        processShieldBreakAnimation(targetId, Number(player.animations), 'shield-break-enabled-live-state');
      }
    }

    updatePanel(true);
    return true;
  }

  function processAnimationState(animations, source = 'unknown', meta = {}) {
    if (animations == null) return;

    const current = flagsFromAnimations(animations);
    const previous = S.lastAnimations == null
      ? null
      : flagsFromAnimations(S.lastAnimations);

    S.lastAnimations = current.animations;

    const transitionAtPerf = Number.isFinite(Number(meta.packetAt))
      ? Number(meta.packetAt)
      : performance.now();

    if (current.shield) {
      cancelTimer('shieldPending');
      S.shieldMissingSince = 0;
      S.shieldAttempts = 0;
      S.lastShieldWatchdogReason = 'shield-effect-confirmed';

      if (!previous || !previous.shield) {
        recordShieldOn(transitionAtPerf, `${source}-shield-on`);
      }
    }

    if (!previous) {
      S.shieldCycle++;
      S.shieldAttempts = 0;

      if (C.autoShield && !current.shield && fastOwnCell()) {
        if (!S.shieldTiming.pendingCycle) {
          beginShieldOffCycle(transitionAtPerf, `${source}-initial-no-shield`);
        }
        if (meta.packetDirect) {
          fireShieldPacketDirect(
            `${source}-initial-no-shield`,
            Number(meta.packetAt) || now()
          );
        } else {
          scheduleShield(`${source}-initial-no-shield`);
        }
      }

      if (current.frozen && !current.antiFreeze) {
        S.freezeCycle++;
        S.antiFreezeAttempts = 0;

        if (meta.packetDirect && C.antiFreezePacketDirectEnabled) {
          fireAntiFreezePacketDirect(
            `${source}-initial-frozen`,
            transitionAtPerf
          );
        } else {
          scheduleAntiFreeze(`${source}-initial-frozen`);
        }
      }

      if (meta.packetDirect) queueMicrotask(updatePanel);
      else updatePanel();
      return;
    }

    if (previous.shield && !current.shield) {
      beginShieldOffCycle(transitionAtPerf, `${source}-shield-off`);
      S.shieldCycle++;
      S.shieldAttempts = 0;

      // Critical fast path: trust the animation bits parsed from the incoming
      // UPDATE_PLAYERS packet. Do not reread window.players here because the
      // game's own onmessage handler may not have applied the new state yet.
      if (meta.packetDirect) {
        fireShieldPacketDirect(
          `${source}-shield-cleared`,
          Number(meta.packetAt) || now()
        );
      } else {
        scheduleShield(`${source}-shield-cleared`);
      }
    } else if (!current.shield && C.autoShield && S.lastOwnCount === 0 && fastOwnCell()) {
      S.shieldCycle++;
      S.shieldAttempts = 0;
      scheduleShield(`${source}-spawn-without-shield`);
    }

    if (!previous.frozen && current.frozen) {
      S.freezeCycle++;
      S.antiFreezeAttempts = 0;

      if (!current.antiFreeze) {
        if (meta.packetDirect && C.antiFreezePacketDirectEnabled) {
          fireAntiFreezePacketDirect(
            `${source}-freeze-started`,
            transitionAtPerf
          );
        } else {
          scheduleAntiFreeze(`${source}-freeze-started`);
        }
      }
    }

    if (previous.frozen && !current.frozen) {
      S.freezeCycle++;
      S.antiFreezeAttempts = 0;
      cancelTimer('antiFreezePending');
    }

    if (meta.packetDirect) queueMicrotask(updatePanel);
    else updatePanel();
  }

  function shieldWatchdog(state, ownCount, source = 'poll') {
    if (
      !C.shieldWatchdogEnabled ||
      !C.autoShield ||
      !S.mapsReady ||
      !S.mainPlayerId ||
      !S.ws ||
      S.ws.readyState !== 1 ||
      ownCount <= 0
    ) {
      S.shieldMissingSince = 0;
      return false;
    }

    const shieldItem = S.EntityTypes && S.EntityTypes.SHIELD;
    if (
      Number.isFinite(Number(shieldItem)) &&
      !autoItemAvailable(shieldItem)
    ) {
      // The last accepted item may already be consumed while the Shield
      // animation update is still in flight. Keep the verifier alive.
      S.lastShieldWatchdogReason = 'shield-inventory-empty-waiting-effect';
      if (!S.shieldPending) {
        queueShieldVerification(
          S.shieldCycle,
          `${source}-inventory-empty-wait-effect`,
          Math.max(35, Number(C.shieldRetryCheckMs) || 20)
        );
      }
      return false;
    }

    const t = now();

    if (state.shield) {
      S.shieldMissingSince = 0;
      S.shieldAttempts = 0;
      return false;
    }

    if (!S.shieldMissingSince) S.shieldMissingSince = t;

    // A timer handle can remain non-null after browser throttling or an
    // interrupted callback. Remove it if it has clearly become stale.
    if (
      S.shieldPending &&
      S.shieldPendingCreatedAt &&
      t - S.shieldPendingCreatedAt > C.shieldPendingStaleMs
    ) {
      cancelTimer('shieldPending');
      S.lastShieldWatchdogReason = 'cleared-stale-pending-timer';
    }

    if (t - S.shieldMissingSince < C.shieldWatchdogGraceMs) return false;

    // The dedicated retry timer normally owns recovery. The watchdog only
    // restarts it if the timer disappeared or became stale while Shield is OFF.
    if (S.shieldPending) return false;
    if (t - S.shieldLastWatchdogKickAt < C.shieldWatchdogRetryGapMs) return false;

    S.shieldLastWatchdogKickAt = t;
    S.shieldWatchdogRecoveries++;
    S.lastShieldWatchdogReason = `${source}-restart-until-effect-loop`;

    return queueShieldVerification(
      S.shieldCycle,
      `${source}-watchdog-until-effect`,
      1
    );
  }

  function resetDefenseState(reason = 'reset') {
    cancelTimer('shieldPending');
    cancelTimer('antiFreezePending');
    clearAntiRecLoopTimer();
    clearAutoRecTimer();
    clearShieldBreakTimer();
    clearTargetFreezeTimer();
    clearTargetAntiRecTimer();

    S.shieldBreakTargetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
    S.shieldBreakLastAnimations = null;
    S.shieldBreakShieldOn = false;
    S.shieldBreakPortalSent = false;
    S.shieldBreakAttempts = 0;
    S.shieldBreakDetectedAt = 0;

    S.lastAnimations = null;
    S.lastOwnCount = 0;
    S.fastOwnCell = null;
    S.fastOwnCellAt = 0;
    S.shieldTiming.pendingCycle = null;
    S.shieldTiming.sendsThisCycle = 0;

    S.shieldCycle++;
    S.shieldAttempts = 0;
    S.shieldMissingSince = 0;
    S.shieldLastWatchdogKickAt = 0;
    S.lastShieldWatchdogReason = reason;

    S.freezeCycle++;
    S.antiFreezeAttempts = 0;
    S.lastAntiFreezeReactionMs = null;
    S.lastAntiFreezeReactionUs = null;

    S.viewMotion.clear();
    S.viewGlobalLocks.clear();
    S.viewTargetLocks.clear();
    S.lastViewScanAt = 0;

    if (C.autoRecEnabled && S.ws && S.ws.readyState === 1) {
      scheduleAutoRecLoop(1, `${reason}-auto-rec-rearm`);
    }
    if (S.ws && S.ws.readyState === 1) {
      if (C.targetAutoFreezeEnabled && normalizeShieldBreakPlayerId(C.shieldBreakPlayerId)) {
        scheduleTargetFreezeCheck(0, `${reason}-target-freeze-rearm`);
      }
      if (C.targetAutoAntiRecEnabled && normalizeShieldBreakPlayerId(C.shieldBreakPlayerId)) {
        scheduleTargetAntiRec(0, `${reason}-target-antirec-rearm`);
      }
    }
  }

  function scheduleClientSync(source = 'ws', requestedDelayMs = 0) {
    S.syncRequests++;
    S.syncPendingSource = source;

    if (S.syncTimer) return false;

    const elapsed = now() - Number(S.syncLastAt || 0);
    const configuredGap = Number(C.wsSyncMinMs);
    const minimumGap = Math.max(
      0,
      Number.isFinite(configuredGap) ? configuredGap : 0
    );
    const waitMs = Math.max(
      0,
      Number(requestedDelayMs) || 0,
      minimumGap - elapsed
    );

    S.syncTimer = setTimeout(() => {
      S.syncTimer = null;
      S.syncLastAt = now();
      S.syncRuns++;

      const pendingSource = S.syncPendingSource || source;
      S.syncPendingSource = '';
      syncFromClient(pendingSource);
    }, Math.ceil(waitMs));

    return true;
  }

  function syncFromClient(source = 'poll') {
    if (!S.mapsReady || !S.mainPlayerId) {
      updatePanel();
      return;
    }

    const cells = ownCells();
    const ownCount = cells.length;
    const animations = liveAnimations();
    const currentState = flagsFromAnimations(animations || 0);

    if (animations != null) processAnimationState(animations, source);

    // Independent liveness path. It does not depend on seeing ON -> OFF.
    shieldWatchdog(currentState, ownCount, source);

    // main_out.js has applied players/cells by this point.
    viewDefenseTick(source, false);

    // Target-ID Freeze is state-driven. Sync is only a liveness fallback; it
    // checks the target's current effect bits before deciding whether to send.
    if (C.targetAutoFreezeEnabled && normalizeShieldBreakPlayerId(C.shieldBreakPlayerId)) {
      const targetFlags = targetIdAnimationFlags();
      if (targetFlags && (targetFlags.frozen || targetFlags.antiFreeze)) {
        clearTargetFreezeTimer();
      } else if (!S.targetFreezeTimer) {
        scheduleTargetFreezeCheck(0, `${source}-target-freeze-sync`);
      }
    }

    if (S.lastOwnCount === 0 && ownCount > 0) {
      const state = flagsFromAnimations(animations || 0);

      S.shieldCycle++;
      S.shieldAttempts = 0;

      if (C.autoShield && !state.shield) {
        scheduleShield(`${source}-own-cell-appeared`);
      }

      if (C.autoAntiFreeze && state.frozen && !state.antiFreeze) {
        S.freezeCycle++;
        S.antiFreezeAttempts = 0;
        scheduleAntiFreeze(`${source}-own-cell-appeared-frozen`);
      }
    }

    if (ownCount === 0) {
      cancelTimer('shieldPending');
      cancelTimer('antiFreezePending');
      S.lastAnimations = null;
    }

    S.lastOwnCount = ownCount;
    updatePanel();
  }

  function forceRescan() {
    syncFromClient('manual-rescan');
    return getState();
  }

  function setAutoShield(value) {
    C.autoShield = !!value;
    savePrefs();

    if (!C.autoShield) {
      cancelTimer('shieldPending');
    } else {
      S.shieldCycle++;
      S.shieldAttempts = 0;
      S.shieldMissingSince = 0;
      S.shieldLastWatchdogKickAt = 0;
      const state = flagsFromAnimations(liveAnimations() || 0);
      if (!state.shield && strictLiveOwnDropCell()) scheduleShield('toggle-on');
    }

    updatePanel();
    return C.autoShield;
  }

  function setAutoAntiFreeze(value) {
    C.autoAntiFreeze = !!value;
    savePrefs();

    if (!C.autoAntiFreeze) {
      cancelTimer('antiFreezePending');
    } else {
      S.freezeCycle++;
      S.antiFreezeAttempts = 0;
      const state = flagsFromAnimations(liveAnimations() || 0);
      if (state.frozen && !state.antiFreeze) scheduleAntiFreeze('toggle-on');
    }

    updatePanel();
    return C.autoAntiFreeze;
  }

  function getState() {
    const animations = liveAnimations();
    const effects = animations == null ? null : flagsFromAnimations(animations);
    const cells = ownCells();

    return {
      ws: {
        ready: !!(S.ws && S.ws.readyState === 1),
        readyState: S.ws ? S.ws.readyState : -1,
        url: S.ws ? S.ws.url : '',
        socketsSeen: S.socketsSeen,
        earlyInstalled: S.early.installed,
        earlyCaptures: S.early.captures,
        earlyErrors: [...S.early.errors]
      },

      nativeMaps: {
        ready: S.mapsReady,
        error: S.mapsError,
        PacketIds: S.PacketIds,
        EntityTypes: S.EntityTypes,
        PlayerFlags: S.PlayerFlags
      },

      mainPlayerId: S.mainPlayerId,
      ownCells: cells.map(c => ({
        id: c.id,
        x: Math.round(c.x),
        y: Math.round(c.y),
        size: Math.round(c.size)
      })),
      effects,
      autoShield: C.autoShield,
      autoAntiFreeze: C.autoAntiFreeze,
      shieldAttempts: S.shieldAttempts,
      shieldUntilEffectMode: {
        enabled: true,
        stopCondition: 'ANIMATION_SHIELD confirmed by server',
        timerActive: !!S.shieldPending,
        attemptsThisOffCycle: S.shieldAttempts,
        checkMs: C.shieldRetryCheckMs,
        minimumSendGapMs: C.shieldRetryMinSendGapMs,
        effectiveSendGapMs: shieldRetrySendGapMs(),
        retryMax: 'unlimited-until-effect'
      },
      lastShieldReactionMs: S.lastShieldReactionMs,
      lastShieldReactionUs: S.lastShieldReactionUs,
      lastShieldClearPacketAt: S.lastShieldClearPacketAt,
      shieldTiming: {
        clearToSendMs: S.shieldTiming.lastClearToSendMs,
        sendToOnMs: S.shieldTiming.lastSendToOnMs,
        clearToOnMs: S.shieldTiming.lastClearToOnMs,
        visibleDurationMs: S.shieldTiming.lastVisibleDurationMs,
        unshieldedGapMs: S.shieldTiming.lastUnshieldedGapMs,
        sendIntervalMs: S.shieldTiming.lastSendIntervalMs,
        clearIntervalMs: S.shieldTiming.lastClearIntervalMs,
        onIntervalMs: S.shieldTiming.lastOnIntervalMs,
        sendsThisCycle: S.shieldTiming.sendsThisCycle,
        cyclesCompleted: S.shieldTiming.cyclesCompleted,
        lastSendReason: S.shieldTiming.lastSendReason,
        lastOnAt: S.shieldTiming.lastOnAtWall,
        lastOffAt: S.shieldTiming.lastOffAtWall,
        lastSendAt: S.shieldTiming.lastSendAtWall,
        pending: S.shieldTiming.pendingCycle
          ? {
              offAt: S.shieldTiming.pendingCycle.offAtWall,
              firstSendAt: S.shieldTiming.pendingCycle.firstSendAtWall,
              sends: S.shieldTiming.pendingCycle.sends,
              source: S.shieldTiming.pendingCycle.source
            }
          : null,
        history: S.shieldTiming.history.slice()
      },
      fastOwnCellAgeMs: S.fastOwnCellAt ? now() - S.fastOwnCellAt : null,
      selfLock: {
        enabled: true,
        failClosed: !!C.selfDropFailClosed,
        blocks: S.selfDropBlocks,
        lastVerifiedCell: S.lastVerifiedSelfCell
      },
      shieldWatchdog: {
        enabled: !!C.shieldWatchdogEnabled,
        missingForMs: S.shieldMissingSince ? now() - S.shieldMissingSince : 0,
        recoveries: S.shieldWatchdogRecoveries,
        lastKickAt: S.shieldLastWatchdogKickAt,
        lastReason: S.lastShieldWatchdogReason,
        pending: !!S.shieldPending,
        pendingAgeMs: S.shieldPendingCreatedAt ? now() - S.shieldPendingCreatedAt : 0
      },
      antiFreezeAttempts: S.antiFreezeAttempts,
      antiFreezeTiming: {
        packetDirectEnabled: !!C.antiFreezePacketDirectEnabled,
        reactionMs: S.lastAntiFreezeReactionMs,
        reactionUs: S.lastAntiFreezeReactionUs,
        frozenPacketAt: S.lastAntiFreezeFrozenPacketAt,
        packetDirectSends: S.antiFreezePacketDirectSends,
        packetDirectFallbacks: S.antiFreezePacketDirectFallbacks,
        pendingRetry: !!S.antiFreezePending,
        retryMs: C.antiFreezeRetryMs
      },
      hotkeys: {
        shieldCode: C.shieldHotkeyCode,
        shieldLabel: hotkeyLabel(C.shieldHotkeyCode),
        fastShieldCode: C.fastShieldHotkeyCode,
        fastShieldLabel: hotkeyLabel(C.fastShieldHotkeyCode),
        pushCode: C.pushBurstHotkeyCode,
        pushLabel: hotkeyLabel(C.pushBurstHotkeyCode),
        growthCode: C.growthHotkeyCode,
        growthLabel: hotkeyLabel(C.growthHotkeyCode),
        comboCode: C.comboHotkeyCode,
        comboLabel: hotkeyLabel(C.comboHotkeyCode),
        capture: S.hotkeyCapture,
        message: S.hotkeyMessage
      },
      fastShield: {
        hotkeyCode: C.fastShieldHotkeyCode,
        hotkeyLabel: hotkeyLabel(C.fastShieldHotkeyCode),
        intervalMs: C.fastShieldIntervalMs,
        running: !!S.fastShieldRunning,
        attempts: S.fastShieldAttempts,
        sends: S.fastShieldSends,
        last: S.lastFastShield,
        target: 'largest-live-own-cell'
      },
      pushBurst: {
        enabled: !!C.pushBurstEnabled,
        hotkeyCode: C.pushBurstHotkeyCode,
        hotkeyLabel: hotkeyLabel(C.pushBurstHotkeyCode),
        count: C.pushBurstCount,
        intervalMs: C.pushBurstIntervalMs,
        running: S.pushBurstRunning,
        bursts: S.pushBursts,
        packetsSent: S.pushPacketsSent,
        last: S.lastPushBurst
      },
      targetIdAutomation: {
        playerId: normalizeShieldBreakPlayerId(C.shieldBreakPlayerId),
        autoFreezeEnabled: !!C.targetAutoFreezeEnabled,
        autoAntiRecEnabled: !!C.targetAutoAntiRecEnabled,
        freezeSends: S.targetFreezeSends,
        freezeAttempts: S.targetFreezeAttempts,
        freezeLastSend: S.targetFreezeLastSend,
        freezeLastBlock: S.targetFreezeLastBlock,
        antiRecSends: S.targetAntiRecSends,
        antiRecAttempts: S.targetAntiRecAttempts,
        antiRecLastSend: S.targetAntiRecLastSend,
        antiRecLastBlock: S.targetAntiRecLastBlock
      },
      shieldBreaker: {
        enabled: !!C.shieldBreakEnabled,
        playerId: normalizeShieldBreakPlayerId(C.shieldBreakPlayerId),
        shieldOn: !!S.shieldBreakShieldOn,
        portalSentThisShield: !!S.shieldBreakPortalSent,
        timerActive: !!S.shieldBreakTimer,
        attempts: S.shieldBreakAttempts,
        sends: S.shieldBreakSends,
        detectedAt: S.shieldBreakDetectedAt,
        lastSend: S.shieldBreakLastSend,
        lastBlock: S.shieldBreakLastBlock
      },
      manualTargeting: {
        mouse: S.mouseWorld
          ? {
              x: S.mouseWorld.x,
              y: S.mouseWorld.y,
              ageMs: now() - S.mouseWorld.t,
              source: S.mouseWorld.source
            }
          : null,
        outgoingPacketsObserved: S.outgoingPacketsObserved,
        growth: {
          mode: 'growth30-plus-shield-no-mouse',
          amount: 30,
          hotkeyCode: C.growthHotkeyCode,
          hotkeyLabel: hotkeyLabel(C.growthHotkeyCode),
          sends: S.growthSends,
          last: S.lastGrowthSend
        },
        autoPellet: {
          enabled: !!C.autoPelletEnabled,
          amount: C.autoPelletAmount,
          intervalMs: C.autoPelletIntervalMs,
          worldTarget: configuredAutoPelletWorld(),
          runs: S.autoPelletRuns,
          packets: S.autoPelletPackets,
          totalPellets: S.autoPelletTotal,
          last: S.lastAutoPellet
        },
        combo: {
          running: S.comboRunning,
          queueLength: S.comboQueue.length,
          inventory: comboInventoryStatus(),
          currentPortalInventory: portalInventoryCount(),
          livePortalNodes: livePortalNodeIds().size,
          hotkeyCode: C.comboHotkeyCode,
          hotkeyLabel: hotkeyLabel(C.comboHotkeyCode),
          sends: S.comboSends,
          blocks: S.comboBlocks,
          lastSend: S.lastComboSend,
          lastBlock: S.lastComboBlock
        }
      },
      viewDefense: {
        mode: C.viewTargetMode,
        centerDropOnly: !!C.viewCenterDropOnly,
        autoAntiRec: !!C.autoViewAntiRec,
        autoFreeze: !!C.autoViewFreeze,
        freezeMode: 'mouse-hover-center-skip-antifreeze',
        scanMs: C.viewScanMs,
        forwardDrop: {
          enabled: !!C.viewForwardDropEnabled,
          base: C.viewForwardDropBase,
          sizeFactor: C.viewForwardDropSizeFactor,
          speedFactor: C.viewForwardDropSpeedFactor,
          min: C.viewForwardDropMin,
          max: C.viewForwardDropMax
        },
        scans: S.viewScans,
        drops: S.viewDrops,
        blocks: S.viewBlocks,
        antiRecCooldownMs: S.EntityTypes
          ? viewItemIntervalMs(S.EntityTypes.ANTIRECOMBINE)
          : null,
        antiRecMode: 'mouse-hover-single-target',
        antiRecPerPlayerLockMs: C.viewAntiRecPerPlayerLockMs,
        antiRecContinuous: {
          enabled: !!C.autoViewAntiRec,
          timerActive: !!S.antiRecLoopTimer,
          nextDueAt: S.antiRecLoopDueAt,
          runs: S.antiRecLoopRuns,
          totalDrops: S.antiRecLoopDrops,
          lastRunAt: S.antiRecLoopLastRunAt,
          lastSource: S.antiRecLoopLastSource,
          lastVisibleCount: S.antiRecLoopLastVisibleCount,
          lastSentOwners: S.antiRecLoopLastSentOwners.slice()
        },
        antiRecEffectiveTargetLockMs: S.EntityTypes
          ? viewTargetLockMs(S.EntityTypes.ANTIRECOMBINE)
          : C.viewAntiRecPerPlayerLockMs,
        visiblePlayerCount: S.visibleEnemyCount,
        visiblePlayerIds: S.visibleEnemyOwnerIds.slice(),
        visiblePlayerUpdatedAt: S.visibleEnemyUpdatedAt,
        freezeCooldownMs: S.EntityTypes
          ? viewItemIntervalMs(S.EntityTypes.FREEZE)
          : C.viewFreezePerPlayerLockMs,
        freezePerPlayerLockMs: C.viewFreezePerPlayerLockMs,
        mouseTargetExtraRadius: C.viewMouseTargetExtraRadius,
        lastTarget: S.lastViewTarget,
        lastDrop: S.lastViewDrop,
        lastBlock: S.lastViewBlock
      },
      performance: {
        mode: 'event-driven-low-lag',
        statePollMs: C.statePollMs,
        pollEnabled: !!S.poll,
        eventDrivenOnly: Number(C.statePollMs) === 0,
        wsSyncMinMs: C.wsSyncMinMs,
        viewScanMs: C.viewScanMs,
        uiUpdateMinMs: C.uiUpdateMinMs,
        syncRequests: S.syncRequests,
        syncRuns: S.syncRuns,
        syncPending: !!S.syncTimer,
        uiRequests: S.uiRequests,
        uiUpdates: S.uiUpdates,
        uiPending: !!S.uiUpdateTimer
      },
      lastDrop: S.lastDrop,
      lastBlock: S.lastBlock,
      messages: S.messages,
      lastPacketOp: S.lastPacketOp
    };
  }

  function createPanel() {
    if (!C.panel || document.getElementById('acNativeDefenseV31')) return;

    const style = document.createElement('style');
    style.textContent = `
      #acNativeDefenseV31 {
        position: fixed;
        left: 14px;
        top: 100px;
        z-index: 2147483647;
        width: 292px;
        color: #eaffff;
        background: rgba(4, 12, 17, .92);
        border: 1px solid rgba(67, 243, 255, .65);
        border-radius: 9px;
        padding: 8px;
        font: 12px/1.25 Arial, sans-serif;
        box-shadow: 0 0 18px rgba(0, 229, 255, .18);
        user-select: none;
      }
      #acNativeDefenseV31 .head {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 6px;
        font-weight: 700;
      }
      #acNativeDefenseV31.minimized {
        width: auto;
        min-width: 205px;
      }
      #acNativeDefenseV31.minimized .head {
        margin-bottom: 0;
      }
      #acNativeDefenseV31.minimized > :not(.head) {
        display: none !important;
      }
      #acNativeDefenseV31 .head-actions {
        display: flex;
        align-items: center;
        gap: 5px;
      }
      #acNativeDefenseV31 .mini-btn {
        min-width: 28px;
        padding: 2px 6px;
        font-size: 13px;
        line-height: 1.1;
      }
      #acNativeDefenseV31 .status {
        font-size: 11px;
        opacity: .9;
      }
      #acNativeDefenseV31 label {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        margin: 5px 0;
      }
      #acNativeDefenseV31 button {
        border: 1px solid rgba(67, 243, 255, .5);
        background: rgba(18, 56, 67, .9);
        color: #eaffff;
        border-radius: 6px;
        padding: 4px 7px;
        cursor: pointer;
      }
      #acNativeDefenseV31 .keybtn {
        min-width: 54px;
        font-weight: 700;
        color: #8ffcff;
      }
      #acNativeDefenseV31 .keybtn.capture {
        color: #111;
        background: #ffe16b;
        border-color: #fff0a6;
      }
      #acNativeDefenseV31 .hotkey-msg {
        min-height: 14px;
        margin-top: 4px;
        font-size: 10px;
        color: #8ffcff;
      }
      #acNativeDefenseV31 .power-row {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto auto;
        gap: 5px;
        align-items: center;
        margin-top: 6px;
      }
      #acNativeDefenseV31 .power-row button:first-child {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      #acNativeDefenseV31 .target-box {
        margin-top: 7px;
        padding: 6px;
        border: 1px solid rgba(255, 188, 67, .42);
        border-radius: 7px;
        background: rgba(66, 39, 5, .22);
      }
      #acNativeDefenseV31 .target-row {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        gap: 6px;
        align-items: center;
        margin-top: 5px;
      }
      #acNativeDefenseV31 .target-input {
        min-width: 0;
        width: 100%;
        box-sizing: border-box;
        border: 1px solid rgba(255, 188, 67, .55);
        border-radius: 5px;
        color: #fff6dc;
        background: rgba(20, 14, 6, .96);
        padding: 5px 6px;
      }
      #acNativeDefenseV31 .target-state {
        max-width: 128px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 10px;
      }
      #acNativeDefenseV31 .amount {
        width: 52px;
        height: 25px;
        box-sizing: border-box;
        border: 1px solid rgba(67, 243, 255, .45);
        border-radius: 5px;
        color: #eaffff;
        background: rgba(6, 22, 29, .95);
        text-align: center;
      }
      #acNativeDefenseV31 .ok { color: #63ff98; }
      #acNativeDefenseV31 .bad { color: #ff6b77; }
      #acNativeDefenseV31 .muted { opacity: .7; }
    `;
    document.documentElement.appendChild(style);

    const panel = document.createElement('div');
    panel.id = 'acNativeDefenseV31';
    panel.innerHTML = `
      <div class="head">
        <span>AC Shield Until Effect v39.0.0</span>
        <span class="head-actions">
          <span id="acndWs">WS×</span>
          <button id="acndMini" class="mini-btn" type="button" title="Minimize">−</button>
        </span>
      </div>
      <div class="status" id="acndStatus">Loading native maps…</div>
      <label>
        <span>Auto Shield</span>
        <span style="display:flex;align-items:center;gap:5px">
          <button id="acndShieldKey" class="keybtn" type="button">${hotkeyLabel(C.shieldHotkeyCode)}</button>
          <input id="acndShield" type="checkbox">
        </span>
      </label>
      <label>
        <span>Auto Anti-Freeze</span>
        <input id="acndAf" type="checkbox">
      </label>
      <label>
        <span>Anti-Rec Mouse (460ms)</span>
        <input id="acndArView" type="checkbox">
      </label>
      <label>
        <span>Auto Rec [E] (1ms)</span>
        <input id="acndAutoRec" type="checkbox">
      </label>
      <label>
        <span>Freeze Mouse (2.9s, skip AF)</span>
        <input id="acndFreezeView" type="checkbox">
      </label>

      <div class="target-box">
        <div style="font-weight:700;margin-bottom:4px">Target Player ID</div>
        <div class="target-row">
          <select id="acndBreakPlayerId" class="target-input" title="Select a player currently known by the client">
            <option value="">Select player…</option>
          </select>
          <span id="acndBreakState" class="target-state muted">ID: —</span>
        </div>
        <label style="margin-top:5px">
          <span>Break Shield → Portal</span>
          <input id="acndBreakShield" type="checkbox">
        </label>
        <label>
          <span>Auto Freeze ID (effect check)</span>
          <input id="acndTargetFreeze" type="checkbox">
        </label>
        <label>
          <span>Auto Anti-Rec ID (460ms)</span>
          <input id="acndTargetAntiRec" type="checkbox">
        </label>
      </div>

      <div style="display:flex;gap:5px;margin-top:7px;flex-wrap:wrap">
        <button id="acndTestShield">Shield Now</button>
        <button id="acndPush15">Push ×15</button>
        <button id="acndPushKey" class="keybtn" type="button">${hotkeyLabel(C.pushBurstHotkeyCode)}</button>
        <button id="acndRescan">Rescan</button>
      </div>

      <div class="power-row">
        <button id="acndFastShield" type="button">Fast Shield @ MAIN: OFF</button>
        <span id="acndFastShieldState">50ms</span>
        <button id="acndFastShieldKey" class="keybtn" type="button">${hotkeyLabel(C.fastShieldHotkeyCode)}</button>
      </div>

      <div class="power-row">
        <button id="acndGrowth">Growth ×30 + Shield</button>
        <span>NO MOUSE</span>
        <button id="acndGrowthKey" class="keybtn" type="button">${hotkeyLabel(C.growthHotkeyCode)}</button>
      </div>

      <div class="power-row">
        <button id="acndAutoPellet" type="button">Auto Pellet ×30 / 5s: OFF</button>
        <span id="acndPelletCount">Pellet: 0</span>
        <span></span>
      </div>
      <div style="display:grid;grid-template-columns:1fr 1fr auto;gap:5px;margin-top:5px;align-items:center">
        <input id="acndPelletWorldX" type="number" step="1" placeholder="World X" style="min-width:0;width:100%;box-sizing:border-box;background:#111;color:#eee;border:1px solid #555;border-radius:4px;padding:4px">
        <input id="acndPelletWorldY" type="number" step="1" placeholder="World Y" style="min-width:0;width:100%;box-sizing:border-box;background:#111;color:#eee;border:1px solid #555;border-radius:4px;padding:4px">
        <button id="acndPelletSetMouse" type="button">Set @ Mouse</button>
      </div>
      <div style="display:flex;align-items:center;justify-content:space-between;gap:6px;margin-top:4px">
        <span class="muted">Set Mouse hotkey</span>
        <button id="acndPelletMouseKey" class="keybtn" type="button">${hotkeyLabel(C.autoPelletSetMouseHotkeyCode)}</button>
      </div>
      <div id="acndPelletWorldInfo" class="muted" style="margin-top:3px">World target: not set</div>

      <div class="power-row">
        <button id="acndCombo">SMART: Shield? P→AR→F→V : AR→F→V</button>
        <span></span>
        <button id="acndComboKey" class="keybtn" type="button">${hotkeyLabel(C.comboHotkeyCode)}</button>
      </div>

      <div id="acndHotkeyMsg" class="hotkey-msg"></div>
      <div class="muted" id="acndDetail" style="margin-top:6px"></div>
    `;

    document.body.appendChild(panel);
    S.panel = panel;

    panel.querySelector('#acndShield').checked = C.autoShield;
    panel.querySelector('#acndAf').checked = C.autoAntiFreeze;
    panel.querySelector('#acndArView').checked = C.autoViewAntiRec;
    panel.querySelector('#acndAutoRec').checked = C.autoRecEnabled;
    panel.querySelector('#acndFreezeView').checked = C.autoViewFreeze;
    panel.querySelector('#acndBreakShield').checked = C.shieldBreakEnabled;
    panel.querySelector('#acndTargetFreeze').checked = C.targetAutoFreezeEnabled;
    panel.querySelector('#acndTargetAntiRec').checked = C.targetAutoAntiRecEnabled;
    refreshShieldBreakPlayerSelect(panel.querySelector('#acndBreakPlayerId'), true);
    panel.classList.toggle('minimized', !!C.panelMinimized);
    panel.querySelector('#acndMini').textContent = C.panelMinimized ? '+' : '−';
    panel.querySelector('#acndMini').title = C.panelMinimized ? 'Expand' : 'Minimize';

    panel.querySelector('#acndShield').onchange = e => setAutoShield(e.target.checked);
    panel.querySelector('#acndAf').onchange = e => setAutoAntiFreeze(e.target.checked);
    panel.querySelector('#acndArView').onchange = e => setAutoViewAntiRec(e.target.checked);
    panel.querySelector('#acndAutoRec').onchange = e => setAutoRec(e.target.checked);
    panel.querySelector('#acndFreezeView').onchange = e => setAutoViewFreeze(e.target.checked);
    panel.querySelector('#acndBreakShield').onchange = e => setShieldBreakEnabled(e.target.checked);
    panel.querySelector('#acndTargetFreeze').onchange = e => setTargetAutoFreeze(e.target.checked);
    panel.querySelector('#acndTargetAntiRec').onchange = e => setTargetAutoAntiRec(e.target.checked);
    const breakPlayerIdSelect = panel.querySelector('#acndBreakPlayerId');
    breakPlayerIdSelect.onchange = () => setShieldBreakPlayerId(breakPlayerIdSelect.value);
    breakPlayerIdSelect.onkeydown = event => event.stopPropagation();
    breakPlayerIdSelect.onkeyup = event => event.stopPropagation();
    panel.querySelector('#acndMini').onclick = () => {
      C.panelMinimized = !C.panelMinimized;
      panel.classList.toggle('minimized', C.panelMinimized);
      panel.querySelector('#acndMini').textContent = C.panelMinimized ? '+' : '−';
      panel.querySelector('#acndMini').title = C.panelMinimized ? 'Expand' : 'Minimize';
      savePrefs();
    };
    panel.querySelector('#acndTestShield').onclick = () => sendShield('manual-panel-shield');
    panel.querySelector('#acndPush15').onclick = () => sendPushBurst(C.pushBurstCount, 'panel-push15');
    panel.querySelector('#acndShieldKey').onclick = () => beginHotkeyCapture('shield');
    panel.querySelector('#acndFastShieldKey').onclick = () => beginHotkeyCapture('fastShield');
    panel.querySelector('#acndFastShield').onclick = event => {
      event.preventDefault();
      const enabled = toggleFastShieldDrop('panel-toggle');
      S.hotkeyMessage = `Fast Shield ${enabled ? 'ON' : 'OFF'} · MAIN · ${C.fastShieldIntervalMs}ms`;
      updatePanel(true);
    };
    panel.querySelector('#acndPushKey').onclick = () => beginHotkeyCapture('push');
    panel.querySelector('#acndGrowth').onclick = () =>
      sendGrowth30Shield('panel-growth30-shield');
    panel.querySelector('#acndAutoPellet').onclick = () =>
      setAutoPellet(!C.autoPelletEnabled);
    const pelletWorldXInput = panel.querySelector('#acndPelletWorldX');
    const pelletWorldYInput = panel.querySelector('#acndPelletWorldY');
    const applyPelletWorldInputs = () => {
      if (!pelletWorldXInput.value.trim() || !pelletWorldYInput.value.trim()) return;
      setAutoPelletWorld(Number(pelletWorldXInput.value), Number(pelletWorldYInput.value), 'panel-world-input');
    };
    pelletWorldXInput.onchange = applyPelletWorldInputs;
    pelletWorldYInput.onchange = applyPelletWorldInputs;
    panel.querySelector('#acndPelletSetMouse').onclick = () =>
      captureAutoPelletWorldFromMouse('panel-set-at-mouse');
    panel.querySelector('#acndPelletMouseKey').onclick = () =>
      beginHotkeyCapture('pelletMouse');
    panel.querySelector('#acndPelletMouseKey').oncontextmenu = event => {
      event.preventDefault();
      clearHotkey('pelletMouse');
    };
    panel.querySelector('#acndCombo').onclick = () =>
      sendTargetedCombo('panel-smart-drop');
    panel.querySelector('#acndGrowthKey').onclick = () =>
      beginHotkeyCapture('growth');
    panel.querySelector('#acndComboKey').onclick = () =>
      beginHotkeyCapture('combo');
    panel.querySelector('#acndShieldKey').oncontextmenu = event => {
      event.preventDefault();
      clearHotkey('shield');
    };
    panel.querySelector('#acndFastShieldKey').oncontextmenu = event => {
      event.preventDefault();
      stopFastShieldDrop('hotkey-cleared');
      clearHotkey('fastShield');
    };
    panel.querySelector('#acndPushKey').oncontextmenu = event => {
      event.preventDefault();
      clearHotkey('push');
    };
    panel.querySelector('#acndGrowthKey').oncontextmenu = event => {
      event.preventDefault();
      clearHotkey('growth');
    };
    panel.querySelector('#acndComboKey').oncontextmenu = event => {
      event.preventDefault();
      clearHotkey('combo');
    };
    panel.querySelector('#acndRescan').onclick = forceRescan;

    updatePanel();
  }

  function updatePanel(force = false) {
    const panel = S.panel;
    if (!panel) return;

    S.uiRequests++;

    const t = now();
    const minimumGap = Math.max(0, Number(C.uiUpdateMinMs) || 140);
    const elapsed = t - Number(S.uiLastUpdateAt || 0);

    if (!force && elapsed < minimumGap) {
      if (!S.uiUpdateTimer) {
        S.uiUpdateTimer = setTimeout(() => {
          S.uiUpdateTimer = null;
          updatePanel(true);
        }, Math.ceil(minimumGap - elapsed));
      }
      return;
    }

    if (force && S.uiUpdateTimer) {
      clearTimeout(S.uiUpdateTimer);
      S.uiUpdateTimer = null;
    }

    S.uiLastUpdateAt = t;
    S.uiUpdates++;

    const ws = panel.querySelector('#acndWs');
    const status = panel.querySelector('#acndStatus');
    const detail = panel.querySelector('#acndDetail');
    const shieldCheckbox = panel.querySelector('#acndShield');
    const autoRecCheckbox = panel.querySelector('#acndAutoRec');
    const breakShieldCheckbox = panel.querySelector('#acndBreakShield');
    const targetFreezeCheckbox = panel.querySelector('#acndTargetFreeze');
    const targetAntiRecCheckbox = panel.querySelector('#acndTargetAntiRec');
    const breakPlayerIdSelect = panel.querySelector('#acndBreakPlayerId');
    const breakState = panel.querySelector('#acndBreakState');
    const shieldKeyButton = panel.querySelector('#acndShieldKey');
    const fastShieldButton = panel.querySelector('#acndFastShield');
    const fastShieldState = panel.querySelector('#acndFastShieldState');
    const fastShieldKeyButton = panel.querySelector('#acndFastShieldKey');
    const pushKeyButton = panel.querySelector('#acndPushKey');
    const growthKeyButton = panel.querySelector('#acndGrowthKey');
    const comboKeyButton = panel.querySelector('#acndComboKey');
    const growthButton = panel.querySelector('#acndGrowth');
    const autoPelletButton = panel.querySelector('#acndAutoPellet');
    const pelletCount = panel.querySelector('#acndPelletCount');
    const pelletWorldXInput = panel.querySelector('#acndPelletWorldX');
    const pelletWorldYInput = panel.querySelector('#acndPelletWorldY');
    const pelletWorldInfo = panel.querySelector('#acndPelletWorldInfo');
    const pelletMouseKeyButton = panel.querySelector('#acndPelletMouseKey');
    const hotkeyMsg = panel.querySelector('#acndHotkeyMsg');

    if (shieldCheckbox && shieldCheckbox.checked !== !!C.autoShield) {
      shieldCheckbox.checked = !!C.autoShield;
    }
    if (autoRecCheckbox && autoRecCheckbox.checked !== !!C.autoRecEnabled) {
      autoRecCheckbox.checked = !!C.autoRecEnabled;
    }
    if (breakShieldCheckbox && breakShieldCheckbox.checked !== !!C.shieldBreakEnabled) {
      breakShieldCheckbox.checked = !!C.shieldBreakEnabled;
    }
    if (targetFreezeCheckbox && targetFreezeCheckbox.checked !== !!C.targetAutoFreezeEnabled) {
      targetFreezeCheckbox.checked = !!C.targetAutoFreezeEnabled;
    }
    if (targetAntiRecCheckbox && targetAntiRecCheckbox.checked !== !!C.targetAutoAntiRecEnabled) {
      targetAntiRecCheckbox.checked = !!C.targetAutoAntiRecEnabled;
    }
    if (breakPlayerIdSelect) {
      refreshShieldBreakPlayerSelect(breakPlayerIdSelect);
    }
    if (breakState) {
      const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
      const targetPlayer = targetId ? configuredShieldBreakPlayer() : null;
      const liveAnim = targetPlayer && Number.isFinite(Number(targetPlayer.animations))
        ? flagsFromAnimations(Number(targetPlayer.animations))
        : null;
      const shieldOn = !!(S.shieldBreakShieldOn || (liveAnim && liveAnim.shield));

      if (!targetId) {
        breakState.textContent = 'ID: —';
        breakState.className = 'target-state muted';
      } else if (targetId === Number(S.mainPlayerId)) {
        breakState.textContent = `ID ${targetId}: SELF`;
        breakState.className = 'target-state bad';
      } else if (!targetPlayer) {
        breakState.textContent = `ID ${targetId}: not seen`;
        breakState.className = 'target-state muted';
      } else if (liveAnim && liveAnim.antiFreeze) {
        breakState.textContent = `ID ${targetId}: ANTI-FREEZE`;
        breakState.className = 'target-state bad';
      } else if (liveAnim && liveAnim.frozen) {
        breakState.textContent = `ID ${targetId}: FROZEN`;
        breakState.className = 'target-state ok';
      } else if (S.shieldBreakPortalSent && shieldOn) {
        breakState.textContent = `ID ${targetId}: PORTAL ✓`;
        breakState.className = 'target-state ok';
      } else if (shieldOn) {
        breakState.textContent = `ID ${targetId}: SHIELD`;
        breakState.className = 'target-state bad';
      } else {
        breakState.textContent = `ID ${targetId}: FREEZE READY`;
        breakState.className = 'target-state ok';
      }
      const targetBits = liveAnim
        ? `shield=${liveAnim.shield ? '1' : '0'} frozen=${liveAnim.frozen ? '1' : '0'} af=${liveAnim.antiFreeze ? '1' : '0'}`
        : 'animation=unknown';
      breakState.title = `${targetBits} | Freeze sends: ${S.targetFreezeSends} | Anti-Rec sends: ${S.targetAntiRecSends}`;
    }

    if (shieldKeyButton) {
      shieldKeyButton.textContent = S.hotkeyCapture === 'shield'
        ? 'PRESS…'
        : hotkeyLabel(C.shieldHotkeyCode);
      shieldKeyButton.classList.toggle('capture', S.hotkeyCapture === 'shield');
      shieldKeyButton.title = 'Click to set Auto Shield toggle key; right-click to clear';
    }

    if (fastShieldKeyButton) {
      fastShieldKeyButton.textContent = S.hotkeyCapture === 'fastShield'
        ? 'PRESS…'
        : hotkeyLabel(C.fastShieldHotkeyCode);
      fastShieldKeyButton.classList.toggle('capture', S.hotkeyCapture === 'fastShield');
      fastShieldKeyButton.title = 'Press once: ON; press again: OFF. Shield every 50ms at largest own cell; right-click to clear';
    }

    if (fastShieldButton) {
      fastShieldButton.classList.toggle('active', !!S.fastShieldRunning);
      fastShieldButton.textContent = S.fastShieldRunning
        ? 'Fast Shield @ MAIN: ON'
        : 'Fast Shield @ MAIN: OFF';
      fastShieldButton.title = 'Toggle continuous Shield every 50ms at your largest/main cell. Does not use real mouse target.';
    }

    if (fastShieldState) {
      fastShieldState.textContent = `${C.fastShieldIntervalMs}ms · ${S.fastShieldSends}`;
      fastShieldState.title = `Attempts: ${S.fastShieldAttempts} · Successful sends: ${S.fastShieldSends}`;
    }

    if (pushKeyButton) {
      pushKeyButton.textContent = S.hotkeyCapture === 'push'
        ? 'PRESS…'
        : hotkeyLabel(C.pushBurstHotkeyCode);
      pushKeyButton.classList.toggle('capture', S.hotkeyCapture === 'push');
      pushKeyButton.title = 'Click to set Push ×15 key; right-click to clear';
    }

    if (growthKeyButton) {
      growthKeyButton.textContent = S.hotkeyCapture === 'growth'
        ? 'PRESS…'
        : hotkeyLabel(C.growthHotkeyCode);
      growthKeyButton.classList.toggle('capture', S.hotkeyCapture === 'growth');
      growthKeyButton.title = 'Click to set Growth ×30 + Shield key; no mouse/aim; right-click to clear';
    }

    if (pelletMouseKeyButton) {
      pelletMouseKeyButton.textContent = S.hotkeyCapture === 'pelletMouse'
        ? 'PRESS…'
        : hotkeyLabel(C.autoPelletSetMouseHotkeyCode);
      pelletMouseKeyButton.classList.toggle('capture', S.hotkeyCapture === 'pelletMouse');
      pelletMouseKeyButton.title = 'Click to set Auto Pellet Set @ Mouse hotkey; right-click to clear';
    }

    if (comboKeyButton) {
      comboKeyButton.textContent = S.hotkeyCapture === 'combo'
        ? 'PRESS…'
        : hotkeyLabel(C.comboHotkeyCode);
      comboKeyButton.classList.toggle('capture', S.hotkeyCapture === 'combo');
      comboKeyButton.title = 'Click to set combo key; right-click to clear';
    }

    if (growthButton) {
      growthButton.textContent = 'Growth ×30 + Shield';
      growthButton.title = 'DROP_GROWTH(30) + DROP_SHIELD only. No mouse read, aim, move or restore.';
    }

    if (autoPelletButton) {
      autoPelletButton.textContent = `Auto Pellet ×${C.autoPelletAmount} / ${(C.autoPelletIntervalMs / 1000).toFixed(0)}s: ${C.autoPelletEnabled ? 'ON' : 'OFF'}`;
      autoPelletButton.classList.toggle('active', !!C.autoPelletEnabled);
      const apWorldButton = configuredAutoPelletWorld();
      autoPelletButton.title = apWorldButton
        ? `Fixed WORLD target (${apWorldButton.x}, ${apWorldButton.y})`
        : 'Set WORLD X/Y or click Set @ Mouse';
    }

    const apWorld = configuredAutoPelletWorld();
    if (pelletWorldXInput && document.activeElement !== pelletWorldXInput) pelletWorldXInput.value = apWorld ? String(apWorld.x) : '';
    if (pelletWorldYInput && document.activeElement !== pelletWorldYInput) pelletWorldYInput.value = apWorld ? String(apWorld.y) : '';
    if (pelletWorldInfo) pelletWorldInfo.textContent = apWorld
      ? `World target: X ${apWorld.x} · Y ${apWorld.y}`
      : 'World target: not set';

    if (pelletCount) {
      pelletCount.textContent = `Pellet: ${S.autoPelletTotal}`;
      pelletCount.title = `${S.autoPelletPackets} successful auto Growth packets`;
    }

    if (hotkeyMsg) {
      hotkeyMsg.textContent = S.hotkeyMessage ||
        `SH:${hotkeyLabel(C.shieldHotkeyCode)} · FSH:${hotkeyLabel(C.fastShieldHotkeyCode)}@50ms · P15:${hotkeyLabel(C.pushBurstHotkeyCode)} · G30+SH:${hotkeyLabel(C.growthHotkeyCode)} · AP-Mouse:${hotkeyLabel(C.autoPelletSetMouseHotkeyCode)} · Combo:${hotkeyLabel(C.comboHotkeyCode)}`;
    }

    const wsReady = !!(S.ws && S.ws.readyState === 1);
    ws.textContent = wsReady ? 'WS' : 'WS×';
    ws.className = wsReady ? 'ok' : 'bad';

    const effects = flagsFromAnimations(liveAnimations() || 0);
    const ownCount = ownCells().length;

    if (!S.mapsReady) {
      status.textContent = `MAP× ${S.mapsError || 'loading'}`;
      status.className = 'status bad';
    } else if (!S.mainPlayerId) {
      status.textContent = 'Waiting INIT_PLAYER…';
      status.className = 'status bad';
    } else {
      status.textContent = `ID ${S.mainPlayerId} · cells ${ownCount}`;
      status.className = 'status ok';
    }

    detail.textContent =
      `Shield:${effects.shield ? 'ON' : 'OFF'} ` +
      `SU:${effects.shield ? 'DONE' : `TRY${S.shieldAttempts}`} ` +
      `SΔ:${S.shieldTiming.lastClearToSendMs == null ? '-' : S.shieldTiming.lastClearToSendMs.toFixed(2)}ms ` +
      `S↗:${S.shieldTiming.lastSendToOnMs == null ? '-' : S.shieldTiming.lastSendToOnMs.toFixed(1)}ms ` +
      `Frozen:${effects.frozen ? 'YES' : 'NO'} ` +
      `AF:${effects.antiFreeze ? 'ON' : 'OFF'} ` +
      `AFΔ:${S.lastAntiFreezeReactionMs == null ? '-' : S.lastAntiFreezeReactionMs.toFixed(3)}ms ` +
      `ZD:${C.statePollMs}/${C.viewScanMs}ms ` +
      `UI:${C.uiUpdateMinMs}ms ` +
      `ARV:${C.autoViewAntiRec ? 'MOUSE@460ms' : 'OFF'} ` +
      `REC:${C.autoRecEnabled ? `ON@${C.autoRecIntervalMs}ms#${S.autoRecSends}` : 'OFF'} ` +
      `IDs:${S.visibleEnemyCount}[${S.visibleEnemyOwnerIds.slice(0, 8).join(',')}${S.visibleEnemyCount > 8 ? ',…' : ''}] ` +
      `ARdrop:${S.antiRecLoopLastSentOwners.length}[${S.antiRecLoopLastSentOwners.slice(0, 8).join(',')}] ` +
      `FV:${C.autoViewFreeze ? 'MOUSE@2900ms/NO-AF' : 'OFF'} ` +
      `SH:${hotkeyLabel(C.shieldHotkeyCode)} ` +
      `FSH:${hotkeyLabel(C.fastShieldHotkeyCode)}${S.fastShieldRunning ? '(ON)' : ''}@${C.fastShieldIntervalMs}ms#${S.fastShieldSends} ` +
      `P15:${hotkeyLabel(C.pushBurstHotkeyCode)}×${C.pushBurstCount}@${C.pushBurstIntervalMs}ms ` +
      `G30+SH:${hotkeyLabel(C.growthHotkeyCode)}@NO-MOUSE ` +
      `AP:${C.autoPelletEnabled ? 'ON' : 'OFF'}×${C.autoPelletAmount}/${C.autoPelletIntervalMs}ms TOTAL:${S.autoPelletTotal} ` +
      `SMART:${hotkeyLabel(C.comboHotkeyCode)}${S.comboRunning ? '(RUN)' : ''} ` +
      `Q:${S.comboQueue.length} ` +
      `Mode:${S.lastComboSend ? (S.lastComboSend.shieldAtSend ? 'SHIELD=P+AR+F+V' : 'NO-SHIELD=AR+F+V') : '-'} ` +
      `Sent:${S.lastComboSend ? S.lastComboSend.sentOrder.join('>') : '-'} ` +
      `CMBerr:${S.lastComboBlock ? S.lastComboBlock.reason : '-'} ` +
      `Mouse:${S.mouseWorld ? Math.round(now() - S.mouseWorld.t) + 'ms' : '—'} ` +
      `drops:${S.lastDrop ? S.lastDrop.item : '-'}`;
  }

  function restartStatePoll() {
    if (S.poll) {
      clearInterval(S.poll);
      S.poll = null;
    }

    const ms = Number(C.statePollMs);

    if (Number.isFinite(ms) && ms > 0) {
      S.poll = setInterval(
        () => syncFromClient('poll'),
        Math.max(1, Math.ceil(ms))
      );
    }

    return {
      enabled: !!S.poll,
      pollMs: Number.isFinite(ms) ? ms : 0
    };
  }

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

  installRenderTransformCapture();
  installEarlySocketCapture();
  loadNativeMaps();
  bootUi();

  // Capture phase handles key assignment and prevents matching game bindings.
  PAGE.addEventListener('keydown', onGlobalHotkey, true);

  restartStatePoll();
  if (C.autoPelletEnabled) scheduleAutoPellet(500);

  const API = {
    version: '39.0.0-smart-nearest-cell-freeze',
    state: getState,
    shieldTiming: () => getState().shieldTiming,
    shieldUntilEffectStatus: () => getState().shieldUntilEffectMode,
    shieldOneDropStatus: () => getState().shieldUntilEffectMode,
    resetShieldTiming: () => {
      resetShieldTiming();
      updatePanel();
      return getState().shieldTiming;
    },
    setShieldUntilEffectTiming: (checkMs, minimumSendGapMs) => {
      const check = Number(checkMs);
      const gap = Number(minimumSendGapMs);

      if (Number.isFinite(check)) {
        C.shieldRetryCheckMs = Math.max(1, check);
      }
      if (Number.isFinite(gap)) {
        C.shieldRetryMinSendGapMs = Math.max(20, gap);
      }

      if (C.autoShield && !flagsFromAnimations(liveAnimations() || 0).shield) {
        scheduleShield('timing-updated');
      }

      updatePanel(true);
      return getState().shieldUntilEffectMode;
    },
    setShieldFastMode: value => {
      const enabled = !!value;
      // Both modes remain packet/event-driven with no repeating poll.
      C.statePollMs = 0;

      if (enabled) {
        C.shieldWatchdogGraceMs = 35;
        C.shieldWatchdogRetryGapMs = 700;
      } else {
        C.shieldWatchdogGraceMs = 80;
        C.shieldWatchdogRetryGapMs = 1200;
      }

      // Keep retrying until the server confirms the Shield effect.
      C.shieldRetryScheduleMs = [];
      C.shieldRetryMs = 20;
      C.shieldRetryCheckMs = enabled ? 15 : 30;
      C.shieldRetryMinSendGapMs = enabled ? 80 : 140;
      C.shieldRetryMax = Infinity;

      restartStatePoll();
      updatePanel();

      return {
        enabled,
        pollMs: C.statePollMs,
        watchdogGraceMs: C.shieldWatchdogGraceMs,
        retryScheduleMs: C.shieldRetryScheduleMs.slice()
      };
    },
    setLowLagMode: value => {
      const enabled = !!value;

      if (enabled) {
        C.statePollMs = 40;
        C.wsSyncMinMs = 24;
        C.wsNodeSyncDelayMs = 18;
        C.viewScanMs = 45;
        C.uiUpdateMinMs = 140;
        C.shieldWatchdogRetryGapMs = 900;
      } else {
        // Zero-delay event-driven mode requested by the user.
        C.statePollMs = 0;
        C.wsSyncMinMs = 0;
        C.wsNodeSyncDelayMs = 0;
        C.viewScanMs = 0;
        C.uiUpdateMinMs = 70;
        C.shieldWatchdogRetryGapMs = 500;
      }

      restartStatePoll();
      updatePanel(true);

      return getState().performance;
    },
    setZeroDelayMode: value => {
      const enabled = !!value;

      if (enabled) {
        C.statePollMs = 0;
        C.wsSyncMinMs = 0;
        C.wsNodeSyncDelayMs = 0;
        C.viewScanMs = 0;
      } else {
        C.statePollMs = 40;
        C.wsSyncMinMs = 24;
        C.wsNodeSyncDelayMs = 18;
        C.viewScanMs = 45;
      }

      restartStatePoll();
      updatePanel(true);

      return {
        enabled,
        pollMs: C.statePollMs,
        wsSyncMinMs: C.wsSyncMinMs,
        wsNodeSyncDelayMs: C.wsNodeSyncDelayMs,
        viewScanMs: C.viewScanMs,
        busyLoop: false
      };
    },
    performance: () => getState().performance,
    wsInfo: () => getState().ws,
    maps: () => getState().nativeMaps,
    selfCell: () => strictLiveOwnDropCell(),
    recoverShield: () => {
      S.shieldMissingSince = now() - C.shieldWatchdogGraceMs;
      S.shieldLastWatchdogKickAt = 0;
      return shieldWatchdog(
        flagsFromAnimations(liveAnimations() || 0),
        ownCells().length,
        'manual-api'
      );
    },
    resetDefense: () => {
      resetDefenseState('manual-api-reset');
      syncFromClient('manual-api-reset');
      return getState();
    },
    rescan: forceRescan,
    shield: () => sendShield('manual-api-shield'),
    antiFreeze: () => sendAntiFreeze('manual-api-anti-freeze'),
    antiFreezeTiming: () => ({
      packetDirectEnabled: !!C.antiFreezePacketDirectEnabled,
      reactionMs: S.lastAntiFreezeReactionMs,
      reactionUs: S.lastAntiFreezeReactionUs,
      frozenPacketAt: S.lastAntiFreezeFrozenPacketAt,
      packetDirectSends: S.antiFreezePacketDirectSends,
      packetDirectFallbacks: S.antiFreezePacketDirectFallbacks,
      attempts: S.antiFreezeAttempts,
      pendingRetry: !!S.antiFreezePending
    }),
    setAntiFreezePacketDirect: value => {
      C.antiFreezePacketDirectEnabled = !!value;
      updatePanel();
      return C.antiFreezePacketDirectEnabled;
    },
    viewTick: () => viewDefenseTick('manual-api', true),
    setViewSensitivity: level => {
      const raw = Number(level);
      const v = Math.max(-0.02, Math.min(0.95, Number.isFinite(raw) ? raw : 0));
      C.viewAimDotMin = v;
      C.viewEmergencyAimDot = v;
      updatePanel();
      return {
        aimDotMin: C.viewAimDotMin,
        emergencyAimDot: C.viewEmergencyAimDot
      };
    },
    setAntiRecPerPlayerLock: ms => {
      const raw = Number(ms);
      C.viewAntiRecPerPlayerLockMs = Math.max(
        1,
        Number.isFinite(raw) ? raw : 460
      );

      // Reset only Anti-Rec owner locks so the new duration applies cleanly.
      for (const key of Array.from(S.viewTargetLocks.keys())) {
        if (String(key).startsWith('anti-rec-player:')) {
          S.viewTargetLocks.delete(key);
        }
      }

      if (C.autoViewAntiRec) {
        scheduleAntiRecLoop(0, 'anti-rec-interval-changed');
      }

      updatePanel(true);
      return {
        antiRecPerPlayerLockMs: C.viewAntiRecPerPlayerLockMs,
        effectiveMs: C.viewAntiRecPerPlayerLockMs,
        mode: 'mouse-hover-global-and-player-lock'
      };
    },
    setFreezeMouseLock: ms => {
      const raw = Number(ms);
      C.viewFreezePerPlayerLockMs = Math.max(
        1,
        Number.isFinite(raw) ? raw : 2900
      );

      for (const key of Array.from(S.viewTargetLocks.keys())) {
        if (String(key).startsWith('freeze-player:')) {
          S.viewTargetLocks.delete(key);
        }
      }
      if (S.EntityTypes) {
        S.viewGlobalLocks.delete(Number(S.EntityTypes.FREEZE));
      }

      updatePanel(true);
      return {
        freezePerPlayerLockMs: C.viewFreezePerPlayerLockMs,
        mode: 'mouse-hover-global-and-player-lock'
      };
    },
    visiblePlayers: () => ({
      count: S.visibleEnemyCount,
      ids: S.visibleEnemyOwnerIds.slice(),
      updatedAt: S.visibleEnemyUpdatedAt
    }),
    antiRecLoopStatus: () => ({
      enabled: !!C.autoViewAntiRec,
      intervalMs: C.viewAntiRecPerPlayerLockMs,
      timerActive: !!S.antiRecLoopTimer,
      nextDueAt: S.antiRecLoopDueAt,
      runs: S.antiRecLoopRuns,
      totalDrops: S.antiRecLoopDrops,
      lastRunAt: S.antiRecLoopLastRunAt,
      lastSource: S.antiRecLoopLastSource,
      visibleCount: S.antiRecLoopLastVisibleCount,
      lastSentOwners: S.antiRecLoopLastSentOwners.slice()
    }),
    runAntiRecLoopNow: () => antiRecContinuousTick('manual-api'),
    autoRecStatus: () => ({
      enabled: !!C.autoRecEnabled,
      intervalMs: C.autoRecIntervalMs,
      timerActive: !!S.autoRecTimer,
      nextDueAt: S.autoRecDueAt,
      runs: S.autoRecRuns,
      sends: S.autoRecSends,
      lastSendAt: S.autoRecLastSendAt,
      lastSource: S.autoRecLastSource
    }),
    setAutoRec,
    toggleAutoRec: () => setAutoRec(!C.autoRecEnabled),
    setAutoRecInterval: ms => {
      const raw = Number(ms);
      C.autoRecIntervalMs = Math.max(1, Number.isFinite(raw) ? raw : 1);
      if (C.autoRecEnabled) scheduleAutoRecLoop(1, 'auto-rec-interval-changed');
      updatePanel(true);
      return C.autoRecIntervalMs;
    },
    runAutoRecNow: () => sendAutoRecPacket('manual-api'),
    setViewForwardDrop: value => {
      C.viewForwardDropEnabled = !!value;
      updatePanel();
      return {
        configured: C.viewForwardDropEnabled,
        effective: C.viewCenterDropOnly
          ? false
          : C.viewForwardDropEnabled,
        reason: C.viewCenterDropOnly
          ? 'mouse-hover-center-mode-forces-exact-center'
          : ''
      };
    },
    setViewForwardDistance: (base, max = C.viewForwardDropMax) => {
      const nextBase = Number(base);
      const nextMax = Number(max);
      if (Number.isFinite(nextBase)) C.viewForwardDropBase = Math.max(0, nextBase);
      if (Number.isFinite(nextMax)) C.viewForwardDropMax = Math.max(C.viewForwardDropMin, nextMax);
      updatePanel();
      return {
        enabled: C.viewForwardDropEnabled,
        base: C.viewForwardDropBase,
        min: C.viewForwardDropMin,
        max: C.viewForwardDropMax,
        sizeFactor: C.viewForwardDropSizeFactor,
        speedFactor: C.viewForwardDropSpeedFactor
      };
    },
    setAutoViewAntiRec,
    setAutoViewFreeze,
    setViewCenterMode: value => {
      C.viewCenterDropOnly = !!value;
      C.viewTargetMode = C.viewCenterDropOnly
        ? 'mouse-hover-center'
        : 'legacy-forward';
      updatePanel(true);
      return {
        mode: C.viewTargetMode,
        centerDropOnly: C.viewCenterDropOnly
      };
    },
    shieldNow: () => sendShield('manual-api-shield'),
    toggleAutoShield: () => {
      const enabled = setAutoShield(!C.autoShield);
      S.hotkeyMessage = `Auto Shield ${enabled ? 'ON' : 'OFF'}`;
      updatePanel();
      return enabled;
    },
    push15: () => sendPushBurst(15, 'manual-api-push15'),
    pushBurst: count => sendPushBurst(count, 'manual-api-push-burst'),
    beginShieldHotkeySet: () => beginHotkeyCapture('shield'),
    beginPushHotkeySet: () => beginHotkeyCapture('push'),
    beginGrowthHotkeySet: () => beginHotkeyCapture('growth'),
    beginAutoPelletMouseHotkeySet: () => beginHotkeyCapture('pelletMouse'),
    beginComboHotkeySet: () => beginHotkeyCapture('combo'),
    growth: () => sendGrowth30Shield('manual-api-growth30-shield'),
    autoPelletNow: () => sendAutoPellet30('manual-api-auto-pellet-now'),
    setAutoPellet,
    setAutoPelletWorld: (x, y) => setAutoPelletWorld(x, y, 'manual-api-world'),
    captureAutoPelletWorld: () => captureAutoPelletWorldFromMouse('manual-api-set-at-mouse'),
    getAutoPelletWorld: () => configuredAutoPelletWorld(),
    toggleAutoPellet: () => setAutoPellet(!C.autoPelletEnabled),
    resetAutoPelletCount: () => {
      S.autoPelletPackets = 0;
      S.autoPelletTotal = 0;
      S.lastAutoPellet = null;
      updatePanel(true);
      return true;
    },
    combo: () => sendTargetedCombo('manual-api-smart-drop'),
    comboStatus: () => getState().manualTargeting.combo,
    comboInventory: () => comboInventoryStatus(),
    portalEvidence: () => {
      const baseline = new Set();
      return {
        inventory: portalInventoryCount(),
        livePortalNodes: livePortalNodeIds().size,
        latestSmartDrop: S.lastComboSend
      };
    },
    cancelCombo: () => cancelTargetedCombo('manual-api-cancel'),
    setShieldHotkey: code => {
      const value = String(code || '');
      if (!value) return clearHotkey('shield');
      const ok = assignCapturedHotkey('shield', value);
      return ok ? { code: C.shieldHotkeyCode, label: hotkeyLabel(C.shieldHotkeyCode) } : false;
    },
    setFastShieldHotkey: code => {
      const value = String(code || '');
      if (!value) return clearHotkey('fastShield');
      const ok = assignCapturedHotkey('fastShield', value);
      return ok ? { code: C.fastShieldHotkeyCode, label: hotkeyLabel(C.fastShieldHotkeyCode) } : false;
    },
    fastShieldOnce: () => fastShieldDropOnce('manual-api-fast-shield'),
    toggleFastShield: () => toggleFastShieldDrop('manual-api-toggle'),
    startFastShield: () => startFastShieldDrop('manual-api-start'),
    stopFastShield: () => stopFastShieldDrop('manual-api-stop'),
    setPushHotkey: code => {
      const value = String(code || '');
      if (!value) return clearHotkey('push');
      const ok = assignCapturedHotkey('push', value);
      return ok ? { code: C.pushBurstHotkeyCode, label: hotkeyLabel(C.pushBurstHotkeyCode) } : false;
    },
    setGrowthHotkey: code => {
      const value = String(code || '');
      if (!value) return clearHotkey('growth');
      const ok = assignCapturedHotkey('growth', value);
      return ok ? { code: C.growthHotkeyCode, label: hotkeyLabel(C.growthHotkeyCode) } : false;
    },
    setAutoPelletMouseHotkey: code => {
      const value = String(code || '');
      if (!value) return clearHotkey('pelletMouse');
      const ok = assignCapturedHotkey('pelletMouse', value);
      return ok ? {
        code: C.autoPelletSetMouseHotkeyCode,
        label: hotkeyLabel(C.autoPelletSetMouseHotkeyCode)
      } : false;
    },
    setComboHotkey: code => {
      const value = String(code || '');
      if (!value) return clearHotkey('combo');
      const ok = assignCapturedHotkey('combo', value);
      return ok ? { code: C.comboHotkeyCode, label: hotkeyLabel(C.comboHotkeyCode) } : false;
    },
    cancelPushBurst: () => {
      S.pushBurstToken++;
      if (S.pushBurstTimer) clearTimeout(S.pushBurstTimer);
      S.pushBurstTimer = null;
      S.pushBurstRunning = false;
      updatePanel();
      return true;
    },
    setPushInterval: ms => {
      C.pushBurstIntervalMs = Math.max(0, Number(ms) || 10.5);
      updatePanel();
      return C.pushBurstIntervalMs;
    },
    setPushEnabled: value => {
      C.pushBurstEnabled = !!value;
      updatePanel();
      return C.pushBurstEnabled;
    },
    setShieldBreakEnabled,
    setShieldBreakPlayerId,
    setTargetAutoFreeze,
    setTargetAutoAntiRec,
    targetAutoFreezeNow: () => targetAutoFreezeTick('manual-api-target-freeze-now'),
    targetAutoAntiRecNow: () => targetAutoAntiRecTick('manual-api-target-antirec-now'),
    shieldBreakNow: () => {
      if (!C.shieldBreakEnabled) return false;
      return attemptShieldBreakPortal('manual-api-shield-break-now');
    },
    shieldBreakStatus: () => ({
      enabled: !!C.shieldBreakEnabled,
      playerId: normalizeShieldBreakPlayerId(C.shieldBreakPlayerId),
      shieldOn: !!S.shieldBreakShieldOn,
      portalSentThisShield: !!S.shieldBreakPortalSent,
      attempts: S.shieldBreakAttempts,
      sends: S.shieldBreakSends,
      lastSend: S.shieldBreakLastSend,
      lastBlock: S.shieldBreakLastBlock
    }),
    setAutoShield,
    setAutoAntiFreeze,
    config: C
  };

  PAGE[KEY] = API;
  PAGE.__AAF = API;
  try { window.__AAF = API; } catch (_) {}

  if (C.autoViewAntiRec) {
    scheduleAntiRecLoop(0, 'startup');
  }
  if (C.autoRecEnabled) {
    scheduleAutoRecLoop(1, 'startup-auto-rec');
  }
  if (normalizeShieldBreakPlayerId(C.shieldBreakPlayerId)) {
    queueMicrotask(() => {
      const targetId = normalizeShieldBreakPlayerId(C.shieldBreakPlayerId);
      const player = configuredShieldBreakPlayer();
      if (player && Number.isFinite(Number(player.animations))) {
        if (C.shieldBreakEnabled) {
          processShieldBreakAnimation(targetId, Number(player.animations), 'startup-shield-break-live-state');
        }
        if (C.targetAutoFreezeEnabled) {
          processTargetIdAnimation(targetId, Number(player.animations), 'startup-target-freeze-live-state');
        }
      }
      if (C.targetAutoAntiRecEnabled) {
        scheduleTargetAntiRec(0, 'startup-target-antirec');
      }
    });
  }

  console.log('[AC Shield Until Effect v38.6.0] loaded');
})();