Void Bacon+ v3.4

have fun ngas dont do anything stupid

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

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

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

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

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

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

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

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         Void Bacon+ v3.4
// @namespace    VoidBacon
// @version      3.4
// @author       John pork
// @match        *://survev.io/*
// @description  have fun ngas dont do anything stupid
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
'use strict';

const STORE='voidbacon.v2';
const CFG_STORE='voidbacon.cfg';
function loadState(){try{return JSON.parse(localStorage.getItem(STORE))||{};}catch{return{};}}
const state=Object.assign({x:120,y:80,w:980,h:660,open:true,max:false,tab:'combat',restore:null},loadState());
const clamp=(v,mn,mx)=>Math.max(mn,Math.min(mx,v));
const save=()=>localStorage.setItem(STORE,JSON.stringify({x:state.x,y:state.y,w:state.w,h:state.h,open:state.open,max:state.max,tab:state.tab}));

const cfg={
  Esp:false,names:false,healthBars:false,lootEsp:false,barrelEsp:false,gunEsp:false,grenTimer:false,
  lockOn:false,predict:false,magnet:false,smoothAim:false,
  smoothAmount:50,stickyTarget:false,fovEnabled:false,fovSize:80,showFov:false,wallcheck:true,
  xray:false,layerSpoof:false,mapHighlights:false,autoDoor:false,autoLoot:false,
};

const SAVEABLE_BOOLS=['Esp','names','healthBars','lootEsp','barrelEsp','gunEsp','grenTimer',
  'lockOn','predict','magnet','smoothAim','stickyTarget','fovEnabled','showFov','wallcheck',
  'xray','layerSpoof','mapHighlights','autoDoor','autoLoot'];

function saveCfg(){
  const out={};
  for(const k of SAVEABLE_BOOLS)out[k]=cfg[k];
  out.smoothAmount=cfg.smoothAmount;out.fovSize=cfg.fovSize;
  localStorage.setItem(CFG_STORE,JSON.stringify(out));
  toast('Settings saved ✓');
}
function loadCfg(){
  try{
    const saved=JSON.parse(localStorage.getItem(CFG_STORE)||'{}');
    for(const k of SAVEABLE_BOOLS){if(typeof saved[k]==='boolean')cfg[k]=saved[k];}
    if(typeof saved.smoothAmount==='number')cfg.smoothAmount=saved.smoothAmount;
    if(typeof saved.fovSize==='number')cfg.fovSize=saved.fovSize;
  }catch(e){}
}

let _fbRating=5;

async function submitFeedback(text,rating){
  if(!text.trim()){toast('Enter some feedback first');return;}
  const btn=document.getElementById('_vmFbSubmit');
  if(btn){btn.disabled=true;btn.textContent='SENDING...';}
  try{
    const payload={embeds:[{title:`⭐ Feedback — ${rating}/5 stars`,description:text.trim().slice(0,1800),color:rating>=4?0x5bc470:rating>=3?0xffc43c:0xe87a7a,footer:{text:`Void Bacon+ v3.3.3 | ${new Date().toUTCString()}`}}]};
    const hookUrl='https://discord.com/api/webhooks/1506623115633885234/fURly2_CSjiLajkTd4Nt7g1NH-2luzA8W72dtcthdHO3AYVRHnbNHK5oizIQ_HLe37zt';
    if(!hookUrl){toast('No webhook set in script');return;}
    const res=await fetch(hookUrl,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});
    if(res.ok||res.status===204){toast('Feedback sent ✓');const inp=document.getElementById('_vmFbText');if(inp)inp.value='';}
    else toast(`Discord error: ${res.status}`);
  }catch(e){toast('Network error');}
  finally{if(btn){btn.disabled=false;btn.textContent='SEND TO DISCORD ↗';}}
}

const settings={lockOnEnabled:true,magnetEnabled:true,throwerEnabled:true,zoomEnabled:true};
const keybinds={lockOn:'t',magnet:'e',thrower:'q',zoomIn:'=',zoomOut:'-',zoomReset:'0',stickyTarget:'n',layerSpoof:'y'};

const GUN_DATA={
  mp5:{range:100,spread:3,bulletSpeed:85},mac10:{range:50,spread:10,bulletSpeed:75},
  ump9:{range:100,spread:1.5,bulletSpeed:100},vector:{range:46,spread:2.5,bulletSpeed:88},
  vector45:{range:45,spread:4.5,bulletSpeed:82},scorpion:{range:120,spread:4,bulletSpeed:90},
  vss:{range:125,spread:2,bulletSpeed:110},famas:{range:150,spread:1.1,bulletSpeed:110},
  hk416:{range:175,spread:4,bulletSpeed:105},m4a1:{range:165,spread:2,bulletSpeed:98},
  ak47:{range:200,spread:2.5,bulletSpeed:100},scar:{range:175,spread:2,bulletSpeed:108},
  scarssr:{range:200,spread:1.5,bulletSpeed:108},an94:{range:300,spread:1.5,bulletSpeed:110},
  groza:{range:175,spread:5,bulletSpeed:104},grozas:{range:185,spread:3.5,bulletSpeed:106},
  imbel:{range:200,spread:3,bulletSpeed:92},dp28:{range:225,spread:2,bulletSpeed:110},
  bar:{range:275,spread:2,bulletSpeed:114},pkp:{range:200,spread:2.5,bulletSpeed:120},
  m249:{range:220,spread:1.5,bulletSpeed:125},qbb97:{range:200,spread:4,bulletSpeed:118},
  mk12:{range:400,spread:1,bulletSpeed:132},l86:{range:425,spread:1,bulletSpeed:134},
  mosin:{range:500,spread:1,bulletSpeed:178},sv98:{range:520,spread:1,bulletSpeed:182},
  awc:{range:500,spread:0.5,bulletSpeed:136},m39:{range:400,spread:1,bulletSpeed:125},
  svd:{range:425,spread:1,bulletSpeed:127},garand:{range:444,spread:0.4,bulletSpeed:144},
  scout_elite:{range:450,spread:1,bulletSpeed:164},model94:{range:175,spread:1.5,bulletSpeed:156},
  blr:{range:400,spread:1.5,bulletSpeed:160},mkg45:{range:145,spread:3.5,bulletSpeed:126},
  m870:{range:27,spread:10,bulletSpeed:66},m1100:{range:25,spread:25,bulletSpeed:66},
  mp220:{range:27,spread:10,bulletSpeed:66},spas12:{range:27,spread:10,bulletSpeed:66},
  saiga:{range:45,spread:7,bulletSpeed:88},usas:{range:24,spread:7,bulletSpeed:72},
  m1014:{range:60,spread:4,bulletSpeed:118},m9:{range:100,spread:3,bulletSpeed:85},
  m9_dual:{range:100,spread:4,bulletSpeed:85},m93r:{range:100,spread:4,bulletSpeed:85},
  glock:{range:44,spread:12,bulletSpeed:70},p30l:{range:100,spread:2,bulletSpeed:94},
  ot38:{range:125,spread:1.25,bulletSpeed:112},ots38:{range:135,spread:1.2,bulletSpeed:115},
  colt45:{range:110,spread:16,bulletSpeed:106},m1911:{range:88,spread:2,bulletSpeed:80},
  m1a1:{range:88,spread:6,bulletSpeed:80},deagle:{range:120,spread:2.5,bulletSpeed:115},
  deagle_dual:{range:120,spread:3.5,bulletSpeed:115},flare_gun:{range:16,spread:1.25,bulletSpeed:4},
};

const GREN_FUSE={frag:4.0,mirv:4.0,mirv_mini:1.8,smoke:2.5,strobe:13.5,martyr:3.0};
const GREN_BLAST={frag:12,mirv:12,mirv_mini:8,smoke:12,strobe:2.5,martyr:8};
function getGrenFuse(t){if(!t)return 4;const k=t.toLowerCase();for(const[n,f]of Object.entries(GREN_FUSE))if(k.includes(n))return f;return 4;}
function getGrenBlast(t){if(!t)return 12;const k=t.toLowerCase();for(const[n,r]of Object.entries(GREN_BLAST))if(k.includes(n))return r;return 12;}
function lootColor(item){
  const t=(typeof item.type==='string'?item.type:'').toLowerCase();
  if(/awm|deagle|mk_20|sv-98|mosin|l86|nt-16|mirv|strobe|spas|usas|saiga|qbb|pkp|dp-28|m249|helmet03|vest03|pack03/.test(t))return'#ff66ff';
  if(/scar|m416|ak|akm|bar|garand|model_94|mp220|vector|p90|m79|frag|smoke|flare|helmet02|vest02|pack02/.test(t))return'#ffcc44';
  if(/m9|glock|ump|mp5|cz|p30|mac|helmet01|vest01|pack01/.test(t))return'#44aaff';
  return'#cccccc';
}
const MAP_COLORS={container_06:14074643,barn_01:6959775,stone_02:1646367,stone_04:15406938,stone_05:15406938,crate_03:5342557,bunker_storm_01:6959775,bunker_hydra_01:10030546,bunker_crossing_stairs_01b:13571226,bunker_crossing_stairs_01:13571226};
const MAP_SCALES={container_06:1,stone_02:6,barn_01:1,stone_04:6,stone_05:6,crate_03:1.8,bunker_storm_01:1.75,bunker_hydra_01:1.75,bunker_crossing_stairs_01b:2,bunker_crossing_stairs_01:2};

// ============================================================
//  STATE VARS
// ============================================================
const KEY_W=87,KEY_A=65,KEY_S=83,KEY_D=68;
let _nearest=null,_stickyTarget=null;
let _fps=0,_fpsFrames=0,_fpsLast=performance.now(),_ms=0,_msLast=performance.now();
let _magnetKeys={w:false,a:false,s:false,d:false};
let _zoomMultiplier=1.0,_zoomHooked=false,_grenCook=null;
let _baseStud=0,_layerSpoofActive=false,_layerOrig=null,_layerDescriptor=null,_layerKeyHeld=false;
const _thrownGrenades=new Map();
let _smoothSx=null,_smoothSy=null;

// ============================================================
//  AUTO-MAPPER — finds scrambled keys by shape, not by name
//  Runs independently, patches the live getter functions
// ============================================================
const _MAPPER_STORE = 'voidbacon.keymap';
let _lastScanResult = null;

const _isVec2      = v => v && typeof v==='object' && typeof v.x==='number' && typeof v.y==='number';
const _isUnitVec   = v => { if(!_isVec2(v))return false; const m=Math.hypot(v.x,v.y); return m>0.97&&m<1.03; };
const _isWorldPos  = v => { if(!_isVec2(v))return false; return Math.abs(v.x)>5&&Math.abs(v.y)>5&&Math.abs(v.x)<5000&&Math.abs(v.y)<5000; };
const _objKeys     = o => { try{return Object.keys(o);}catch{return[];} };
const _safeGet     = (o,k) => { try{return o[k];}catch{return undefined;} };
const _allProtoKeys= obj => {
  const out=new Set(); let p=obj;
  while(p&&p!==Object.prototype){Object.getOwnPropertyNames(p).forEach(k=>out.add(k));p=Object.getPrototypeOf(p);}
  return[...out];
};
const _poolArr = v => {
  if(!v||typeof v!=='object')return null;
  for(const k of _objKeys(v)){
    const a=_safeGet(v,k);
    if(Array.isArray(a)&&a.length>0&&typeof a[0]==='object'&&a[0].__id!=null)return{key:k,arr:a};
  }
  return null;
};

function runAutoMapper(){
  const app = window.__Re || Object.values(window).find(v=>v&&typeof v==='object'&&'audioManager'in v&&'pixi'in v&&'game'in v);
  if(!app)return{error:'App not found — load the game first'};

  const game=app.game;
  const R={};
  const missing=[];
  const log=[];

  const found=(label,key)=>{ R[label]=key; log.push(`✅ ${label} → ${key}`); return key; };
  const nope =(label)=>{ R[label]=null; missing.push(label); log.push(`❌ ${label} — not found`); return null; };

  // ── GAME LEVEL ──────────────────────────────────────────────
  const barnKey=_objKeys(game).find(k=>{const v=_safeGet(game,k);return v&&v.playerPool&&typeof v.playerInfo==='object';});
  barnKey?found('barnKey',barnKey):nope('barnKey');

  const inputKey=_objKeys(game).find(k=>{const v=_safeGet(game,k);return v&&(_isVec2(v.mousePos)||_isVec2(v.mouse))&&('mouseButtons'in v||'keys'in v);});
  inputKey?found('inputKey',inputKey):nope('inputKey');

  const meKey=_objKeys(game).find(k=>{const v=_safeGet(game,k);return v&&typeof v==='object'&&v.__id!=null&&v.container&&typeof v.active==='boolean'&&typeof v.layer==='number';});
  meKey?found('meKey',meKey):nope('meKey');

  const me=meKey?_safeGet(game,meKey):null;

  let activeIdKey=null;
  if(me?.__id!=null){activeIdKey=_objKeys(game).find(k=>{const v=_safeGet(game,k);return typeof v==='number'&&v===me.__id&&k.length<=8;});}
  activeIdKey?found('activeIdKey',activeIdKey):nope('activeIdKey');

  const pixiKey=_objKeys(game).find(k=>{const v=_safeGet(game,k);return v&&v.stage&&Array.isArray(v.stage.children);});
  pixiKey?found('pixiKey',pixiKey):nope('pixiKey');

  const lootBarnKey=_objKeys(game).find(k=>{const v=_safeGet(game,k);return v&&v.lootPool;});
  lootBarnKey?found('lootBarnKey',lootBarnKey):nope('lootBarnKey');

  const projBarnKey=_objKeys(game).find(k=>{const v=_safeGet(game,k);return v&&v.projectilePool;});
  projBarnKey?found('projBarnKey',projBarnKey):nope('projBarnKey');

  const mapKey=_objKeys(game).find(k=>{const v=_safeGet(game,k);return v&&(typeof v.mapName==='string'||v.mapData||typeof v.getGroundSurface==='function');});
  mapKey?found('mapKey',mapKey):nope('mapKey');

  // ── MAP LEVEL ───────────────────────────────────────────────
  const mapObj=mapKey?_safeGet(game,mapKey):null;

  const obstKey=mapObj?_objKeys(mapObj).find(k=>{const p=_poolArr(_safeGet(mapObj,k));return p&&p.arr.some(o=>o&&typeof o.type==='string'&&_isVec2(o.pos));}):null;
  obstKey?found('obstKey',obstKey):nope('obstKey');

  const buildKey=mapObj?_objKeys(mapObj).find(k=>{const p=_poolArr(_safeGet(mapObj,k));return p&&p.arr.some(o=>o&&(o.ceiling||o.imgs||o.ceilingImg));}):null;
  buildKey?found('buildKey',buildKey):nope('buildKey');

  const obstPool=obstKey&&mapObj?_safeGet(mapObj,obstKey):null;
  const poolKey=_poolArr(obstPool)?.key;
  poolKey?found('poolKey',poolKey):nope('poolKey');

  // Also try playerPool arr key
  const barnObj=barnKey?_safeGet(game,barnKey):null;
  const playerPoolKey=_poolArr(barnObj?.playerPool)?.key;
  playerPoolKey?found('playerPoolKey',playerPoolKey):nope('playerPoolKey');

  // ── PLAYER LEVEL ────────────────────────────────────────────
  if(me){
    const pkeys=_allProtoKeys(me);

    const posKey=pkeys.filter(k=>{try{return _isWorldPos(me[k]);}catch{return false;}})
      .filter(k=>!k.toLowerCase().includes('sprite')&&!k.toLowerCase().includes('container'))
      .sort()[0];
    posKey?found('posKey',posKey):nope('posKey');

    const dirKey=pkeys.filter(k=>{try{return _isUnitVec(me[k]);}catch{return false;}})[0];
    dirKey?found('dirKey',dirKey):nope('dirKey');

    const weapKey=pkeys.find(k=>{try{const v=me[k];return typeof v==='string'&&(v==='fists'||/^[a-z0-9_]{1,20}$/.test(v))&&k.toLowerCase().includes('weap');}catch{return false;}})
      ||pkeys.find(k=>{try{const v=me[k];return typeof v==='string'&&v==='fists';}catch{return false;}});
    weapKey?found('weapKey',weapKey):nope('weapKey');

    const throwKey=pkeys.find(k=>{try{const v=me[k];return v==='equip'||v==='cook'||v==='throw';}catch{return false;}});
    throwKey?found('throwKey',throwKey):nope('throwKey');
  }

  R._log=log;
  R._missing=missing;
  R._ok=missing.length===0;
  return R;
}

function applyMapperResult(R){
  if(!R||R.error)return false;
  // Patch the live getter functions used throughout the script
  // These are re-evaluated every call so patching them here is enough
  window.__VB_KEYMAP__ = R;
  localStorage.setItem(_MAPPER_STORE, JSON.stringify(
    Object.fromEntries(Object.entries(R).filter(([k])=>!k.startsWith('_')))
  ));
  _lastScanResult = R;
  return true;
}

function loadSavedKeymap(){
  try{
    const saved=JSON.parse(localStorage.getItem(_MAPPER_STORE)||'null');
    if(saved){_lastScanResult=saved;window.__VB_KEYMAP__=saved;}
  }catch(e){}
}

// ── Getter functions that respect the keymap ──────────────────
function _km(field){ return _lastScanResult?.[field]??null; }

function isAppObj(a){try{return a&&typeof a==='object'&&'audioManager'in a&&'pixi'in a&&'game'in a&&'inputBinds'in a;}catch{return false;}}
function poolArr(p){
  const k=_km('poolKey')||_km('playerPoolKey');
  if(k&&p&&Array.isArray(p[k]))return p[k];
  return p?.m_getPool?.()??p?.m_pool??p?.aTL??p?.cAGTlU??[];
}
function getGame(){return window.__SurvevDev?.game??findRe()?.game??null;}
function getBarn(game){
  const k=_km('barnKey');
  return(k&&game?.[k])||window.__SurvevDev?.barn||game?.gAl||game?.DaWsN||game?.m_playerBarn||null;
}
function getInput(game){
  const k=_km('inputKey');
  return(k&&game?.[k])||window.__SurvevDev?.input||game?.DyO||game?.evqDZ||game?.m_input||findRe()?.input||null;
}
function getMe(game){
  const k=_km('meKey');
  return(k&&game?.[k])||window.__SurvevDev?.me||game?.agncI||game?.zSsh||game?.m_activePlayer||null;
}
function getMyId(game){
  const k=_km('activeIdKey');
  return(k&&game?.[k])||window.__SurvevDev?.myId||game?.pRZlU||game?.zYO||game?.m_activeId||getMe(game)?.__id||null;
}
function getWorldContainer(game){
  const k=_km('pixiKey');
  const pixi=k?game?.[k]:null;
  return(pixi?.stage?.children?.[0])||window.__SurvevDev?.world||game?.qtqkB?.stage?.children?.[0]||game?.WNNSH?.stage?.children?.[0]||game?.m_pixi?.stage?.children?.[0]||null;
}
function getObstacles(game){
  const mapK=_km('mapKey'),obstK=_km('obstKey'),poolK=_km('poolKey');
  const mapObj=mapK?game?.[mapK]:null;
  const pool=obstK&&mapObj?mapObj[obstK]:null;
  if(pool&&poolK&&Array.isArray(pool[poolK]))return pool[poolK];
  return window.__SurvevDev?.obstacles??poolArr(game?.ttHlEc?.ZeD??game?.iNj?.wDx??game?.m_map?.m_obstaclePool)??[];
}
function getBuildingPool(game){
  const mapK=_km('mapKey'),buildK=_km('buildKey'),poolK=_km('poolKey');
  const mapObj=mapK?game?.[mapK]:null;
  const pool=buildK&&mapObj?mapObj[buildK]:null;
  if(pool&&poolK&&Array.isArray(pool[poolK]))return pool[poolK];
  return window.__SurvevDev?.buildings??poolArr(game?.ttHlEc?.nDHYkP??game?.iNj?.FMxekW??game?.m_map?.m_buildingPool)??[];
}
function getPlayers(barn){
  const k=_km('playerPoolKey')||_km('poolKey');
  if(k&&barn?.playerPool&&Array.isArray(barn.playerPool[k]))return barn.playerPool[k];
  return window.__SurvevDev?.players??poolArr(barn?.playerPool)??[];
}
function getLoot(game){
  const lootK=_km('lootBarnKey'),poolK=_km('poolKey');
  const lb=lootK?game?.[lootK]:null;
  if(lb&&poolK&&lb.lootPool&&Array.isArray(lb.lootPool[poolK]))return lb.lootPool[poolK];
  return window.__SurvevDev?.loot??poolArr(game?.rDwHRG?.lootPool??game?.xGaQs?.lootPool??game?.m_lootBarn?.lootPool)??[];
}
function getProjectiles(game){
  const projK=_km('projBarnKey'),poolK=_km('poolKey');
  const pb=projK?game?.[projK]:null;
  if(pb&&poolK&&pb.projectilePool&&Array.isArray(pb.projectilePool[poolK]))return pb.projectilePool[poolK];
  return window.__SurvevDev?.projectiles??poolArr(game?.RLl?.projectilePool??game?.wIXF?.projectilePool??game?.m_projectileBarn?.projectilePool)??[];
}
function getPlayerPos(p){
  const k=_km('posKey');
  if(k&&p&&_isWorldPos(p[k]))return p[k];
  return p?.tGUqfM??p?.kYDJg??p?.rvyyT??p?.dMlpwc??p?.iUBbYp??p?.UrupxR??p?.m_pos??p?.pos??null;
}
function getPlayerDir(p){
  const k=_km('dirKey');
  if(k&&p&&_isUnitVec(p[k]))return p[k];
  return p?.SIpSSa??p?.zeq??p?.EZOCU??p?.rdhj??p?.rob??p?.tvwkM??p?.m_dir??null;
}
function isPlayerDead(p){return p?.m_netData?.m_dead??p?.dead??false;}
function isPlayerDowned(p){return p?.m_netData?.m_downed??p?.downed??false;}
function findRe(){
  if(isAppObj(window.__Re))return window.__Re;
  if(isAppObj(window.__SurvevDev?.app))return window.__SurvevDev.app;
  return null;
}

// Auto-finder inject (same as before)
const inject=document.createElement('script');
inject.textContent=`(function(){
  const origCall=Function.prototype.call;
  function isApp(v){try{return v!=null&&typeof v==='object'&&'audioManager'in v&&'pixi'in v&&'game'in v&&'inputBinds'in v;}catch(e){return false;}}
  function def(o,k,g){try{if(o&&!(k in o))Object.defineProperty(o,k,{configurable:true,enumerable:false,get:g});}catch(e){}}
  function arr(p){return p?.aTL??p?.cAGTlU??p?.m_pool??p?.m_getPool?.()??[];}
  function aliasPool(p){def(p,'aTL',function(){return this.cAGTlU??this.m_pool??this.m_getPool?.()??[];});}
  function aliasPlayer(p){
    if(!p||typeof p!=='object')return;
    def(p,'tGUqfM',function(){return this.rvyyT??this.dMlpwc??this.iUBbYp??this.UrupxR??this.m_pos??this.pos??null;});
    def(p,'kYDJg',function(){return this.tGUqfM;});
    def(p,'SIpSSa',function(){return this.EZOCU??this.rdhj??this.rob??this.tvwkM??this.m_dir??null;});
    def(p,'zeq',function(){return this.SIpSSa;});
  }
  function aliasGame(game){
    if(!game||typeof game!=='object')return false;
    def(game,'gAl',function(){return this.DaWsN??this.m_playerBarn;});
    def(game,'DyO',function(){return this.evqDZ??this.m_input;});
    def(game,'agncI',function(){return this.zSsh??this.m_activePlayer;});
    def(game,'pRZlU',function(){return this.zYO??this.m_activeId;});
    def(game,'qtqkB',function(){return this.WNNSH??this.m_pixi;});
    def(game,'rDwHRG',function(){return this.xGaQs??this.m_lootBarn;});
    def(game,'RLl',function(){return this.wIXF??this.m_projectileBarn;});
    def(game,'ttHlEc',function(){const m=this.iNj??this.m_map;return m?{ZeD:m.wDx??m.m_obstaclePool,nDHYkP:m.FMxekW??m.m_buildingPool}:undefined;});
    aliasPool(game.gAl?.playerPool);aliasPool(game.ttHlEc?.ZeD);aliasPool(game.ttHlEc?.nDHYkP);aliasPool(game.rDwHRG?.lootPool);aliasPool(game.RLl?.projectilePool);
    for(const p of arr(game.gAl?.playerPool))aliasPlayer(p);aliasPlayer(game.agncI);
    try{if(!window.__SurvevDev)Object.defineProperty(window,'__SurvevDev',{configurable:true,get(){return{app:window.__Re,game,input:game.DyO,barn:game.gAl,me:game.agncI,myId:game.pRZlU,pixi:game.qtqkB,world:game.qtqkB?.stage?.children?.[0],players:arr(game.gAl?.playerPool),obstacles:arr(game.ttHlEc?.ZeD),buildings:arr(game.ttHlEc?.nDHYkP),loot:arr(game.rDwHRG?.lootPool),projectiles:arr(game.RLl?.projectilePool)}}});}catch(e){}
    return true;
  }
  function aliasApp(app){if(!isApp(app))return false;window.__Re=app;return aliasGame(app.game);}
  if(!isApp(window.__Re)){
    Function.prototype.call=function(thisArg){
      if(!isApp(window.__Re)&&isApp(thisArg)){window.__Re=thisArg;aliasApp(thisArg);Function.prototype.call=origCall;}
      return Reflect.apply(origCall,this,arguments);
    };
    setTimeout(()=>{if(Function.prototype.call!==origCall)Function.prototype.call=origCall;},30000);
  }
  setInterval(()=>{if(isApp(window.__Re))aliasApp(window.__Re);},250);
})();`;
(document.head||document.documentElement).appendChild(inject);inject.remove();

// ============================================================
//  WALLCHECK
// ============================================================
const _WALL_NAMES=['metal_wall_','brick_wall_','concrete_wall_','stone_wall_','container_wall_','_wall_int_','bank_wall_','barn_wall_','cabin_wall_','hut_wall_','house_wall_','mansion_wall_','police_wall_','shack_wall_','outhouse_wall_','teahouse_wall_','warehouse_wall_','silo_','bollard_','sandbags_','hedgehog'];
const _SOFT_NAMES=['tree_','bush_','brush_','crate_','barrel_','refrigerator_','control_panel_','chest_','case_','oven_','bed_','bookshelf_','couch_','table_','drawers_','window','glass_wall_','locker_','deposit_box_','toilet_'];
function isHardWall(obs){
  if(!obs||obs.dead)return false;if(obs.collidable===false)return false;if(obs.isWall===true)return true;
  const n=obs.type||'';for(const w of _WALL_NAMES)if(n.includes(w))return true;
  for(const s of _SOFT_NAMES)if(n.includes(s))return false;if(obs.destructible===false)return true;return false;
}
function _rayHitsAABB(ax,ay,bx,by,mnX,mnY,mxX,mxY){
  const dx=bx-ax,dy=by-ay;let tmin=0,tmax=1;
  if(Math.abs(dx)<1e-9){if(ax<mnX||ax>mxX)return false;}
  else{let t1=(mnX-ax)/dx,t2=(mxX-ax)/dx;if(t1>t2){const tmp=t1;t1=t2;t2=tmp;}tmin=Math.max(tmin,t1);tmax=Math.min(tmax,t2);if(tmin>tmax)return false;}
  if(Math.abs(dy)<1e-9){if(ay<mnY||ay>mxY)return false;}
  else{let t1=(mnY-ay)/dy,t2=(mxY-ay)/dy;if(t1>t2){const tmp=t1;t1=t2;t2=tmp;}tmin=Math.max(tmin,t1);tmax=Math.min(tmax,t2);if(tmin>tmax)return false;}
  return tmin<tmax;
}
function _rayHitsCircle(ax,ay,bx,by,cx,cy,r){
  const dx=bx-ax,dy=by-ay,len2=dx*dx+dy*dy;if(len2<1e-12)return false;
  const ox=cx-ax,oy=cy-ay,t=Math.max(0,Math.min(1,(ox*dx+oy*dy)/len2));
  const px=ax+t*dx,py=ay+t*dy;return(cx-px)*(cx-px)+(cy-py)*(cy-py)<r*r;
}
function hasLOS(game,myPos,tPos){
  if(!cfg.wallcheck||!myPos||!tPos)return true;
  try{
    const obs=getObstacles(game);if(!obs.length)return true;
    const ax=myPos.x,ay=myPos.y,bx=tPos.x,by=tPos.y;
    for(const o of obs){
      if(!o||!o.active||o.dead||!isHardWall(o))continue;const c=o.collider;if(!c)continue;
      if(c.type===1){if(_rayHitsAABB(ax,ay,bx,by,c.min.x,c.min.y,c.max.x,c.max.y))return false;}
      else if(c.type===0){if(_rayHitsCircle(ax,ay,bx,by,c.pos.x,c.pos.y,c.rad))return false;}
    }
    return true;
  }catch(e){return true;}
}

// ============================================================
//  PREDICTION
// ============================================================
const PRED_HIST=12,PRED_MAX_DT=0.25,PRED_MAX_V=18,PRED_EMA=0.55;
const _predState=new Map();
function _solveIntercept(dx,dy,vx,vy,S){
  const a=vx*vx+vy*vy-S*S,b=2*(dx*vx+dy*vy),c=dx*dx+dy*dy;let t;
  if(Math.abs(a)<1e-6){if(Math.abs(b)<1e-6)return null;t=-c/b;}
  else{const disc=b*b-4*a*c;if(disc<0)return null;const sq=Math.sqrt(disc);const t1=(-b-sq)/(2*a),t2=(-b+sq)/(2*a);const v1=t1>1e-3?t1:Infinity,v2=t2>1e-3?t2:Infinity;t=Math.min(v1,v2);}
  if(!isFinite(t)||t<1e-3||t>3.5)return null;return t;
}
function getPredictedWorldPos(target,myWorldPos,bulletSpeed){
  const id=target.__id,now=performance.now();const cur=getPlayerPos(target);if(!cur)return null;
  if(!_predState.has(id))_predState.set(id,{buf:[],vx:0,vy:0});
  const st=_predState.get(id);st.buf.push({x:cur.x,y:cur.y,t:now});
  if(st.buf.length>PRED_HIST)st.buf.shift();if(st.buf.length<2)return null;
  const recent=[];
  for(let i=1;i<st.buf.length;i++){
    const a=st.buf[i-1],b=st.buf[i],dt=(b.t-a.t)/1000;
    if(dt<=0||dt>PRED_MAX_DT)continue;if(Math.hypot((b.x-a.x)/dt,(b.y-a.y)/dt)>PRED_MAX_V*2.5)continue;
    recent.push({x:b.x,y:b.y,t:b.t/1000});
  }
  if(!recent.length)return null;const N=recent.length;
  let sw=0,swt=0,swt2=0,swx=0,swy=0,swxt=0,swyt=0;
  for(let i=0;i<N;i++){const w=Math.exp((i+1)/N),{x,y,t}=recent[i];sw+=w;swt+=w*t;swt2+=w*t*t;swx+=w*x;swy+=w*y;swxt+=w*x*t;swyt+=w*y*t;}
  const denom=sw*swt2-swt*swt;if(Math.abs(denom)<1e-12)return null;
  let rvx=(sw*swxt-swt*swx)/denom,rvy=(sw*swyt-swt*swy)/denom;
  const spd=Math.hypot(rvx,rvy);if(spd>PRED_MAX_V){const sc=PRED_MAX_V/spd;rvx*=sc;rvy*=sc;}
  st.vx=PRED_EMA*rvx+(1-PRED_EMA)*st.vx;st.vy=PRED_EMA*rvy+(1-PRED_EMA)*st.vy;
  const t=_solveIntercept(cur.x-myWorldPos.x,cur.y-myWorldPos.y,st.vx,st.vy,bulletSpeed);
  if(t===null)return null;return{x:cur.x+st.vx*t,y:cur.y+st.vy*t};
}

// ============================================================
//  AIM / MAGNET / MISC
// ============================================================
const FIRE_GATE_RAD=2.5*(Math.PI/180);
function applyAimLock(tsx,tsy,input){
  if(!input)return;let fx=tsx,fy=tsy;
  if(cfg.smoothAim){
    const f=0.04+(cfg.smoothAmount/100)*0.31;
    if(_smoothSx===null){_smoothSx=input.mousePos.x;_smoothSy=input.mousePos.y;}
    _smoothSx+=(tsx-_smoothSx)*f;_smoothSy+=(tsy-_smoothSy)*f;fx=_smoothSx;fy=_smoothSy;
  }else{_smoothSx=null;_smoothSy=null;}
  input.mousePos.x=fx;input.mousePos.y=fy;
}
function clearMagnetKeys(input){
  if(!input)return;
  if(_magnetKeys.w){input.keys[KEY_W]=false;_magnetKeys.w=false;}
  if(_magnetKeys.a){input.keys[KEY_A]=false;_magnetKeys.a=false;}
  if(_magnetKeys.s){input.keys[KEY_S]=false;_magnetKeys.s=false;}
  if(_magnetKeys.d){input.keys[KEY_D]=false;_magnetKeys.d=false;}
}
function applyMagnet(input,me,msx,msy,tsx,tsy,distStuds){
  if(!input||distStuds>6){clearMagnetKeys(input);return false;}
  const dx=tsx-msx,dy=tsy-msy,len=Math.hypot(dx,dy);
  if(len>0.5){const nx=dx/len,ny=dy/len,thr=0.3;input.keys[KEY_W]=ny<-thr;_magnetKeys.w=input.keys[KEY_W];input.keys[KEY_S]=ny>thr;_magnetKeys.s=input.keys[KEY_S];input.keys[KEY_A]=nx<-thr;_magnetKeys.a=input.keys[KEY_A];input.keys[KEY_D]=nx>thr;_magnetKeys.d=input.keys[KEY_D];}
  input.mousePos.x=tsx;input.mousePos.y=tsy;
  const dir=getPlayerDir(me);
  if(dir&&typeof dir.x==='number'){const ga=Math.atan2(-dir.y,dir.x),ta=Math.atan2(tsy-msy,tsx-msx);let err=Math.abs(ga-ta);if(err>Math.PI)err=Math.PI*2-err;if(err<=FIRE_GATE_RAD){input.mouseButtons[0]=true;setTimeout(()=>{if(input?.mouseButtons)input.mouseButtons[0]=false;},30);}}
  return true;
}
function doLayerSpoof(game){const me=getMe(game);if(!me||typeof me.layer==='undefined')return;try{if(!_layerSpoofActive){_layerOrig=me.layer;_layerDescriptor=Object.getOwnPropertyDescriptor(me,'layer')||Object.getOwnPropertyDescriptor(Object.getPrototypeOf(me),'layer');const spoof=me.layer===0?1:0;Object.defineProperty(me,'layer',{configurable:true,get:()=>spoof,set(){}});if(me.container)me.container.alpha=0.5;_layerSpoofActive=true;}}catch(e){}}
function undoLayerSpoof(game){const me=getMe(game);if(!me)return;try{if(_layerSpoofActive){if(_layerDescriptor)Object.defineProperty(me,'layer',_layerDescriptor);else if(_layerOrig!==null)me.layer=_layerOrig;if(me.container)me.container.alpha=1;_layerSpoofActive=false;_layerOrig=null;_layerDescriptor=null;}}catch(e){_layerSpoofActive=false;}}
let _mapHookInstalled=false;
function installMapHighlights(){if(_mapHookInstalled||!cfg.mapHighlights)return;try{const origSort=Array.prototype.sort;Array.prototype.sort=function(...args){try{if(cfg.mapHighlights&&this.length>0&&this[0]?.obj?.ori!=null){this.forEach(e=>{if(!e||!e.obj)return;const col=MAP_COLORS[e.obj.type];const sc=MAP_SCALES[e.obj.type];if(col!=null&&sc!=null&&e.shapes){e.shapes.forEach(s=>{s.color=col;s.scale=sc;});e.zIdx=999;}});}}catch(e2){}return origSort.apply(this,args);};_mapHookInstalled=true;}catch(e){}}
const AUTO_DOOR_INTERACT_RANGE=3.5;const _doorPressTimes=new Map();
function _doorInRange(o,myPos){if(!o||!myPos)return false;const extra=(o.door?.interactionRad??AUTO_DOOR_INTERACT_RANGE)+1.15;const c=o.collider;if(c?.type===1&&c.min&&c.max){const px=Math.max(c.min.x,Math.min(c.max.x,myPos.x)),py=Math.max(c.min.y,Math.min(c.max.y,myPos.y));return Math.hypot(myPos.x-px,myPos.y-py)<=extra+0.25;}if(c?.type===0&&c.pos){return Math.hypot(c.pos.x-myPos.x,c.pos.y-myPos.y)<=((c.rad??0)+extra+0.25);}const p=o.door?.closedPos??o.pos;return p&&Math.hypot(p.x-myPos.x,p.y-myPos.y)<=extra+1.5;}
function _pressInteract(input,game){if(input?.keys){input.keysOld[70]=false;input.keys[70]=true;setTimeout(()=>{if(input?.keys){input.keys[70]=false;input.keysOld[70]=false;}},90);}try{const uiMgr=Object.values(game||{}).find(v=>v&&typeof v==='object'&&'interactionTouched'in v);if(uiMgr){uiMgr.interactionTouched=true;setTimeout(()=>{if(uiMgr)uiMgr.interactionTouched=false;},90);}}catch(e){}}
let _lastDoorTime=0;
function tickAutoDoor(game,input){if(!cfg.autoDoor||!input)return;const myPos=getPlayerPos(getMe(game));if(!myPos)return;const now=Date.now();for(const o of getObstacles(game)){if(!o?.active||!o.isDoor||!o.pos)continue;if(o.door?.open===true){_doorPressTimes.delete(o.__id);continue;}if(o.door?.locked===true||o.door?.canUse===false)continue;if(!_doorInRange(o,myPos))continue;const last=_doorPressTimes.get(o.__id)??0;if(now-last<260)continue;_doorPressTimes.set(o.__id,now);_lastDoorTime=Date.now();_pressInteract(input,game);break;}}
const LOOT_SKIP=new Set(['gun','melee','chest','helmet','scope','backpack','outfit','perk','xp']);
const AUTO_LOOT_RANGE=2.5,AUTO_LOOT_INTERVAL=150;let _lastLootTime=0;
const _LOOT_CAT={bandage:'heal',healthkit:'heal',soda:'boost',painkiller:'boost',pills:'boost',adren:'boost','9mm':'ammo','762mm':'ammo','556mm':'ammo','50AE':'ammo','12gauge':'ammo','308sub':'ammo','45acp':'ammo','40mm':'ammo',flare:'ammo',frag:'throwable',mirv:'throwable',smoke:'throwable',strobe:'throwable',potato:'throwable',martyr:'throwable',chest01:'chest',chest02:'chest',chest03:'chest',chest04:'chest',helmet01:'helmet',helmet02:'helmet',helmet03:'helmet',helmet04:'helmet','1xscope':'scope','2xscope':'scope','4xscope':'scope','8xscope':'scope','15xscope':'scope',pan:'melee',crowbar:'melee',katana:'melee',fists:'melee',naginata:'melee'};
function _lootCat(type){if(!type)return'gun';if(_LOOT_CAT[type])return _LOOT_CAT[type];const t=type.toLowerCase();if(t.includes('scope'))return'scope';if(t.includes('helmet'))return'helmet';if(t.includes('chest')||t.includes('vest'))return'chest';if(t.includes('pack')||t.includes('backpack'))return'backpack';if(/mm$|gauge$|acp$|sub$|ae$/.test(t))return'ammo';return'gun';}
function tickAutoLoot(game,input){if(!cfg.autoLoot||!input)return;const myPos=getPlayerPos(getMe(game));if(!myPos)return;const now=Date.now();if(now-_lastLootTime<AUTO_LOOT_INTERVAL)return;if(now-_lastDoorTime<500)return;const lootBarnK=_km('lootBarnKey');const lb=lootBarnK?game[lootBarnK]:null;const closest=(lb??game.rDwHRG??game.xGaQs??game.m_lootBarn)?.closestLoot;if(closest?.active&&!LOOT_SKIP.has(_lootCat(closest.type))){_lastLootTime=now;input.keys[70]=true;setTimeout(()=>{if(input?.keys)input.keys[70]=false;},50);return;}let bestDist=AUTO_LOOT_RANGE,bestLoot=null;for(const l of getLoot(game)){if(!l?.active||!l.pos)continue;if(l.ownerId&&l.ownerId!==getMyId(game))continue;if(LOOT_SKIP.has(_lootCat(l.type)))continue;const d=Math.hypot(l.pos.x-myPos.x,l.pos.y-myPos.y);if(d<bestDist){bestDist=d;bestLoot=l;}}if(bestLoot){_lastLootTime=now;input.keys[70]=true;setTimeout(()=>{if(input?.keys)input.keys[70]=false;},50);}}
function hookZoom(){if(_zoomHooked)return true;const game=getGame();if(!game)return false;const player=getMe(game);if(!player)return false;const proto=Object.getPrototypeOf(player);const key=Object.getOwnPropertyNames(proto).find(k=>{try{const f=proto[k]?.toString()??'';return f.includes('mobile')&&f.length<300&&typeof proto[k]==='function';}catch{return false;}});if(!key)return false;const orig=proto[key];proto[key]=function(...a){return orig.apply(this,a)*_zoomMultiplier;};_zoomHooked=true;return true;}
function setZoom(m){if(!settings.zoomEnabled)return;if(!_zoomHooked)hookZoom();_zoomMultiplier=m;updateZoomUI();}
function updateZoomUI(){const el=document.getElementById('_vmZoomVal');if(el)el.textContent=(1/_zoomMultiplier).toFixed(2)+'x';}
const MACRO_STEPS=[inp=>{if(inp?.mouseButtons){inp.mouseButtons[0]=true;setTimeout(()=>{if(inp?.mouseButtons)inp.mouseButtons[0]=false;},25);}},inp=>{if(inp?.keys){inp.keys[51]=true;setTimeout(()=>{if(inp?.keys)inp.keys[51]=false;},25);}},inp=>{if(inp?.keys){inp.keys[52]=true;setTimeout(()=>{if(inp?.keys)inp.keys[52]=false;},25);}}];
const macro={running:false,stepIdx:0,delay:100,cycles:0,_timeout:null,tick(){if(!this.running)return;const inp=getInput(getGame())??null;if(inp)MACRO_STEPS[this.stepIdx](inp);this.stepIdx++;if(this.stepIdx>=MACRO_STEPS.length){this.stepIdx=0;this.cycles++;}this._timeout=setTimeout(()=>this.tick(),Math.max(10,this.delay));},start(){if(!settings.throwerEnabled||this.running)return;this.running=true;this.stepIdx=0;this.cycles=0;this.tick();updateMacroUI();},stop(){this.running=false;clearTimeout(this._timeout);this.stepIdx=0;updateMacroUI();},toggle(){this.running?this.stop():this.start();},setDelay(ms){this.delay=Math.max(10,Math.min(200,ms));const el=document.getElementById('_vmMacroDelay');if(el)el.textContent=this.delay+'ms';const sl=document.getElementById('_vmMacroSlider');if(sl)sl.value=this.delay;}};
window.SurvMacro=macro;
function updateMacroUI(){const el=document.getElementById('_vmtgl-thrower');if(el)el.classList.toggle('on',macro.running);}
function applyXray(buildings){for(const b of buildings){if(!b||!b.active)continue;if(b.ceiling)b.ceiling.fadeAlpha=0;for(const img of(b.imgs||[]))if(img&&img.isCeiling&&img.sprite)img.sprite.alpha=0;}}
function tickXray(game){if(!cfg.xray)return;applyXray(getBuildingPool(game));for(const o of getObstacles(game)){if(!o||!o.active)continue;const t=(typeof o.type==='string'?o.type:'').toLowerCase();if(/^tree|^bush|^hedge|^flower|^vine/.test(t)){if(o.sprite)o.sprite.alpha=0.15;if(o.img?.sprite)o.img.sprite.alpha=0.15;}}}
function restoreXray(game){for(const b of getBuildingPool(game)){if(!b)continue;if(b.ceiling)b.ceiling.fadeAlpha=1;for(const img of(b.imgs||[]))if(img&&img.isCeiling&&img.sprite)img.sprite.alpha=img.sprite.imgAlpha??1;}}
function restoreXrayObs(game){for(const o of getObstacles(game)){if(!o||!o.active)continue;const t=(typeof o.type==='string'?o.type:'').toLowerCase();if(/^tree|^bush|^hedge|^flower|^vine/.test(t)){if(o.sprite)o.sprite.alpha=1;if(o.img?.sprite)o.img.sprite.alpha=1;}}}

// ============================================================
//  STATS
// ============================================================
const modeMap={1:'Solo',2:'Duo',3:'Trio',4:'Squad'};
function timeAgo(d){const s=Math.floor((Date.now()-new Date(d))/1000);return s<60?`${s}s`:s<3600?`${Math.floor(s/60)}m`:s<86400?`${Math.floor(s/3600)}h`:`${Math.floor(s/86400)}d`;}
function fetchStats(slug){
  if(!slug)return;
  const box=document.getElementById('_vmStatsBox'),hist=document.getElementById('_vmHistoryBox');
  if(box)box.innerHTML='<span style="color:#888;font-size:12px">loading...</span>';
  if(hist)hist.innerHTML='<span style="color:#888;font-size:12px">loading...</span>';
  fetch('https://api.survev.io/api/user_stats',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({slug,interval:'alltime',mapIdFilter:'-1'})})
  .then(r=>r.json()).then(d=>{if(!box)return;if(!d||d.banned){box.innerHTML='<span style="color:#e87a7a">not found</span>';return;}const kpg=d.games>0?(d.kills/d.games).toFixed(2):'0.00';const wPct=d.games>0?((d.wins/d.games)*100).toFixed(1):'0.0';box.innerHTML=`<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:6px;text-align:center">${[['WINS',d.wins??0],['KILLS',d.kills??0],['GAMES',d.games??0],['K/G',kpg],['WIN%',wPct+'%']].map(([l,v])=>`<div style="padding:9px 4px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);border-radius:8px"><div style="font-size:17px;font-weight:700;color:#ebecf0">${v}</div><div style="font-size:9px;color:#7b7f88;letter-spacing:1px;margin-top:2px">${l}</div></div>`).join('')}</div>`;}).catch(()=>{if(box)box.innerHTML='<span style="color:#e87a7a">error</span>';});
  fetch('https://api.survev.io/api/match_history',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({slug,offset:0,count:10,teamModeFilter:7})})
  .then(r=>r.json()).then(matches=>{if(!hist)return;if(!Array.isArray(matches)||!matches.length){hist.innerHTML='<span style="color:#555;font-size:12px">no matches</span>';return;}hist.innerHTML=`<div style="border-radius:9px;overflow:hidden;border:1px solid rgba(255,255,255,.07)"><div style="display:grid;grid-template-columns:55px 55px 1fr 70px 42px;gap:6px;padding:7px 14px;background:rgba(255,255,255,.03)">${['RANK','MODE','KILLS','DMG','AGO'].map(h=>`<span style="font-size:9px;color:#7b7f88;letter-spacing:1px">${h}</span>`).join('')}</div>${matches.map(m=>{const rCol=m.rank===1?'#5bc470':m.rank<=3?'#e8c060':'#555';const mCol=m.team_count===1?'#c8a8ff':m.team_count===2?'#80c8ff':m.team_count===3?'#80e8a0':'#ffb060';return`<div style="display:grid;grid-template-columns:55px 55px 1fr 70px 42px;gap:6px;align-items:center;padding:8px 14px;border-bottom:1px solid rgba(255,255,255,.05);cursor:pointer" onclick="window._vmShowMatch('${m.guid}','${slug}')"><span style="font-size:11px;font-weight:700;color:${rCol}">${m.rank===1?'1st':'#'+m.rank}</span><span style="font-size:11px;font-weight:600;color:${mCol}">${modeMap[m.team_count]||'?'}</span><span style="font-size:11px;color:#9699a3">${m.kills??0}k/${m.team_kills??0}tk</span><span style="font-size:11px;color:#7b7f88">${Math.round((m.damage_dealt??0)/1000*10)/10}k</span><span style="font-size:11px;color:#7b7f88;text-align:right">${timeAgo(m.end_time)}</span></div>`;}).join('')}</div>`;}).catch(()=>{if(hist)hist.innerHTML='<span style="color:#e87a7a">error</span>';});
}
window._vmShowMatch=function(guid,slug){const detail=document.getElementById('_vmMatchDetail');if(!detail)return;detail.style.display='block';detail.innerHTML='<div style="color:#7b7f88;padding:12px;font-size:12px">loading...</div>';fetch('https://api.survev.io/api/match_data',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({gameId:guid})}).then(r=>r.json()).then(players=>{if(!Array.isArray(players)||!players.length){detail.innerHTML='<span style="color:#e87a7a;padding:12px">no data</span>';return;}const rows=players.map(p=>{const isMe=p.slug===slug;const t=Math.round(p.time_alive??0);const rCol=p.rank===1?'#5bc470':p.rank<=3?'#e8c060':'#555';return`<div style="display:grid;grid-template-columns:40px 1fr 36px 64px 64px 56px;gap:5px;align-items:center;padding:7px 14px;border-bottom:1px solid rgba(255,255,255,.05);background:${isMe?'rgba(91,196,112,0.08)':''}"><span style="font-size:11px;font-weight:700;color:${rCol}">#${p.rank}</span><span style="font-size:11px;font-weight:${isMe?700:400};color:${isMe?'#5bc470':'#9699a3'};overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${p.slug||p.username||'?'}</span><span style="font-size:11px;color:#9699a3">${p.kills??0}</span><span style="font-size:11px;color:#7b7f88">${Math.round(p.damage_dealt??0)}</span><span style="font-size:11px;color:#7b7f88">${Math.round(p.damage_taken??0)}</span><span style="font-size:11px;color:#7b7f88">${Math.floor(t/60)}:${String(t%60).padStart(2,'0')}</span></div>`;}).join('');detail.innerHTML=`<div style="display:flex;justify-content:space-between;align-items:center;padding:9px 14px;background:rgba(255,255,255,.03);border-bottom:1px solid rgba(255,255,255,.07)"><span style="font-size:10px;color:#7b7f88;letter-spacing:.5px">MATCH DETAIL</span><span style="cursor:pointer;color:#7b7f88;padding:3px 9px" onclick="document.getElementById('_vmMatchDetail').style.display='none'">x</span></div><div style="display:grid;grid-template-columns:40px 1fr 36px 64px 64px 56px;gap:5px;padding:6px 14px;background:rgba(255,255,255,.02)">${['#','PLAYER','K','DEALT','TAKEN','TIME'].map(h=>`<span style="font-size:9px;color:#7b7f88;letter-spacing:.5px">${h}</span>`).join('')}</div><div style="max-height:240px;overflow-y:auto">${rows}</div>`;}).catch(()=>{detail.innerHTML='<span style="color:#e87a7a;padding:12px">error</span>';});};

// ============================================================
//  CSS
// ============================================================
const uiStyle=document.createElement('style');
uiStyle.textContent=`
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
:root{--ui-shadow:0 40px 120px rgba(0,0,0,.82);--ui-radius:18px;}
#voidUI,#voidUI *{box-sizing:border-box;}
#voidUI{position:fixed;display:none;z-index:999999;font-family:Inter,system-ui,sans-serif;color:#ebecf0;border-radius:var(--ui-radius);overflow:hidden;background:linear-gradient(180deg,rgba(24,25,29,.62),rgba(13,14,17,.80));backdrop-filter:blur(38px) saturate(135%);box-shadow:0 0 0 1px rgba(255,255,255,.05),var(--ui-shadow),inset 0 1px 0 rgba(255,255,255,.04);transform-origin:50% 30%;will-change:left,top,width,height;}
#voidUI.open{display:block;animation:voidPop .22s cubic-bezier(.2,.85,.2,1);}
#voidUI.dragging{user-select:none;cursor:grabbing;}
#voidUI::after{content:"";position:absolute;inset:0;pointer-events:none;opacity:.08;background-image:linear-gradient(rgba(255,255,255,.16) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.16) 1px,transparent 1px);background-size:24px 24px;mask-image:linear-gradient(180deg,rgba(0,0,0,.6),rgba(0,0,0,.15));}
#voidFrame{position:relative;width:100%;height:100%;}
#voidBg{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;opacity:.95;}
#cursorLight{position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle 240px at var(--mx,50%) var(--my,20%),rgba(255,255,255,.085),transparent 62%);mix-blend-mode:screen;opacity:.75;}
#voidNoise{position:absolute;inset:0;pointer-events:none;background-image:radial-gradient(circle at 15% 20%,rgba(255,255,255,.03) 0,transparent 22%),radial-gradient(circle at 85% 12%,rgba(255,255,255,.03) 0,transparent 18%),radial-gradient(circle at 70% 80%,rgba(255,255,255,.025) 0,transparent 20%);mix-blend-mode:screen;opacity:.8;}
#voidSidebar{position:absolute;left:0;top:0;bottom:0;width:188px;background:rgba(8,9,11,.58);border-right:1px solid rgba(255,255,255,.05);backdrop-filter:blur(8px);}
#brand{height:52px;display:flex;align-items:center;padding:0 16px;font-size:12px;font-weight:700;letter-spacing:1.6px;border-bottom:1px solid rgba(255,255,255,.04);background:rgba(255,255,255,.02);}
#brand span{color:#fff;opacity:.88;}
#sidebarTabs{position:relative;padding:12px 10px;display:flex;flex-direction:column;gap:4px;}
#tabIndicator{position:absolute;left:10px;top:12px;width:calc(100% - 20px);height:40px;border-radius:11px;background:linear-gradient(180deg,rgba(255,255,255,.08),rgba(255,255,255,.035));border:1px solid rgba(255,255,255,.055);box-shadow:inset 0 1px 0 rgba(255,255,255,.04);pointer-events:none;transition:transform .22s cubic-bezier(.2,.85,.2,1);}
.tab{display:flex;align-items:center;justify-content:space-between;height:40px;padding:0 12px;border-radius:11px;color:#7b7f88;cursor:pointer;transition:.18s ease;position:relative;z-index:1;letter-spacing:1.7px;font-size:10px;font-weight:700;}
.tab:hover{color:#fff;transform:translateX(2px);}
.tab.active{color:#fff;}
.tab.active::before{content:"";position:absolute;left:-10px;top:8px;bottom:8px;width:2px;border-radius:2px;background:rgba(255,255,255,.92);}
#shell{position:absolute;left:188px;top:0;right:0;bottom:0;display:flex;flex-direction:column;min-width:0;}
#topbar{height:52px;display:flex;align-items:center;gap:12px;padding:0 14px 0 18px;background:rgba(255,255,255,.028);border-bottom:1px solid rgba(255,255,255,.05);cursor:grab;touch-action:none;min-width:0;}
#topbar:active{cursor:grabbing;}
#headline{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}
#headline .title{font-size:13px;font-weight:700;letter-spacing:.8px;}
#headline .sub{font-size:10px;color:#8f929b;letter-spacing:1.6px;}
#topRight{flex:none;display:flex;align-items:center;gap:10px;margin-left:auto;}
#live{display:flex;align-items:center;gap:8px;color:#a5a8b2;font-size:10px;letter-spacing:1.7px;font-weight:700;}
#live .dot{width:7px;height:7px;border-radius:50%;background:#fff;box-shadow:0 0 0 4px rgba(255,255,255,.08),0 0 14px rgba(255,255,255,.35);}
#discordBtn{height:22px;padding:0 10px;border:1px solid rgba(88,101,242,.35);border-radius:999px;background:linear-gradient(180deg,rgba(88,101,242,.92),rgba(66,82,220,.92));color:#fff;font-size:9px;font-weight:800;letter-spacing:1px;cursor:pointer;box-shadow:0 8px 18px rgba(88,101,242,.18),inset 0 1px 0 rgba(255,255,255,.12);transition:transform .18s,filter .18s;}
#discordBtn:hover{transform:translateY(-1px) scale(1.03);filter:brightness(1.08);}
#controls{flex:none;display:flex;align-items:center;gap:8px;}
.ctrl{width:28px;height:28px;border-radius:9px;border:1px solid rgba(255,255,255,.06);background:rgba(255,255,255,.03);color:#eef0f5;cursor:pointer;transition:.18s;display:grid;place-items:center;font-size:12px;}
.ctrl:hover{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.13);transform:translateY(-1px);}
#toolbar{display:flex;align-items:center;gap:10px;padding:9px 14px;border-bottom:1px solid rgba(255,255,255,.045);background:rgba(255,255,255,.012);min-width:0;flex-wrap:wrap;}
#statusChip,#fpsChip,#msChip{padding:6px 10px;border-radius:10px;background:rgba(255,255,255,.025);border:1px solid rgba(255,255,255,.04);color:#7b7f88;font-size:10px;letter-spacing:1.3px;font-weight:700;white-space:nowrap;}
#statusChip{background:rgba(255,255,255,.035);color:#d9dbe2;}
.savecfg-btn{display:flex;align-items:center;justify-content:center;gap:5px;padding:6px 13px;border-radius:10px;background:linear-gradient(180deg,rgba(91,196,112,.18),rgba(60,160,80,.08));border:1px solid rgba(91,196,112,.3);color:#5bc470;font:700 10px Inter,sans-serif;letter-spacing:1px;cursor:pointer;white-space:nowrap;transition:.18s;}
.savecfg-btn:hover{background:linear-gradient(180deg,rgba(91,196,112,.28),rgba(60,160,80,.14));transform:translateY(-1px);}
#pages{position:relative;flex:1;overflow:hidden;min-width:0;}
.page{position:absolute;inset:0;padding:14px 16px;overflow:auto;opacity:0;transform:translateY(8px);pointer-events:none;transition:.18s ease;}
.page.active{opacity:1;transform:translateY(0);pointer-events:auto;}
.pgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;}
.pgrid3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;}
.psec{font-size:10px;letter-spacing:2px;color:#7b7f88;text-transform:uppercase;margin-bottom:8px;padding-bottom:5px;border-bottom:1px solid rgba(255,255,255,.06);}
.pcard{border-radius:12px;background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,.03));border:1px solid rgba(255,255,255,.06);box-shadow:inset 0 1px 0 rgba(255,255,255,.04);padding:12px 14px;transition:.18s;}
.pcard:hover{border-color:rgba(255,255,255,.12);}
.ptgl{display:flex;align-items:center;justify-content:space-between;height:38px;padding:0 12px;border-radius:10px;background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,.03));border:1px solid rgba(255,255,255,.06);cursor:pointer;transition:.18s;}
.ptgl:hover{transform:translateY(-1px);border-color:rgba(255,255,255,.12);}
.ptgl.on{background:linear-gradient(180deg,rgba(255,255,255,.10),rgba(255,255,255,.05));border-color:rgba(255,255,255,.16);}
.ptgl .tlbl{font-size:10px;font-weight:700;letter-spacing:1.5px;color:#7b7f88;}
.ptgl.on .tlbl{color:#fff;}
.ptgl .tpip{width:6px;height:6px;border-radius:50%;background:rgba(255,255,255,.2);transition:.18s;}
.ptgl.on .tpip{background:#fff;box-shadow:0 0 6px rgba(255,255,255,.5);}
.ptgl.red.on{background:linear-gradient(180deg,rgba(255,80,80,.12),rgba(255,60,60,.06));border-color:rgba(255,80,80,.25);}.ptgl.red.on .tlbl{color:#ff6464;}.ptgl.red.on .tpip{background:#ff6464;box-shadow:0 0 6px rgba(255,100,100,.5);}
.ptgl.yellow.on{background:linear-gradient(180deg,rgba(255,200,60,.12),rgba(255,180,40,.06));border-color:rgba(255,200,60,.25);}.ptgl.yellow.on .tlbl{color:#ffc43c;}.ptgl.yellow.on .tpip{background:#ffc43c;box-shadow:0 0 6px rgba(255,200,60,.5);}
.ptgl.blue.on{background:linear-gradient(180deg,rgba(80,160,255,.12),rgba(60,140,255,.06));border-color:rgba(80,160,255,.25);}.ptgl.blue.on .tlbl{color:#58a8ff;}.ptgl.blue.on .tpip{background:#58a8ff;box-shadow:0 0 6px rgba(80,160,255,.5);}
.ptgl.purple.on{background:linear-gradient(180deg,rgba(168,85,247,.12),rgba(147,51,234,.06));border-color:rgba(168,85,247,.35);}.ptgl.purple.on .tlbl{color:#a855f7;}.ptgl.purple.on .tpip{background:#a855f7;box-shadow:0 0 6px rgba(168,85,247,.5);}
.ptgl.green.on{background:linear-gradient(180deg,rgba(91,196,112,.12),rgba(60,160,80,.06));border-color:rgba(91,196,112,.35);}.ptgl.green.on .tlbl{color:#5bc470;}.ptgl.green.on .tpip{background:#5bc470;box-shadow:0 0 6px rgba(91,196,112,.5);}
.zrow{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.06);border-radius:10px;gap:8px;}
.zbtn{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.08);border-radius:6px;font:700 14px Inter,sans-serif;color:#eef0f5;width:28px;height:26px;cursor:pointer;transition:all .1s;}
.zbtn:hover{background:rgba(255,255,255,.12);transform:scale(1.08);}
.zval{font-size:13px;font-weight:700;color:#fff;min-width:44px;text-align:center;}
.mslider{width:100%;appearance:none;height:3px;border-radius:999px;background:rgba(255,255,255,.10);outline:none;margin:8px 0;}
.mslider::-webkit-slider-thumb{appearance:none;width:13px;height:13px;border-radius:50%;background:#fff;border:none;box-shadow:0 0 0 3px rgba(255,255,255,.12);cursor:pointer;}
.srow{display:flex;align-items:center;justify-content:space-between;padding:10px 13px;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.05);border-radius:9px;margin-bottom:5px;}
.srow-left{display:flex;flex-direction:column;gap:3px;}
.srow-title{font-size:11px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:#d0d2db;}
.srow-sub{font-size:10px;color:#7b7f88;}
.kbinput{background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.08);border-radius:6px;padding:5px 12px;font:700 12px Inter,sans-serif;color:#fff;width:60px;text-align:center;cursor:pointer;outline:none;transition:all .15s;}
.kbinput.listening{border-color:rgba(88,101,242,.8);background:rgba(88,101,242,.12);color:#8899ff;}
.step{font-size:11px;color:#9699a3;line-height:1.7;padding:10px 13px;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.05);border-radius:9px;margin-bottom:6px;}
.step b{color:#ebecf0;}
.step.warn{background:rgba(255,180,40,.04);border-color:rgba(255,180,40,.12);color:#9a7c24;}
.slugrow{display:flex;gap:7px;margin-bottom:10px;}
.slugrow input{flex:1;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);border-radius:6px;padding:7px 11px;font:12px Inter,sans-serif;color:#ebecf0;outline:none;}
.slugrow input:focus{border-color:rgba(88,101,242,.6);}
.slugrow button{background:rgba(88,101,242,.85);color:#fff;border:none;border-radius:6px;padding:7px 13px;font:700 10px Inter,sans-serif;cursor:pointer;transition:background .15s;}
.slugrow button:hover{background:rgba(88,101,242,1);}
#_vmMatchDetail{display:none;border-radius:9px;overflow:hidden;border:1px solid rgba(255,255,255,.07);background:rgba(0,0,0,.3);margin-top:10px;}
#toastHost{position:absolute;right:14px;bottom:14px;display:flex;flex-direction:column;gap:8px;pointer-events:none;z-index:10;}
.toast{min-width:160px;max-width:260px;padding:9px 12px;border-radius:10px;background:rgba(14,15,18,.88);border:1px solid rgba(255,255,255,.08);box-shadow:0 20px 50px rgba(0,0,0,.45);color:#eef0f5;font-size:11px;animation:toastIn .18s ease;}
#resizer{position:absolute;right:5px;bottom:5px;width:16px;height:16px;cursor:nwse-resize;border-radius:4px;background:linear-gradient(135deg,transparent 50%,rgba(255,255,255,.26) 50%),linear-gradient(135deg,transparent 68%,rgba(255,255,255,.14) 68%);opacity:.9;}
/* FEEDBACK */
.fb-star{font-size:22px;cursor:pointer;color:#555;transition:color .12s;user-select:none;}
.fb-star.sel{color:#ffc43c;}
.fb-submit-btn{background:linear-gradient(180deg,rgba(91,196,112,.9),rgba(60,160,80,.9));color:#fff;border:none;border-radius:8px;padding:9px 0;font:700 11px Inter,sans-serif;letter-spacing:.8px;cursor:pointer;width:100%;transition:.18s;margin-top:8px;}
.fb-submit-btn:hover{filter:brightness(1.1);transform:translateY(-1px);}
.fb-submit-btn:disabled{opacity:.5;cursor:default;transform:none;filter:none;}
.fb-textarea{width:100%;min-height:90px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:9px 11px;font:12px/1.5 Inter,sans-serif;color:#ebecf0;outline:none;resize:vertical;}
.fb-textarea:focus{border-color:rgba(91,196,112,.5);}
@keyframes voidPop{from{opacity:0;transform:scale(.97) translateY(6px);}to{opacity:1;transform:scale(1) translateY(0);}}
@keyframes toastIn{from{opacity:0;transform:translateY(8px) scale(.98);}to{opacity:1;transform:translateY(0) scale(1);}}
::-webkit-scrollbar{width:8px;}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.10);border-radius:999px;border:2px solid transparent;background-clip:padding-box;}
::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.16);background-clip:padding-box;}
`;
document.head.appendChild(uiStyle);

// ============================================================
//  HTML
// ============================================================
const ui=document.createElement('div');ui.id='voidUI';
ui.innerHTML=`
<div id="voidFrame">
  <canvas id="voidBg"></canvas>
  <div id="cursorLight"></div>
  <div id="voidNoise"></div>
  <aside id="voidSidebar">
    <div id="brand"><span>Void Bacon+</span></div>
    <div id="sidebarTabs">
      <div id="tabIndicator"></div>
      <div class="tab active" data-tab="combat">COMBAT</div>
      <div class="tab" data-tab="visual">VISUAL</div>
      <div class="tab" data-tab="world">WORLD</div>
      <div class="tab" data-tab="keybinds">KEYS</div>
      <div class="tab" data-tab="stats">STATS</div>
      <div class="tab" data-tab="feedback">FEEDBACK ⭐</div>
      <div class="tab" data-tab="updates">UPDATES</div>
    </div>
  </aside>
  <section id="shell">
    <div id="topbar">
      <div id="headline">
        <div class="title">VOID BACON+ v3.4</div>
        <div class="sub">INSERT / ESC · drag · resize</div>
      </div>
      <div id="topRight">
        <div id="live"><span class="dot"></span> LIVE</div>
        <button id="discordBtn" type="button">DISCORD</button>
      </div>
      <div id="controls">
        <button class="ctrl" data-act="max">&#9634;</button>
        <button class="ctrl" data-act="save">&#10226;</button>
        <button class="ctrl" data-act="close">&#215;</button>
      </div>
    </div>
    <div id="toolbar">
      <div id="statusChip">READY</div>
      <div id="fpsChip">-- FPS</div>
      <div id="msChip">-- MS</div>
      <button class="savecfg-btn" onclick="window._vmSaveCfg()">💾 SAVE SETTINGS</button>
    </div>
    <div id="pages">

      <!-- COMBAT -->
      <div class="page active" data-page="combat">
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
          <div>
            <div class="psec">AIM ASSIST</div>
            <div style="display:flex;flex-direction:column;gap:7px;margin-bottom:12px">
              <div class="ptgl red" id="_vmtgl-lockOn" onclick="window._vmFlip('lockOn')"><span class="tlbl">Lock-On [T]</span><div class="tpip"></div></div>
              <div class="ptgl purple" id="_vmtgl-predict" onclick="window._vmFlip('predict')"><span class="tlbl">PREDICT (needs Lock-On)</span><div class="tpip"></div></div>
              <div class="ptgl blue" id="_vmtgl-smoothAim" onclick="window._vmFlip('smoothAim')"><span class="tlbl">Smooth Aim</span><div class="tpip"></div></div>
            </div>
            <div style="padding:10px 12px;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.05);border-radius:9px;margin-bottom:12px">
              <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px"><span style="font-size:10px;color:#7b7f88;letter-spacing:1.3px">SMOOTHNESS</span><span style="font-size:12px;font-weight:700;color:#ebecf0" id="_vmSmoothVal">50</span></div>
              <input class="mslider" id="_vmSmoothSlider" type="range" min="1" max="100" value="50"/>
              <div style="margin-top:6px;display:flex;flex-direction:column;gap:5px">
                <div class="ptgl green" id="_vmtgl-stickyTarget" onclick="window._vmFlip('stickyTarget')"><span class="tlbl">Sticky Target [N]</span><div class="tpip"></div></div>
                <div class="ptgl" id="_vmtgl-wallcheck" onclick="window._vmFlip('wallcheck')"><span class="tlbl">Wallcheck (AABB+Circle)</span><div class="tpip"></div></div>
              </div>
              <div style="margin-top:6px;display:flex;flex-direction:column;gap:5px">
                <div class="ptgl blue" id="_vmtgl-fovEnabled" onclick="window._vmFlip('fovEnabled')"><span class="tlbl">FOV Limit</span><div class="tpip"></div></div>
                <div style="display:flex;align-items:center;gap:8px;padding:6px 0">
                  <span style="font-size:10px;color:#7b7f88;min-width:36px">FOV</span>
                  <input class="mslider" id="_vmFovSlider" type="range" min="50" max="400" value="80" style="flex:1"/>
                  <span style="font-size:12px;font-weight:700;color:#ebecf0;min-width:32px" id="_vmFovVal">80</span>
                  <div class="ptgl" id="_vmtgl-showFov" onclick="window._vmFlip('showFov')" style="height:28px;padding:0 8px;flex:none"><span class="tlbl" style="font-size:9px">SHOW</span><div class="tpip"></div></div>
                </div>
              </div>
            </div>
            <div class="psec">MOVEMENT</div>
            <div class="ptgl yellow" id="_vmtgl-magnet" onclick="window._vmFlip('magnet')"><span class="tlbl">Magnet [E] move+aim+fire</span><div class="tpip"></div></div>
          </div>
          <div>
            <div class="psec">GRENADES</div>
            <div class="pcard">
              <div class="pgrid" style="margin-bottom:10px">
                <div class="ptgl yellow" id="_vmtgl-grenTimer" onclick="window._vmFlip('grenTimer')"><span class="tlbl">Timer</span><div class="tpip"></div></div>
                <div class="ptgl yellow" id="_vmtgl-thrower" onclick="window._vmMacroTgl()"><span class="tlbl">Thrower [Q]</span><div class="tpip"></div></div>
              </div>
              <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px"><span style="font-size:10px;color:#7b7f88;letter-spacing:1.3px">DELAY</span><span style="font-size:12px;font-weight:700;color:#ebecf0" id="_vmMacroDelay">100ms</span></div>
              <input class="mslider" id="_vmMacroSlider" type="range" min="10" max="200" value="100" step="5"/>
              <div style="font-size:10px;color:#7b7f88;margin-top:6px">Frag/Mirv 4s · Smoke 2.5s · Strobe 13.5s</div>
            </div>
          </div>
        </div>
      </div>

      <!-- VISUAL -->
      <div class="page" data-page="visual">
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
          <div>
            <div class="psec">ESP</div>
            <div class="pgrid3" style="margin-bottom:10px">
              <div class="ptgl" id="_vmtgl-Esp" onclick="window._vmFlip('Esp')"><span class="tlbl">Lines</span><div class="tpip"></div></div>
              <div class="ptgl" id="_vmtgl-names" onclick="window._vmFlip('names')"><span class="tlbl">Names</span><div class="tpip"></div></div>
              <div class="ptgl" id="_vmtgl-healthBars" onclick="window._vmFlip('healthBars')"><span class="tlbl">HP</span><div class="tpip"></div></div>
            </div>
            <div class="pgrid" style="margin-bottom:10px">
              <div class="ptgl blue" id="_vmtgl-gunEsp" onclick="window._vmFlip('gunEsp')"><span class="tlbl">Gun Aim</span><div class="tpip"></div></div>
              <div class="ptgl red" id="_vmtgl-barrelEsp" onclick="window._vmFlip('barrelEsp')"><span class="tlbl">Barrels</span><div class="tpip"></div></div>
            </div>
            <div style="font-size:10px;color:#7b7f88;margin-bottom:10px">purple=t3 · gold=t2 · blue=t1 · [W]=wall blocked</div>
            <div class="psec">LOOT</div>
            <div class="ptgl yellow" id="_vmtgl-lootEsp" onclick="window._vmFlip('lootEsp')"><span class="tlbl">Loot ESP</span><div class="tpip"></div></div>
          </div>
          <div>
            <div class="psec">ZOOM</div>
            <div class="zrow" style="margin-bottom:12px">
              <span style="font-size:10px;color:#7b7f88;letter-spacing:1.5px">VIEW</span>
              <div style="display:flex;align-items:center;gap:6px">
                <button class="zbtn" id="_vmZoomIn">+</button>
                <span class="zval" id="_vmZoomVal">1.00x</span>
                <button class="zbtn" id="_vmZoomOut">-</button>
              </div>
              <button class="zbtn" id="_vmZoomReset" style="width:auto;padding:0 9px;font-size:10px">reset</button>
            </div>
            <div class="psec">GLASS</div>
            <div class="pcard">
              <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px"><span style="font-size:10px;color:#7b7f88">OPACITY</span><span style="font-size:12px;font-weight:700;color:#ebecf0" id="_vmTransVal">62%</span></div>
              <input class="mslider" data-vis="trans" type="range" min="18" max="88" value="62"/>
              <div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;margin-bottom:2px"><span style="font-size:10px;color:#7b7f88">BLUR</span><span style="font-size:12px;font-weight:700;color:#ebecf0" id="_vmBlurVal">38</span></div>
              <input class="mslider" data-vis="blur" type="range" min="0" max="60" value="38"/>
              <div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px;margin-bottom:2px"><span style="font-size:10px;color:#7b7f88">RADIUS</span><span style="font-size:12px;font-weight:700;color:#ebecf0" id="_vmRadiusVal">18</span></div>
              <input class="mslider" data-vis="radius" type="range" min="8" max="28" value="18"/>
            </div>
          </div>
        </div>
      </div>

      <!-- WORLD -->
      <div class="page" data-page="world">
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
          <div>
            <div class="psec">WORLD</div>
            <div style="display:flex;flex-direction:column;gap:7px">
              <div class="ptgl blue" id="_vmtgl-xray" onclick="window._vmFlip('xray')"><span class="tlbl">X-Ray</span><div class="tpip"></div></div>
              <div class="ptgl green" id="_vmtgl-layerSpoof" onclick="window._vmFlip('layerSpoof')"><span class="tlbl">Layer Spoofer [HOLD Y]</span><div class="tpip"></div></div>
              <div class="ptgl" id="_vmtgl-mapHighlights" onclick="window._vmFlip('mapHighlights')"><span class="tlbl">Map Highlights</span><div class="tpip"></div></div>
              <div class="ptgl green" id="_vmtgl-autoLoot" onclick="window._vmFlip('autoLoot')"><span class="tlbl">Auto Loot</span><div class="tpip"></div></div>
              <div class="ptgl green" id="_vmtgl-autoDoor" onclick="window._vmFlip('autoDoor')"><span class="tlbl">Auto Door</span><div class="tpip"></div></div>
            </div>
          </div>
          <div>
            <div class="psec">INFO</div>
            <div style="font-size:11px;color:#7b7f88;line-height:1.7;padding:10px 12px;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.05);border-radius:9px">
              <b style="color:#ebecf0">Wallcheck</b> AABB+circle ray-cast.<br><br>
              <b style="color:#ebecf0">Sticky [N]</b> locks nearest enemy.<br><br>
              <b style="color:#ebecf0">Magnet [E]</b> at 6 studs: move+aim+fire.<br><br>
              <b style="color:#ebecf0">Layer Spoofer</b> HOLD [Y].<br><br>
              <b style="color:#ebecf0">Auto Loot/Door</b> heals, ammo, doors.
            </div>
          </div>
        </div>
      </div>

      <!-- KEYS -->
      <div class="page" data-page="keybinds">
        <div style="font-size:10px;color:#7b7f88;margin-bottom:10px">Click a key box then press your new key.</div>
        <div class="srow"><div class="srow-left"><span class="srow-title" style="color:#ff6464">Lock-On</span><span class="srow-sub">Toggle aim assist</span></div><input class="kbinput" id="_vmKb-lockOn" value="T" readonly onclick="window._vmKbListen('lockOn')"/></div>
        <div class="srow"><div class="srow-left"><span class="srow-title" style="color:#ffc43c">Magnet</span><span class="srow-sub">Move+aim+fire at 6 studs</span></div><input class="kbinput" id="_vmKb-magnet" value="E" readonly onclick="window._vmKbListen('magnet')"/></div>
        <div class="srow"><div class="srow-left"><span class="srow-title" style="color:#ffc43c">Thrower</span><span class="srow-sub">Start/stop macro</span></div><input class="kbinput" id="_vmKb-thrower" value="Q" readonly onclick="window._vmKbListen('thrower')"/></div>
        <div class="srow"><div class="srow-left"><span class="srow-title" style="color:#5bc470">Sticky Target</span><span class="srow-sub">Lock nearest</span></div><input class="kbinput" id="_vmKb-stickyTarget" value="N" readonly onclick="window._vmKbListen('stickyTarget')"/></div>
        <div class="srow"><div class="srow-left"><span class="srow-title" style="color:#5bc470">Layer Spoofer</span><span class="srow-sub">HOLD</span></div><input class="kbinput" id="_vmKb-layerSpoof" value="Y" readonly onclick="window._vmKbListen('layerSpoof')"/></div>
        <div class="srow"><div class="srow-left"><span class="srow-title" style="color:#58a8ff">Zoom In</span></div><input class="kbinput" id="_vmKb-zoomIn" value="=" readonly onclick="window._vmKbListen('zoomIn')"/></div>
        <div class="srow"><div class="srow-left"><span class="srow-title" style="color:#58a8ff">Zoom Out</span></div><input class="kbinput" id="_vmKb-zoomOut" value="-" readonly onclick="window._vmKbListen('zoomOut')"/></div>
        <div class="srow"><div class="srow-left"><span class="srow-title" style="color:#58a8ff">Zoom Reset</span></div><input class="kbinput" id="_vmKb-zoomReset" value="0" readonly onclick="window._vmKbListen('zoomReset')"/></div>
      </div>

      <!-- STATS -->
      <div class="page" data-page="stats">
        <div class="pcard" style="margin-bottom:10px">
          <div class="slugrow"><input id="_vmSlugInput" type="text" placeholder="player slug e.g. fr"/><button id="_vmStatsFetch">GO</button></div>
          <div id="_vmStatsBox" style="text-align:center;padding:5px 0;font-size:11px;color:#7b7f88">enter slug above</div>
        </div>
        <div class="psec" style="margin-bottom:8px">RECENT MATCHES <span style="font-size:9px;color:#7b7f88;font-weight:400;letter-spacing:0">click row for detail</span></div>
        <div id="_vmHistoryBox" style="font-size:11px;color:#7b7f88;text-align:center;padding:5px 0">search above</div>
        <div id="_vmMatchDetail"></div>
      </div>

      <!-- UPDATES -->
      <div class="page" data-page="updates">
        <div class="psec">CHANGELOG</div>
        <div style="display:flex;flex-direction:column;gap:8px">
          <div class="pcard">
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
              <span style="font-size:11px;font-weight:700;color:#ebecf0">v3.4</span>
              <span style="font-size:9px;color:#7b7f88;letter-spacing:1px">LATEST</span>
            </div>
            <div style="font-size:11px;color:#7b7f88;line-height:1.8">
              <span style="color:#5bc470">+</span> Auto-detects scrambled data — no more manual fixing after updates. Script scans the game object by shape on every load and patches itself automatically.<br>
              <span style="color:#5bc470">+</span> Settings now persist across sessions (💾 Save Settings button)<br>
              <span style="color:#5bc470">+</span> Feedback + Updates tabs added
            </div>
          </div>
          <div class="pcard">
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
              <span style="font-size:11px;font-weight:700;color:#ebecf0">v3.3</span>
              <span style="font-size:9px;color:#7b7f88;letter-spacing:1px"></span>
            </div>
            <div style="font-size:11px;color:#7b7f88;line-height:1.8">
              <span style="color:#5bc470">+</span> Rewrote game finder — no more setup snippets, auto-finds via Function.prototype.call hook<br>
              <span style="color:#5bc470">+</span> Proper AABB + Circle ray-cast wallcheck<br>
              <span style="color:#5bc470">+</span> Magnet: move + aim + fire all at once when in range<br>
              <span style="color:#5bc470">+</span> Sticky target stays locked even if closer enemy appears<br>
              <span style="color:#e87a7a">-</span> Removed manual setup tab (no longer needed)
            </div>
          </div>
          <div class="pcard">
            <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
              <span style="font-size:11px;font-weight:700;color:#ebecf0">v3.2</span>
              <span style="font-size:9px;color:#7b7f88;letter-spacing:1px"></span>
            </div>
            <div style="font-size:11px;color:#7b7f88;line-height:1.8">
              <span style="color:#5bc470">+</span> Smooth aim with lerp interpolation<br>
              <span style="color:#5bc470">+</span> Velocity-based prediction for lead shots<br>
              <span style="color:#5bc470">+</span> Auto door + auto loot<br>
              <span style="color:#5bc470">+</span> Layer spoofer (hold Y)<br>
              <span style="color:#5bc470">+</span> Grenade cook timer + thrown grenade blast radius
            </div>
          </div>
        </div>
      </div>

      <!-- FEEDBACK -->
      <div class="page" data-page="feedback">
        <div class="psec">SEND FEEDBACK</div>
        <div class="pcard" style="max-width:520px;margin:0 auto">
          <div style="font-size:11px;color:#7b7f88;margin-bottom:12px;line-height:1.6">Rate the script and leave a bug report or suggestion.</div>
          <div style="margin-bottom:12px">
            <div style="font-size:10px;color:#7b7f88;letter-spacing:1px;margin-bottom:6px">RATING</div>
            <div id="_vmFbStars" style="display:flex;gap:6px">${[1,2,3,4,5].map(v=>`<span class="fb-star sel" data-v="${v}">★</span>`).join('')}</div>
          </div>
          <div style="margin-bottom:6px">
            <div style="font-size:10px;color:#7b7f88;letter-spacing:1px;margin-bottom:6px">YOUR FEEDBACK</div>
            <textarea id="_vmFbText" class="fb-textarea" placeholder="What's working? What's broken? Feature requests?"></textarea>
          </div>
          <button class="fb-submit-btn" id="_vmFbSubmit">SEND TO DISCORD ↗</button>
        </div>
      </div>

    </div><!-- /pages -->
    <div id="toastHost"></div>
    <div id="resizer"></div>
  </section>
</div>`;
document.body.appendChild(ui);

// ============================================================
//  CANVAS / PARTICLES
// ============================================================
const canvas=ui.querySelector('#voidBg'),ctx2d=canvas.getContext('2d');
const toastHost=ui.querySelector('#toastHost');
const tabs=[...ui.querySelectorAll('.tab')],pages=[...ui.querySelectorAll('.page')];
const topbar=ui.querySelector('#topbar'),tabIndicator=ui.querySelector('#tabIndicator');
let particles=[];const dpr=Math.max(1,window.devicePixelRatio||1);
function resizeCanvas(){const w=ui.clientWidth,h=ui.clientHeight;canvas.width=Math.max(1,Math.floor(w*dpr));canvas.height=Math.max(1,Math.floor(h*dpr));canvas.style.width=w+'px';canvas.style.height=h+'px';particles=Array.from({length:54},()=>({a:Math.random()*Math.PI*2,r:(40+Math.random()*120)*3.2,s:(0.002+Math.random()*0.003)/5,p:Math.random()*Math.PI*2,sz:(0.6+Math.random()*1.4)*3.0}));}
function drawParticles(ts){const w=canvas.width/dpr,h=canvas.height/dpr;ctx2d.clearRect(0,0,w,h);for(let x=0;x<w;x+=28){ctx2d.strokeStyle='rgba(255,255,255,.028)';ctx2d.beginPath();ctx2d.moveTo(x,0);ctx2d.lineTo(x,h);ctx2d.stroke();}for(let y=0;y<h;y+=28){ctx2d.strokeStyle='rgba(255,255,255,.028)';ctx2d.beginPath();ctx2d.moveTo(0,y);ctx2d.lineTo(w,y);ctx2d.stroke();}const T=ts*0.00005,U=ts*0.00004,cx=w*0.5,cy=h*0.45;for(let i=0;i<particles.length;i++){const p=particles[i];p.a+=p.s*0.33;const x=cx+Math.cos(p.a+p.p)*p.r+Math.sin(T+i)*18;const y=cy+Math.sin(p.a*1.2+p.p)*(p.r*0.62)+Math.cos(U+i)*12;for(let j=i+1;j<particles.length;j++){const q=particles[j];const qx=cx+Math.cos(q.a+q.p)*q.r+Math.sin(T+j)*18;const qy=cy+Math.sin(q.a*1.2+q.p)*(q.r*0.62)+Math.cos(U+j)*12;const dist=Math.hypot(qx-x,qy-y);if(dist<300){ctx2d.strokeStyle=`rgba(255,255,255,${(1-dist/300)*0.048})`;ctx2d.lineWidth=1;ctx2d.beginPath();ctx2d.moveTo(x,y);ctx2d.lineTo(qx,qy);ctx2d.stroke();}}ctx2d.fillStyle='rgba(255,255,255,.62)';ctx2d.beginPath();ctx2d.arc(x,y,p.sz,0,Math.PI*2);ctx2d.fill();}requestAnimationFrame(drawParticles);}

// ============================================================
//  UI LOGIC
// ============================================================
function toast(msg){const el=document.createElement('div');el.className='toast';el.textContent=msg;toastHost.appendChild(el);setTimeout(()=>el.remove(),1800);}
function setStatus(msg){const el=document.getElementById('statusChip');if(el)el.textContent=msg;}
function applyState(persist=true){
  ui.style.left=state.x+'px';ui.style.top=state.y+'px';ui.style.width=state.w+'px';ui.style.height=state.h+'px';
  ui.classList.toggle('open',state.open);ui.style.display=state.open?'block':'none';
  if(state.max){ui.style.left=Math.max(8,Math.round(window.innerWidth*0.04))+'px';ui.style.top=Math.max(8,Math.round(window.innerHeight*0.05))+'px';ui.style.width=Math.min(window.innerWidth*0.92,1350)+'px';ui.style.height=Math.min(window.innerHeight*0.86,920)+'px';}
  if(persist)save();
}
function setTab(name){
  state.tab=name;
  tabs.forEach(t=>t.classList.toggle('active',t.dataset.tab===name));
  pages.forEach(p=>p.classList.toggle('active',p.dataset.page===name));
  const idx=tabs.findIndex(t=>t.dataset.tab===name);
  if(idx>=0)tabIndicator.style.transform=`translateY(${idx*44}px)`;
  save();
}
function setOpen(on){state.open=on;ui.classList.toggle('open',on);ui.style.display=on?'block':'none';if(on)resizeCanvas();save();}
window._vmSetMax=function(on){if(on===state.max)return;if(on){state.restore={x:state.x,y:state.y,w:state.w,h:state.h};state.max=true;}else{state.max=false;if(state.restore)Object.assign(state,state.restore);state.restore=null;}applyState();resizeCanvas();toast(state.max?'Maximized':'Restored');};
window._vmSaveCfg=function(){saveCfg();};

tabs.forEach(t=>t.addEventListener('click',()=>setTab(t.dataset.tab)));
ui.querySelectorAll('.ctrl').forEach(btn=>{btn.addEventListener('click',()=>{const act=btn.dataset.act;if(act==='close')setOpen(false);if(act==='save'){save();toast('Saved');}if(act==='max')window._vmSetMax(!state.max);});});
ui.querySelector('#discordBtn').addEventListener('click',()=>{window.open('https://discord.gg/rdAtZMsyfZ','_blank','noopener,noreferrer');toast('Opening Discord');});

ui.querySelectorAll('.mslider[data-vis]').forEach(r=>{
  const key=r.dataset.vis;const out=document.getElementById(`_vm${key.charAt(0).toUpperCase()+key.slice(1)}Val`);
  const apply=()=>{if(out)out.textContent=r.value+(key==='trans'?'%':'');if(key==='trans'){const a=+r.value/100;ui.style.background=`linear-gradient(180deg,rgba(24,25,29,${a}),rgba(13,14,17,${Math.min(.92,a+.18)}))`;}if(key==='blur')ui.style.backdropFilter=`blur(${r.value}px) saturate(135%)`;if(key==='radius'){ui.style.borderRadius=r.value+'px';ui.style.setProperty('--ui-radius',r.value+'px');}};
  r.addEventListener('input',apply);apply();
});
document.getElementById('_vmSmoothSlider').addEventListener('input',e=>{cfg.smoothAmount=parseInt(e.target.value);document.getElementById('_vmSmoothVal').textContent=e.target.value;});
document.getElementById('_vmFovSlider').addEventListener('input',e=>{cfg.fovSize=parseInt(e.target.value);document.getElementById('_vmFovVal').textContent=e.target.value;});
document.getElementById('_vmMacroSlider').addEventListener('input',e=>macro.setDelay(parseInt(e.target.value)));
document.getElementById('_vmZoomIn').onclick=()=>{if(settings.zoomEnabled)setZoom(Math.max(0.3,+(_zoomMultiplier-0.15).toFixed(2)));};
document.getElementById('_vmZoomOut').onclick=()=>{if(settings.zoomEnabled)setZoom(Math.min(5.0,+(_zoomMultiplier+0.15).toFixed(2)));};
document.getElementById('_vmZoomReset').onclick=()=>{if(settings.zoomEnabled){_zoomHooked=false;_zoomMultiplier=1.0;hookZoom();updateZoomUI();}};

const slugInput=document.getElementById('_vmSlugInput');
document.getElementById('_vmStatsFetch').onclick=()=>fetchStats(slugInput.value.trim());
slugInput.addEventListener('keydown',e=>{if(e.key==='Enter')fetchStats(slugInput.value.trim());});
setTimeout(()=>{const nameEl=document.getElementById('account-player-name');if(nameEl&&!slugInput.value){const slug=nameEl.textContent.trim().toLowerCase().replace(/\s+/g,'-');const skip=['log-in','create-account','guest','sign-in'];if(slug&&!skip.some(s=>slug.includes(s))){slugInput.value=slug;fetchStats(slug);}}},2000);

// ── FEEDBACK WIRING ──────────────────────────────────────────
ui.querySelectorAll('.fb-star').forEach(star=>{
  star.addEventListener('mouseover',()=>{const v=+star.dataset.v;ui.querySelectorAll('.fb-star').forEach(s=>s.classList.toggle('sel',+s.dataset.v<=v));});
  star.addEventListener('mouseout',()=>{ui.querySelectorAll('.fb-star').forEach(s=>s.classList.toggle('sel',+s.dataset.v<=_fbRating));});
  star.addEventListener('click',()=>{_fbRating=+star.dataset.v;ui.querySelectorAll('.fb-star').forEach(s=>s.classList.toggle('sel',+s.dataset.v<=_fbRating));});
});
document.getElementById('_vmFbSubmit').onclick=()=>{const inp=document.getElementById('_vmFbText');submitFeedback(inp.value,_fbRating);};

// ── AUTO-MAPPER runs automatically when game is found ─────────
function _autoMapperRun(){
  const R=runAutoMapper();
  if(R&&!R.error){
    applyMapperResult(R);
    console.log('[VB] Auto-mapper done:',R._missing.length===0?'all keys found':`${R._missing.length} missing: ${R._missing.join(', ')}`);
  }else{
    console.warn('[VB] Auto-mapper failed:',R?.error??'unknown');
  }
}

window._vmFlip=function(key){
  cfg[key]=!cfg[key];const el=document.getElementById(`_vmtgl-${key}`);if(el)el.classList.toggle('on',cfg[key]);
  if(key==='xray'&&!cfg.xray){const game=getGame();if(game){restoreXray(game);restoreXrayObs(game);}}
  if(key==='magnet'&&!cfg.magnet){const inp=getInput(getGame());if(inp)clearMagnetKeys(inp);}
  if(key==='predict'&&!cfg.predict)_predState.clear();
  if(key==='stickyTarget'&&!cfg.stickyTarget)_stickyTarget=null;
  if(key==='smoothAim'&&!cfg.smoothAim){_smoothSx=null;_smoothSy=null;}
  setStatus(cfg[key]?'ACTIVE':'READY');
};
window._vmMacroTgl=function(){if(!settings.throwerEnabled)return;macro.toggle();const el=document.getElementById('_vmtgl-thrower');if(el)el.classList.toggle('on',macro.running);};
window._vmSetFlip=function(key,enabled){
  settings[key+'Enabled']=enabled;
  if(!enabled){
    if(key==='lockOn'&&cfg.lockOn){cfg.lockOn=false;const el=document.getElementById('_vmtgl-lockOn');if(el)el.classList.remove('on');}
    if(key==='magnet'){if(cfg.magnet){cfg.magnet=false;const el=document.getElementById('_vmtgl-magnet');if(el)el.classList.remove('on');}const inp=getInput(getGame());if(inp)clearMagnetKeys(inp);}
    if(key==='thrower'&&macro.running)macro.stop();
    if(key==='zoom'){_zoomMultiplier=1.0;_zoomHooked=false;hookZoom();updateZoomUI();}
  }
};
let _kbListening=null;
window._vmKbListen=function(name){if(_kbListening){const prev=document.getElementById(`_vmKb-${_kbListening}`);if(prev){prev.classList.remove('listening');prev.value=keybinds[_kbListening].toUpperCase();}}_kbListening=name;const el=document.getElementById(`_vmKb-${name}`);if(el){el.classList.add('listening');el.value='...';}};
document.addEventListener('keydown',function(e){
  if(_kbListening){e.preventDefault();e.stopPropagation();const k=e.key===' '?'Space':e.key.length===1?e.key.toUpperCase():e.key;keybinds[_kbListening]=e.key;const el=document.getElementById(`_vmKb-${_kbListening}`);if(el){el.value=k;el.classList.remove('listening');}_kbListening=null;return;}
  if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA')return;
  if(e.key==='Insert'||e.key==='Escape')setOpen(!state.open);
  const k=e.key.toLowerCase();
  if(k===keybinds.lockOn.toLowerCase()&&settings.lockOnEnabled)window._vmFlip('lockOn');
  if(k===keybinds.magnet.toLowerCase()&&settings.magnetEnabled)window._vmFlip('magnet');
  if(k===keybinds.thrower.toLowerCase()&&settings.throwerEnabled){e.preventDefault();macro.toggle();const el=document.getElementById('_vmtgl-thrower');if(el)el.classList.toggle('on',macro.running);}
  if(k===keybinds.stickyTarget.toLowerCase()){if(cfg.stickyTarget&&_nearest){_stickyTarget=_nearest;toast('Target locked');}else{_stickyTarget=null;}}
  if(k===keybinds.layerSpoof.toLowerCase()&&!e.repeat&&!_layerKeyHeld){_layerKeyHeld=true;if(cfg.layerSpoof){const game=getGame();if(game)doLayerSpoof(game);const el=document.getElementById('_vmtgl-layerSpoof');if(el)el.classList.add('on');toast('Layer spoofed');}}
  if(settings.zoomEnabled){if(k===keybinds.zoomIn.toLowerCase()||e.key==='+'){setZoom(Math.max(0.3,+(_zoomMultiplier-0.15).toFixed(2)));e.preventDefault();}if(k===keybinds.zoomOut.toLowerCase()||e.key==='_'){setZoom(Math.min(5.0,+(_zoomMultiplier+0.15).toFixed(2)));e.preventDefault();}if(k===keybinds.zoomReset.toLowerCase()){_zoomHooked=false;_zoomMultiplier=1.0;hookZoom();updateZoomUI();}}
},true);
document.addEventListener('keyup',function(e){
  if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA')return;
  if(e.key.toLowerCase()===keybinds.layerSpoof.toLowerCase()&&_layerKeyHeld){_layerKeyHeld=false;if(cfg.layerSpoof){const game=getGame();if(game)undoLayerSpoof(game);const el=document.getElementById('_vmtgl-layerSpoof');if(el)el.classList.remove('on');toast('Layer restored');}}
});
document.addEventListener('mousedown',function(e){
  if(_kbListening){e.preventDefault();e.stopPropagation();const btnNames={0:'M1',1:'M3',2:'M2',3:'M4',4:'M5'};const btnKeys={0:'__mouse0',1:'__mouse1',2:'__mouse2',3:'__mouse3',4:'__mouse4'};const label=btnNames[e.button]||`M${e.button+1}`,key=btnKeys[e.button]||`__mouse${e.button}`;keybinds[_kbListening]=key;const el=document.getElementById(`_vmKb-${_kbListening}`);if(el){el.value=label;el.classList.remove('listening');}_kbListening=null;return;}
  const btnKey={0:'__mouse0',1:'__mouse1',2:'__mouse2',3:'__mouse3',4:'__mouse4'}[e.button];if(!btnKey)return;
  if(btnKey===keybinds.lockOn&&settings.lockOnEnabled)window._vmFlip('lockOn');
  if(btnKey===keybinds.magnet&&settings.magnetEnabled)window._vmFlip('magnet');
  if(btnKey===keybinds.thrower&&settings.throwerEnabled){macro.toggle();const el=document.getElementById('_vmtgl-thrower');if(el)el.classList.toggle('on',macro.running);}
  if(btnKey===keybinds.stickyTarget){if(cfg.stickyTarget&&_nearest){_stickyTarget=_nearest;toast('Target locked');}else{_stickyTarget=null;}}
},true);

let drag=false,ox=0,oy=0;
topbar.onmousedown=e=>{if(e.target.closest('.ctrl')||state.max)return;drag=true;const r=ui.getBoundingClientRect();ox=e.clientX-r.left;oy=e.clientY-r.top;ui.classList.add('dragging');};
document.addEventListener('mousemove',e=>{if(!drag)return;state.x=e.clientX-ox;state.y=e.clientY-oy;ui.style.left=state.x+'px';ui.style.top=state.y+'px';});
document.addEventListener('mouseup',()=>{drag=false;ui.classList.remove('dragging');if(!state.max)save();});
const resizer=ui.querySelector('#resizer');
resizer.addEventListener('pointerdown',e=>{if(state.max)return;e.preventDefault();const sx=e.clientX,sy=e.clientY,sw=state.w,sh=state.h;const move=ev=>{state.w=clamp(sw+(ev.clientX-sx),580,window.innerWidth-state.x-12);state.h=clamp(sh+(ev.clientY-sy),420,window.innerHeight-state.y-12);applyState(false);resizeCanvas();};const up=()=>{document.removeEventListener('pointermove',move);document.removeEventListener('pointerup',up);save();};document.addEventListener('pointermove',move);document.addEventListener('pointerup',up);});
document.addEventListener('mousemove',e=>{if(!state.open)return;const r=ui.getBoundingClientRect();ui.style.setProperty('--mx',(e.clientX-r.left)+'px');ui.style.setProperty('--my',(e.clientY-r.top)+'px');});

// ============================================================
//  APPLY LOADED CFG TO UI
// ============================================================
function applyCfgToUI(){
  for(const k of SAVEABLE_BOOLS){const el=document.getElementById(`_vmtgl-${k}`);if(el)el.classList.toggle('on',cfg[k]);}
  const ss=document.getElementById('_vmSmoothSlider');if(ss){ss.value=cfg.smoothAmount;document.getElementById('_vmSmoothVal').textContent=cfg.smoothAmount;}
  const fs=document.getElementById('_vmFovSlider');if(fs){fs.value=cfg.fovSize;document.getElementById('_vmFovVal').textContent=cfg.fovSize;}
}

// ============================================================
//  GAME OVERLAY
// ============================================================
function startOverlay(){
  if(window._vmOverlayRunning)return;window._vmOverlayRunning=true;
  const oc=document.createElement('canvas');oc.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998;';document.body.appendChild(oc);
  const octx=oc.getContext('2d');const resize=()=>{oc.width=innerWidth;oc.height=innerHeight;};resize();window.addEventListener('resize',resize);
  hookZoom();installMapHighlights();
  (function loop(){
    requestAnimationFrame(loop);octx.clearRect(0,0,oc.width,oc.height);
    _fpsFrames++;const now=performance.now();const delta=now-_msLast;_msLast=now;_ms=Math.round(_ms*0.85+delta*0.15);
    if(now-_fpsLast>=1000){_fps=_fpsFrames;_fpsFrames=0;_fpsLast=now;const el=document.getElementById('fpsChip');if(el){el.textContent=`${_fps} FPS`;el.style.color=_fps>=55?'#5bc470':_fps>=30?'#e8c060':'#e87a7a';}}
    const msEl=document.getElementById('msChip');if(msEl){msEl.textContent=`${_ms} MS`;msEl.style.color=_ms<=16?'#5bc470':_ms<=33?'#e8c060':'#e87a7a';}
    const killEl=document.querySelector('.js-ui-player-kills');if(killEl)setStatus(killEl.textContent?`${killEl.textContent} KILLS`:'READY');
    const game=getGame();if(!game)return;
    if(!game?.initialized){const inp=getInput(game);if(inp&&cfg.magnet)clearMagnetKeys(inp);return;}
    if(!_zoomHooked)hookZoom();tickXray(game);
    const barn=getBarn(game),myId=getMyId(game),input=getInput(game);if(!barn||!myId)return;
    const worldContainer=getWorldContainer(game);const STUD=worldContainer?.scale?.x||30;if(!_baseStud)_baseStud=STUD;
    const players=getPlayers(barn);const myInfo=barn.playerInfo?.[myId];const myGroup=myInfo?.groupId,myTeam=myInfo?.teamId;
    const me=getMe(game);if(!me?.active)return;
    tickAutoDoor(game,input);tickAutoLoot(game,input);
    let mpos;try{mpos=me.container.toGlobal({x:0,y:0});}catch(e){return;}
    const msx=mpos.x,msy=mpos.y,myWorldPos=getPlayerPos(me);
    if(cfg.gunEsp){const aw=(me.weapTypeOld??'').toLowerCase();const isMelee=!aw||aw==='fists'||/knife|axe|katana|pan|bat|crowbar|bowie|kukri|naginata|spear|saw|hook|sword|sickle|hatchet|machete|cleaver|hammer/.test(aw);const dir=getPlayerDir(me);if(!isMelee&&dir&&typeof dir.x==='number'){const gd=GUN_DATA[aw]??{range:150,spread:3};const rangePx=gd.range*STUD,spreadRad=(gd.spread*Math.PI)/180,ca=Math.atan2(-dir.y,dir.x);octx.save();octx.beginPath();octx.moveTo(msx,msy);octx.arc(msx,msy,rangePx,ca-spreadRad,ca+spreadRad);octx.closePath();octx.fillStyle='rgba(255,255,255,0.06)';octx.fill();octx.strokeStyle='rgba(255,255,255,0.55)';octx.lineWidth=1;octx.setLineDash([5,5]);octx.beginPath();octx.moveTo(msx,msy);octx.lineTo(msx+Math.cos(ca-spreadRad)*rangePx,msy+Math.sin(ca-spreadRad)*rangePx);octx.stroke();octx.beginPath();octx.moveTo(msx,msy);octx.lineTo(msx+Math.cos(ca+spreadRad)*rangePx,msy+Math.sin(ca+spreadRad)*rangePx);octx.stroke();octx.setLineDash([]);octx.beginPath();octx.arc(msx,msy,rangePx,ca-spreadRad,ca+spreadRad);octx.strokeStyle='rgba(255,255,255,0.9)';octx.lineWidth=1.5;octx.stroke();octx.restore();}}
    if(cfg.fovEnabled&&cfg.showFov&&cfg.lockOn){const fovPx=cfg.fovSize*STUD;octx.save();octx.beginPath();octx.arc(msx,msy,fovPx,0,Math.PI*2);octx.strokeStyle='rgba(168,85,247,0.35)';octx.lineWidth=1;octx.setLineDash([4,4]);octx.stroke();octx.setLineDash([]);octx.restore();}
    for(const[id] of _predState){if(!players.find(p=>p.__id===id&&p.active&&!isPlayerDead(p)))_predState.delete(id);}
    let activeTarget=null,activeTargetDist=Infinity;
    if(cfg.stickyTarget&&_stickyTarget){const stP=players.find(p=>p.__id===_stickyTarget.p?.__id&&p.active&&!isPlayerDead(p));if(stP&&stP.container?.visible){try{const sp=stP.container.toGlobal({x:0,y:0});activeTarget={p:stP,sx:sp.x,sy:sp.y};activeTargetDist=Math.hypot(sp.x-msx,sp.y-msy);}catch(e){_stickyTarget=null;}}else{_stickyTarget=null;}}
    if(!activeTarget){const fovPx=cfg.fovEnabled?cfg.fovSize*STUD:Infinity;for(const p of players){if(!p.active||p.__id===myId||isPlayerDead(p)||!p.container?.visible)continue;const info=barn.playerInfo?.[p.__id];const isMate=(myGroup>0&&info?.groupId>0&&info.groupId===myGroup)||(myTeam>0&&info?.teamId>0&&info.teamId===myTeam);if(isMate)continue;let spos;try{spos=p.container.toGlobal({x:0,y:0});}catch(e){continue;}const d=Math.hypot(spos.x-msx,spos.y-msy);if(cfg.fovEnabled&&d>fovPx)continue;if(d<activeTargetDist){activeTargetDist=d;activeTarget={p,sx:spos.x,sy:spos.y};}}}
    _nearest=activeTarget;
    let targetHasLOS=true;if(activeTarget&&cfg.wallcheck&&myWorldPos)targetHasLOS=hasLOS(game,myWorldPos,getPlayerPos(activeTarget.p));
    if(cfg.lockOn&&settings.lockOnEnabled&&activeTarget&&input&&(targetHasLOS||!cfg.wallcheck)){let tsx=activeTarget.sx,tsy=activeTarget.sy;if(cfg.predict&&myWorldPos&&worldContainer){const aw=(me.weapTypeOld??'').toLowerCase();const gd=GUN_DATA[aw]??{bulletSpeed:100};const pw=getPredictedWorldPos(activeTarget.p,myWorldPos,gd.bulletSpeed);if(pw){const psx=worldContainer.x+pw.x*STUD,psy=worldContainer.y-pw.y*STUD;const pulse=(Date.now()%700)/700,ripple=Math.sin(pulse*Math.PI*2);octx.save();octx.beginPath();octx.arc(psx,psy,14+ripple*4,0,Math.PI*2);octx.strokeStyle='rgba(168,85,247,0.55)';octx.lineWidth=1.5;octx.globalAlpha=0.6*(1-pulse);octx.stroke();octx.globalAlpha=0.92;octx.beginPath();octx.arc(psx,psy,5,0,Math.PI*2);octx.fillStyle='#a855f7';octx.shadowBlur=12;octx.shadowColor='#a855f7';octx.fill();octx.shadowBlur=0;octx.restore();tsx=psx;tsy=psy;}}applyAimLock(tsx,tsy,input);}
    if(cfg.magnet&&settings.magnetEnabled&&input&&activeTarget){const distStuds=activeTargetDist/STUD;const magnetActive=applyMagnet(input,me,msx,msy,activeTarget.sx,activeTarget.sy,distStuds);if(magnetActive){const pulse=(Date.now()%600)/600;octx.save();octx.globalAlpha=0.6+Math.sin(pulse*Math.PI*2)*0.4;octx.beginPath();octx.arc(activeTarget.sx,activeTarget.sy,10+Math.sin(pulse*Math.PI*2)*4,0,Math.PI*2);octx.strokeStyle='#ffff00';octx.lineWidth=2;octx.stroke();octx.restore();}}else if((!cfg.magnet||!settings.magnetEnabled)&&input){clearMagnetKeys(input);}
    if(cfg.barrelEsp&&worldContainer){const nowMs=Date.now();for(const obj of getObstacles(game)){if(!obj||!obj.active||!obj.pos||obj.dead||obj.exploded||obj.healthT<=0)continue;const t=(typeof obj.type==='string'?obj.type:'').toLowerCase();if(!/^barrel_0[12]|^fuel_barrel/.test(t))continue;const bx=worldContainer.x+obj.pos.x*STUD,by=worldContainer.y-obj.pos.y*STUD;if(bx<-300||bx>innerWidth+300||by<-300||by>innerHeight+300)continue;const hitRad=(obj.collider?.rad??(obj.scale??1)*1.05)*STUD;const blastRad=(/fuel/.test(t)?14:12)*STUD;const pulse=(nowMs%1400)/1400,sin=Math.sin(pulse*Math.PI*2);octx.save();octx.beginPath();octx.arc(bx,by,blastRad,0,Math.PI*2);octx.fillStyle='#ff4400';octx.globalAlpha=0.05+sin*0.03;octx.fill();octx.beginPath();octx.arc(bx,by,blastRad,0,Math.PI*2);octx.strokeStyle='#ff4400';octx.lineWidth=2;octx.globalAlpha=0.5+sin*0.2;octx.stroke();octx.beginPath();octx.arc(bx,by,hitRad+2,0,Math.PI*2);octx.strokeStyle='#ff2200';octx.lineWidth=2.5;octx.globalAlpha=1;octx.stroke();octx.restore();}}
    if(cfg.grenTimer){const nowMs=Date.now();const weapItem=(me.weapTypeOld??'').toLowerCase();const throwState=me.throwableState??'equip';const isHoldingGren=/frag|mirv|smoke|strobe|martyr/.test(weapItem);const isCooking=throwState==='cook',isThrowing=throwState==='throw'||me.anim?.type===3,isCookable=/frag|mirv|martyr/.test(weapItem);if(isCookable&&isCooking&&!_grenCook)_grenCook={cookStart:nowMs,fuse:getGrenFuse(weapItem),typeId:weapItem,remaining:null,thrownAt:null};if(_grenCook&&_grenCook.remaining===null&&(isThrowing||!isHoldingGren)){const cooked=(nowMs-_grenCook.cookStart)/1000;_grenCook.remaining=Math.max(0.15,_grenCook.fuse-cooked);_grenCook.thrownAt=nowMs;}if(_grenCook?.thrownAt&&(nowMs-_grenCook.thrownAt)>800)_grenCook=null;if(!isHoldingGren&&_grenCook&&!_grenCook.thrownAt)_grenCook=null;const projArr=getProjectiles(game);let newThisFrame=0;const newProjs=[];if(Array.isArray(projArr)){for(const proj of projArr){if(!proj?.active)continue;const pt=(typeof proj.type==='string'?proj.type:'').toLowerCase();if(/^potato|^tomato/.test(pt))continue;if(!_thrownGrenades.has(proj)){newThisFrame++;newProjs.push(proj);}}}for(const proj of newProjs){let fuse,typeId;if(newThisFrame>=3){fuse=1.8;typeId='mirv_child';}else if(_grenCook?.remaining!=null){fuse=_grenCook.remaining;typeId=_grenCook.typeId;_grenCook=null;}else{fuse=getGrenFuse(isHoldingGren?weapItem:'');typeId=isHoldingGren?weapItem:'frag';}_thrownGrenades.set(proj,{spawnTime:nowMs,fuse,typeId});}for(const[proj,data]of _thrownGrenades){const age=(nowMs-data.spawnTime)/1000;if(!proj.active||age>data.fuse+0.6)_thrownGrenades.delete(proj);}if(isCookable&&isCooking&&_grenCook&&!_grenCook.thrownAt){const elapsed=(nowMs-_grenCook.cookStart)/1000,remaining=Math.max(0,_grenCook.fuse-elapsed),progress=Math.min(1,elapsed/_grenCook.fuse);const urgent=progress>0.75,col=urgent?'#ff5050':'#ffdc3c';octx.save();const arcR=24;octx.lineWidth=4;octx.beginPath();octx.arc(msx,msy,arcR,-Math.PI/2,-Math.PI/2+Math.PI*2);octx.strokeStyle='rgba(80,80,80,0.4)';octx.stroke();octx.beginPath();octx.arc(msx,msy,arcR,-Math.PI/2,-Math.PI/2+Math.PI*2*progress);octx.strokeStyle=col;octx.stroke();const lx2=msx,ly2=msy+arcR+12;octx.fillStyle='rgba(0,0,0,0.75)';octx.beginPath();octx.roundRect(lx2-44,ly2-10,88,20,5);octx.fill();octx.font='bold 11px monospace';octx.textAlign='center';octx.textBaseline='middle';octx.strokeStyle='rgba(0,0,0,0.9)';octx.lineWidth=3;const cookLabel=`💣 ${remaining.toFixed(1)}s`;octx.strokeText(cookLabel,lx2,ly2);octx.fillStyle=col;octx.fillText(cookLabel,lx2,ly2);if(urgent){const pulse=(nowMs%250)/250;octx.globalAlpha=0.5*(1-pulse);octx.beginPath();octx.arc(msx,msy,arcR+pulse*14,0,Math.PI*2);octx.strokeStyle='#ff3333';octx.lineWidth=2;octx.stroke();}octx.restore();}for(const[proj,data]of _thrownGrenades){if(!proj.active||!proj.pos||!worldContainer)continue;const px=worldContainer.x+proj.pos.x*STUD,py=worldContainer.y-proj.pos.y*STUD;const age=(nowMs-data.spawnTime)/1000,remaining=Math.max(0,data.fuse-age),progress=Math.min(1,age/data.fuse),urgent=remaining<1.0;const blastPx=getGrenBlast(data.typeId)*STUD;octx.save();octx.beginPath();octx.arc(px,py,blastPx,0,Math.PI*2);octx.fillStyle='#ff3333';octx.globalAlpha=0.04+progress*0.10;octx.fill();octx.globalAlpha=0.5+progress*0.4;octx.strokeStyle='#ff3333';octx.lineWidth=1.8;octx.beginPath();octx.arc(px,py,blastPx,0,Math.PI*2);octx.stroke();octx.globalAlpha=1;const arcR=12;octx.lineWidth=3;octx.strokeStyle='rgba(0,0,0,0.7)';octx.beginPath();octx.arc(px,py,arcR,-Math.PI/2,-Math.PI/2+Math.PI*2);octx.stroke();octx.strokeStyle='#ff3333';octx.beginPath();octx.arc(px,py,arcR,-Math.PI/2,-Math.PI/2+Math.PI*2*progress);octx.stroke();octx.font='bold 12px monospace';octx.textAlign='center';octx.textBaseline='middle';octx.strokeStyle='rgba(0,0,0,1)';octx.lineWidth=4;octx.strokeText(remaining.toFixed(1),px,py);octx.fillStyle='#ff3333';octx.fillText(remaining.toFixed(1),px,py);if(urgent){const pulse=(nowMs%200)/200;octx.globalAlpha=0.75*(1-pulse);octx.beginPath();octx.arc(px,py,blastPx*(0.6+pulse*0.4),0,Math.PI*2);octx.strokeStyle='#ff0000';octx.lineWidth=3;octx.stroke();}octx.restore();}}
    for(const p of players){if(!p.active||isPlayerDead(p)||!p.container?.visible)continue;let spos;try{spos=p.container.toGlobal({x:0,y:0});}catch(e){continue;}const sx=spos.x,sy=spos.y,isSelf=p.__id===myId;const info=barn.playerInfo?.[p.__id];const isMate=!isSelf&&((myGroup>0&&info?.groupId>0&&info.groupId===myGroup)||(myTeam>0&&info?.teamId>0&&info.teamId===myTeam));const isLocked=activeTarget&&p.__id===activeTarget.p.__id;const isSticky=_stickyTarget&&p.__id===_stickyTarget.p?.__id;const pHasLOS=isSelf||isMate||!cfg.wallcheck||!myWorldPos?true:hasLOS(game,myWorldPos,getPlayerPos(p));const color=isSelf?'#5bc470':isMate?'#6aabff':'#ff4444';if(cfg.healthBars&&!isSelf){const status=barn.playerStatus?.[p.__id];let hp=typeof status?.health==='number'?status.health:null;if(hp===null&&barn.playerStatus){for(const s of Object.values(barn.playerStatus)){if(s&&s.playerId===p.__id&&typeof s.health==='number'){hp=s.health;break;}}}if(hp!==null&&isMate){const BW=120,BH=12,BX=sx-BW/2,BY=sy-42;const filled=Math.max(0,Math.min(1,hp/100))*BW,hpCol=hp>60?'#5bc470':hp>30?'#e8c060':'#e87a7a';octx.globalAlpha=0.75;octx.fillStyle='rgba(0,0,0,0.55)';octx.beginPath();octx.roundRect(BX-1,BY-1,BW+2,BH+2,4);octx.fill();octx.globalAlpha=0.4;octx.fillStyle='#333';octx.beginPath();octx.roundRect(BX,BY,BW,BH,3);octx.fill();octx.globalAlpha=0.95;octx.fillStyle=hpCol;if(filled>0){octx.beginPath();octx.roundRect(BX,BY,filled,BH,3);octx.fill();}octx.globalAlpha=1;octx.font='bold 11px monospace';octx.textAlign='center';octx.textBaseline='middle';octx.strokeStyle='rgba(0,0,0,0.9)';octx.lineWidth=2.5;octx.strokeText(`${Math.round(hp)}`,BX+BW/2,BY+BH/2);octx.fillStyle='#fff';octx.fillText(`${Math.round(hp)}`,BX+BW/2,BY+BH/2);}else if(isPlayerDowned(p)){octx.save();octx.globalAlpha=0.8;octx.font='bold 10px monospace';octx.textAlign='center';octx.textBaseline='bottom';octx.strokeStyle='rgba(0,0,0,0.8)';octx.lineWidth=3;octx.strokeText('DOWNED',sx,sy-24);octx.fillStyle='#e8c060';octx.fillText('DOWNED',sx,sy-24);octx.restore();}}if(!isSelf){const dist=Math.hypot(sx-msx,sy-msy),alpha=Math.max(0.75,1-dist/2400),studs=Math.round(dist/STUD);if(cfg.Esp){octx.save();octx.beginPath();octx.moveTo(msx,msy);octx.lineTo(sx,sy);octx.setLineDash(isMate?[6,4]:isLocked?[3,3]:[]);octx.strokeStyle=isSticky?'#a855f7':isLocked?'#ff4444':color;octx.lineWidth=isMate?2:2.5;octx.globalAlpha=pHasLOS?alpha:alpha*0.3;octx.stroke();octx.setLineDash([]);if(isSticky){octx.beginPath();octx.arc(sx,sy,14,0,Math.PI*2);octx.strokeStyle='#a855f7';octx.lineWidth=2;octx.globalAlpha=0.7;octx.stroke();}octx.restore();}if(cfg.names){const name=info?.nameTruncated||info?.name||`#${p.__id}`;const tag=isSticky?'[S]':isLocked?'[X]':'';const label=`${name}  ${studs}${tag?' '+tag:''}${!pHasLOS&&!isMate?' [W]':''}`;const lx=(msx+sx)/2,ly=(msy+sy)/2;octx.save();octx.globalAlpha=Math.min(1,alpha+0.2);octx.font='bold 11px monospace';octx.textAlign='center';octx.textBaseline='middle';octx.strokeStyle='rgba(0,0,0,0.8)';octx.lineWidth=3;octx.strokeText(label,lx,ly);octx.fillStyle=isSticky?'#a855f7':isLocked?'#ff4444':color;octx.fillText(label,lx,ly);octx.restore();}}}
    if(cfg.lootEsp&&worldContainer){for(const item of getLoot(game)){if(!item?.active||!item.pos)continue;const t=(typeof item.type==='string'?item.type:'').toLowerCase();const lx=worldContainer.x+item.pos.x*STUD,ly=worldContainer.y-item.pos.y*STUD;if(lx<-50||lx>innerWidth+50||ly<-50||ly>innerHeight+50)continue;const col=lootColor(item),rawR=item.rad!=null?item.rad*STUD:0;const isGun=/gun|rifle|shotgun|pistol|smg|sniper|melee|m9|glock|deagle|mp5|ump|vector|ak|scar|m416|bar|mosin|sv|l86|m249|qbb|pkp|dp|spas|saiga|usas/.test(t);const r=isGun?Math.max(22,rawR*1.25):Math.max(14,rawR);octx.save();octx.beginPath();octx.arc(lx,ly,r,0,Math.PI*2);octx.fillStyle=col;octx.globalAlpha=0.18;octx.fill();octx.beginPath();octx.arc(lx,ly,r,0,Math.PI*2);octx.strokeStyle=col;octx.lineWidth=2.5;octx.globalAlpha=0.95;octx.stroke();octx.restore();}}
  })();
}

// ============================================================
//  WAIT FOR GAME + INIT
// ============================================================
function waitForGame(){
  let tries=0;
  const poll=setInterval(()=>{tries++;const game=getGame();if(game?.initialized){clearInterval(poll);_autoMapperRun();startOverlay();installMapHighlights();return;}if(game)setStatus('LOBBY');if(tries>240){clearInterval(poll);setStatus('NEEDS SETUP – refresh');}},500);
}
function init(){
  loadCfg();
  loadSavedKeymap();
  applyState(false);resizeCanvas();setTab(state.tab);setOpen(state.open);applyCfgToUI();
  requestAnimationFrame(drawParticles);
  window.addEventListener('resize',()=>{if(state.open){applyState(false);resizeCanvas();}});
  const obs=new MutationObserver((_,o)=>{if(document.getElementById('ui-game')){o.disconnect();waitForGame();}});
  if(document.getElementById('ui-game'))waitForGame();else obs.observe(document.body,{childList:true,subtree:true});
}
if(document.readyState==='loading')window.addEventListener('DOMContentLoaded',init);else init();
console.log('%c[Void Bacon+ v3.3.3] ESC=panel | T=lock-on | E=magnet | Q=macro | N=sticky | HOLD-Y=layer | DEBUG tab = auto-mapper','color:#a855f7;font-weight:bold');
})();