Void Bacon+ v3.5

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!)

Advertisement:

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

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

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

Advertisement:

// ==UserScript==
// @name         Void Bacon+ v3.5
// @namespace    VoidBacon
// @version      3.5
// @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';
const EDIT_STORE='voidbacon.edit';

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'];

// ── EDIT PERSISTENCE ──────────────────────────────────────────
let _bgSaved={type:'image',url:'',fit:'cover',dim:0};
let _vbRules={};
let _draggedEls={};      // stableKey -> {left,top}
let _deletedKeys=[];     // array of stableKeys
const VB_STYLE_ID='vb-el-overrides';
const VB_KEY_ATTR='data-vb-key';
let _vbKeyCtr=0;

// Generate a stable, reload-safe key for an element
function _vbKey(el){
  if(el.getAttribute(VB_KEY_ATTR))return el.getAttribute(VB_KEY_ATTR);
  let key;
  if(el.id){
    key='#'+el.id;
  } else {
    const tag=el.tagName.toLowerCase();
    const cls=el.className&&typeof el.className==='string'?el.className.trim().split(/\s+/).filter(Boolean).map(c=>'.'+c).join(''):'';
    const sel=tag+cls;
    let idx=0;
    try{const all=[...document.querySelectorAll(sel)];idx=Math.max(0,all.indexOf(el));}catch{}
    key=sel+'::'+idx;
  }
  el.setAttribute(VB_KEY_ATTR,key);
  return key;
}

// Find element on page from a saved key
function _elByKey(key){
  if(!key)return null;
  if(key.startsWith('#')){
    const id=key.split('::')[0].slice(1);
    return document.getElementById(id)||null;
  }
  const parts=key.split('::');
  const sel=parts[0];
  const idx=parseInt(parts[1]||'0',10);
  try{return document.querySelectorAll(sel)[idx]||null;}catch{return null;}
}

// CSS injection — uses background-color only so background-image (potato logo) survives
function _darkenHex(hex,amt){
  const v=String(hex||'').replace('#','');
  if(!/^[0-9a-f]{6}$/i.test(v))return'#000000';
  return'#'+[0,2,4].map(i=>Math.max(0,Math.round(parseInt(v.slice(i,i+2),16)*(1-amt))).toString(16).padStart(2,'0')).join('');
}

function _vbFlush(){
  let s=document.getElementById(VB_STYLE_ID);
  if(!s){s=document.createElement('style');s.id=VB_STYLE_ID;document.head.appendChild(s);}
  const lines=[];
  for(const[key,r]of Object.entries(_vbRules)){
    const attr=VB_KEY_ATTR;
    const sel=`[${attr}="${key.replace(/\\/g,'\\\\').replace(/"/g,'\\"')}"]`;
    if(r.bgMode==='solid'){
      const dark=_darkenHex(r.bgColor,0.35);
      lines.push(`${sel}{background-color:${r.bgColor}!important;box-shadow:inset 0 -3px ${dark}!important;border-bottom-color:${dark}!important;}`);
      lines.push(`${sel}.btn-mode-potato{box-shadow:inset 0 -3px ${dark}!important;border-bottom:4px solid ${dark}!important;}`);
    } else if(r.bgMode==='gradient'){
      lines.push(`${sel}{background:linear-gradient(${r.gradAngle}deg,${r.gradA},${r.gradB})!important;}`);
    } else if(r.bgMode==='none'){
      lines.push(`${sel}{background-color:transparent!important;background:none!important;}`);
    }
    if(r.textColor){
      lines.push(`${sel}{color:${r.textColor}!important;text-shadow:none!important;}`);
      lines.push(`${sel}>*{color:${r.textColor}!important;}`);
    }
    if(r.fontSize>0){
      lines.push(`${sel} span,${sel} a{font-size:${r.fontSize}px!important;}`);
    }
    if(r.borderW>0){
      lines.push(`${sel}{border:${r.borderW}px solid ${r.borderColor}!important;}`);
    } else if(r.borderColor&&r.borderColor!=='#36a7ff'){
      lines.push(`${sel}{border-color:${r.borderColor}!important;outline:2px solid ${r.borderColor}!important;}`);
    }
    lines.push(`${sel}{border-radius:${r.borderR}px!important;}`);
    if(r.scale&&r.scale!==1){
      const base=r.baseTransform?r.baseTransform+' ':'';
      lines.push(`${sel}{transform:${base}scale(${r.scale})!important;transform-origin:center center!important;}`);
    }
    if(r.opacity!=null&&r.opacity!==1)lines.push(`${sel}{opacity:${r.opacity}!important;}`);
  }
  s.textContent=lines.join('\n');
}

// ── SAVE / LOAD ───────────────────────────────────────────────
function saveCfg(){
  const out={};
  for(const k of SAVEABLE_BOOLS)out[k]=cfg[k];
  out.smoothAmount=cfg.smoothAmount;
  out.fovSize=cfg.fovSize;
  out._bg=_bgSaved;
  localStorage.setItem(CFG_STORE,JSON.stringify(out));
  const editOut={rules:_vbRules,dragged:_draggedEls,deleted:_deletedKeys};
  localStorage.setItem(EDIT_STORE,JSON.stringify(editOut));
  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;
    if(saved._bg)_bgSaved=Object.assign(_bgSaved,saved._bg);
  }catch(e){}
  try{
    const es=JSON.parse(localStorage.getItem(EDIT_STORE)||'{}');
    if(es.rules&&typeof es.rules==='object')_vbRules=es.rules;
    if(es.dragged&&typeof es.dragged==='object')_draggedEls=es.dragged;
    if(Array.isArray(es.deleted))_deletedKeys=es.deleted;
  }catch(e){}
}

// Restore edit state — called after DOM ready and on rescan
function restoreEditState(){
  // Re-stamp keys and flush CSS rules immediately (works any time)
  for(const key of Object.keys(_vbRules)){
    const el=_elByKey(key);
    if(el&&!el.getAttribute(VB_KEY_ATTR))el.setAttribute(VB_KEY_ATTR,key);
  }
  if(Object.keys(_vbRules).length)_vbFlush();

  // Re-apply drag positions immediately
  for(const[key,pos]of Object.entries(_draggedEls)){
    const el=_elByKey(key);
    if(!el)continue;
    el.style.setProperty('position','fixed','important');
    el.style.setProperty('left',pos.left+'px','important');
    el.style.setProperty('top',pos.top+'px','important');
    el.style.setProperty('margin','0','important');
  }

  // Re-delete deleted elements
  for(const key of _deletedKeys){
    const el=_elByKey(key);
    if(el)el.remove();
  }

  // Background needs #background div which only exists after game/menu loads
  // Poll for it so we don't silently fail
  if(_bgSaved.url)_bgApplyWhenReady();
}

// Poll until #background exists then apply — retries every 300ms for up to 15s
function _bgApplyWhenReady(){
  if(!_bgSaved.url)return;
  const target=document.getElementById('background');
  if(target){_bgApplyToPage(_bgSaved.type,_bgSaved.url,_bgSaved.fit,_bgSaved.dim);return;}
  let tries=0;
  const poll=setInterval(()=>{
    tries++;
    const t=document.getElementById('background');
    if(t){clearInterval(poll);_bgApplyToPage(_bgSaved.type,_bgSaved.url,_bgSaved.fit,_bgSaved.dim);return;}
    if(tries>50)clearInterval(poll); // give up after 15s
  },300);
}

// ── GAME DATA / MAPPINGS ──────────────────────────────────────
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 ─────────────────────────────────────────────────────
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 _kbListening=null;
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'};


// ── AUTO-MAPPER ───────────────────────────────────────────────
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'};
  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}`);return null;};
  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');
  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');
  const barnObj=barnKey?_safeGet(game,barnKey):null;
  const playerPoolKey=_poolArr(barnObj?.playerPool)?.key;
  playerPoolKey?found('playerPoolKey',playerPoolKey):nope('playerPoolKey');
  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;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){}}
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;}

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;}}

// ── SA2 AIM ENGINE (SimpleAim v2) ─────────────────────────────
// Mouse proxy: game always reads aimX/aimY (lerped), real mouse tracked separately
const MIN_MOVE=0.05;
const velMap=new Map();
let _saAimX=0,_saAimY=0;   // what the game sees
let _saRealX=0,_saRealY=0; // actual hardware mouse
let _saLerpX=0,_saLerpY=0; // current interpolated position
let _saHasTarget=false;
let _saInputHooked=false;

// Track real mouse movement always
document.addEventListener('mousemove',e=>{
  _saRealX=e.clientX;_saRealY=e.clientY;
  if(!_saHasTarget){_saLerpX=_saRealX;_saLerpY=_saRealY;}
},true);

// Hook input.mousePos with a proxy so game reads our lerped value
function _saHookMouse(inp){
  if(_saInputHooked||!inp)return;
  _saLerpX=_saAimX=inp.mousePos?.x??0;
  _saLerpY=_saAimY=inp.mousePos?.y??0;
  _saRealX=_saLerpX;_saRealY=_saLerpY;
  const proxy={
    get x(){return _saAimX;},set x(v){_saRealX=v;},
    get y(){return _saAimY;},set y(v){_saRealY=v;},
  };
  try{Object.defineProperty(inp,'mousePos',{configurable:true,get:()=>proxy});_saInputHooked=true;}
  catch(e){console.warn('[VB] aim hook failed',e);}
}

function _saLerp(a,b,t){return a+(b-a)*t;}

// SA2 velocity tracker — EMA with decay-to-zero when stationary
function _getVelocity(id,pos,now){
  if(!velMap.has(id)){velMap.set(id,{vx:0,vy:0,lx:pos.x,ly:pos.y,lt:now});return{vx:0,vy:0};}
  const s=velMap.get(id);const dt=(now-s.lt)/1000;const moved=Math.hypot(pos.x-s.lx,pos.y-s.ly);
  if(dt>0&&dt<0.25){if(moved>MIN_MOVE){let rx=(pos.x-s.lx)/dt,ry=(pos.y-s.ly)/dt;const spd=Math.hypot(rx,ry);if(spd>22){const sc=22/spd;rx*=sc;ry*=sc;}s.vx=0.80*rx+0.20*s.vx;s.vy=0.80*ry+0.20*s.vy;}else{s.vx*=0.85;s.vy*=0.85;if(Math.abs(s.vx)<0.01)s.vx=0;if(Math.abs(s.vy)<0.01)s.vy=0;}}
  s.lx=pos.x;s.ly=pos.y;s.lt=now;return{vx:s.vx,vy:s.vy};
}

// Quadratic intercept solver
function _solveIntercept(dx,dy,vx,vy,spd){
  const a=vx*vx+vy*vy-spd*spd,b=2*(dx*vx+dy*vy),c=dx*dx+dy*dy;
  if(Math.abs(a)<1e-6){const t=(Math.abs(b)>1e-6)?-c/b:Math.hypot(dx,dy)/spd;return(t>0.005&&t<2)?t:Math.hypot(dx,dy)/spd;}
  const disc=b*b-4*a*c;if(disc<0)return Math.hypot(dx,dy)/spd;
  const sq=Math.sqrt(disc),t1=(-b-sq)/(2*a),t2=(-b+sq)/(2*a);
  return[t1,t2].filter(t=>t>0.005&&t<2).sort((a,b)=>a-b)[0]??Math.hypot(dx,dy)/spd;
}

function calcLeadWorld(target,myWorldPos,bulletSpeed,now){
  const pos=getPlayerPos(target);if(!pos)return{wx:myWorldPos.x,wy:myWorldPos.y,t:0};
  const{vx,vy}=_getVelocity(target.__id,pos,now);
  const dx=pos.x-myWorldPos.x,dy=pos.y-myWorldPos.y;
  const t=_solveIntercept(dx,dy,vx,vy,bulletSpeed);
  return{wx:pos.x+vx*t,wy:pos.y+vy*t,t};
}

// Main SA2 aim tick — call once per frame with the chosen target screen pos.
// Returns the current lerped aim position for drawing.
function _saApplyAim(targetSX,targetSY){
  const speed=0.04+(cfg.smoothAmount/100)*0.31; // maps slider 0-100 → 0.04-0.35
  _saLerpX=_saLerp(_saLerpX,targetSX,speed);
  _saLerpY=_saLerp(_saLerpY,targetSY,speed);
  _saAimX=_saLerpX;
  _saAimY=_saLerpY;
  return{x:_saLerpX,y:_saLerpY};
}

// When no target — glide back to real mouse
function _saReturnToMouse(){
  _saHasTarget=false;
  _saLerpX=_saLerp(_saLerpX,_saRealX,0.12);
  _saLerpY=_saLerp(_saLerpY,_saRealY,0.12);
  _saAimX=_saLerpX;_saAimY=_saLerpY;
}

// ── AIM / MAGNET / MISC ──────────────────────────────────────
const FIRE_GATE_RAD=2.5*(Math.PI/180);
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>';});};

// ── RESCAN ────────────────────────────────────────────────────
function doRescan(){
  setStatus('SCANNING...');
  localStorage.removeItem(_MAPPER_STORE);
  _lastScanResult=null;
  window.__VB_KEYMAP__=null;
  // Reset SA2 mouse hook so it re-attaches to the new input object
  _saInputHooked=false;
  const R=runAutoMapper();
  if(R&&!R.error&&applyMapperResult(R)){
    const missing=R._missing?.length??0;
    toast(missing===0?'Rescan OK — all keys found':`Rescan done — ${missing} key(s) missing`);
    setStatus(missing===0?'READY':'PARTIAL');
  } else {
    toast('Rescan failed — game not loaded yet');
    setStatus('RESCAN FAILED');
  }
  // Re-apply edit state after rescan in case DOM was rebuilt
  setTimeout(restoreEditState, 300);
}

// ============================================================
//  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:8px;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 11px;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);}
.rescan-btn{display:flex;align-items:center;justify-content:center;gap:5px;padding:6px 11px;border-radius:10px;background:linear-gradient(180deg,rgba(88,168,255,.18),rgba(60,120,255,.08));border:1px solid rgba(88,168,255,.3);color:#58a8ff;font:700 10px Inter,sans-serif;letter-spacing:1px;cursor:pointer;white-space:nowrap;transition:.18s;}
.rescan-btn:hover{background:linear-gradient(180deg,rgba(88,168,255,.28),rgba(60,120,255,.14));transform:translateY(-1px);}
.rescan-btn.scanning{animation:rescanPulse .7s infinite;}
@keyframes rescanPulse{0%,100%{opacity:1;}50%{opacity:.5;}}
#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;}
.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);}
.edit-input{width:100%;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);border-radius:8px;padding:7px 11px;font:11px Inter,sans-serif;color:#ebecf0;outline:none;}
.edit-input:focus{border-color:rgba(88,101,242,.5);}
.edit-row{display:flex;align-items:center;gap:8px;margin-bottom:8px;}
.edit-row label{font-size:10px;color:#7b7f88;letter-spacing:1px;min-width:52px;flex-shrink:0;}
.edit-row input[type=color]{flex:1;height:30px;border:1px solid rgba(255,255,255,.1);border-radius:6px;cursor:pointer;background:transparent;}
.edit-row input[type=range]{flex:1;}
.edit-row select{flex:1;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);border-radius:6px;padding:5px 8px;color:#ebecf0;font:11px Inter,sans-serif;}
.edit-row .edit-val{font-size:10px;color:#ebecf0;min-width:32px;text-align:right;}
.edit-btn{padding:8px 0;border-radius:8px;font:700 10px Inter,sans-serif;cursor:pointer;letter-spacing:.5px;border:1px solid;}
@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+ v3.5</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="edit">EDIT</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.5</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>
      <button class="rescan-btn" id="_vmRescanBtn" onclick="window._vmRescan()">🔄 RESCAN</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 (EMA+decay)</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 style="font-size:10px;color:#7b7f88;margin-top:4px">Predict: EMA vel + quadratic intercept. No jitter on stationary.</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">Predict</b> EMA velocity + quadratic intercept. Decays to zero when target stops — no oscillation.<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">Rescan</b> re-scans scrambled game data and re-applies all saved edits.
            </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>

      <!-- EDIT -->
      <div class="page" data-page="edit">
        <div style="display:flex;flex-direction:column;gap:12px">
          <div class="pcard">
            <div class="psec" style="margin-bottom:10px">BACKGROUND</div>
            <div style="font-size:10px;color:#7b7f88;margin-bottom:8px;line-height:1.6">Set an image or video. Hit APPLY then 💾 SAVE SETTINGS — it auto-restores on reload.</div>
            <div class="pgrid" style="gap:8px;margin-bottom:10px">
              <div class="ptgl blue" id="_vmBgTgl-image" onclick="window._vmBgSetType('image')"><span class="tlbl">IMAGE</span><div class="tpip"></div></div>
              <div class="ptgl purple" id="_vmBgTgl-video" onclick="window._vmBgSetType('video')"><span class="tlbl">VIDEO</span><div class="tpip"></div></div>
            </div>
            <div class="edit-row">
              <label>URL</label>
              <input type="text" id="_vmBgUrl" class="edit-input" placeholder="https://..."/>
            </div>
            <div class="edit-row">
              <label>UPLOAD</label>
              <label id="_vmBgFileLbl" style="flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:7px;background:rgba(255,255,255,.04);border:1px dashed rgba(255,255,255,.12);border-radius:8px;cursor:pointer;font:700 9px Inter,sans-serif;color:#7b7f88;letter-spacing:.5px">
                📁 CHOOSE FILE
                <input id="_vmBgFile" type="file" accept="image/*,video/*" style="display:none"/>
              </label>
            </div>
            <div id="_vmBgFileName" style="font-size:10px;color:#7b7f88;text-align:center;margin-bottom:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"></div>
            <div class="edit-row">
              <label>FIT</label>
              <select id="_vmBgFit" class="edit-input" style="padding:5px 8px">
                <option value="cover">cover</option>
                <option value="contain">contain</option>
                <option value="fill">stretch</option>
              </select>
            </div>
            <div class="edit-row">
              <label>DIM</label>
              <input type="range" id="_vmBgDim" min="0" max="0.85" step="0.01" value="0" style="flex:1"/>
              <span class="edit-val" id="_vmBgDimVal">0%</span>
            </div>
            <div id="_vmBgPreview" style="width:100%;height:90px;border-radius:8px;border:1px solid rgba(255,255,255,.07);background:#0a0a0a;overflow:hidden;position:relative;display:flex;align-items:center;justify-content:center;margin-bottom:10px">
              <span id="_vmBgPreviewLbl" style="font-size:10px;color:#444;letter-spacing:1px;position:absolute">NO MEDIA</span>
              <img id="_vmBgPreviewImg" style="width:100%;height:100%;object-fit:cover;display:none" alt=""/>
              <video id="_vmBgPreviewVid" muted loop style="width:100%;height:100%;object-fit:cover;display:none"></video>
              <div id="_vmBgPreviewDim" style="position:absolute;inset:0;background:#000;opacity:0;pointer-events:none"></div>
            </div>
            <div class="pgrid" style="gap:8px">
              <button onclick="window._vmBgApply()" class="edit-btn" style="background:rgba(91,196,112,.14);border-color:rgba(91,196,112,.3);color:#5bc470">APPLY</button>
              <button onclick="window._vmBgClear()" class="edit-btn" style="background:rgba(255,80,80,.08);border-color:rgba(255,80,80,.2);color:#ff6464">CLEAR</button>
            </div>
          </div>
          <div class="pcard">
            <div class="psec" style="margin-bottom:10px">ELEMENT EDITOR</div>
            <div style="font-size:10px;color:#7b7f88;margin-bottom:8px;line-height:1.6">Pick → style → drag. Hit 💾 SAVE SETTINGS to persist across reloads.</div>
            <div style="display:flex;gap:6px;margin-bottom:8px">
              <button id="_vmEditPickBtn" onclick="window._vmEditStartPick()" class="edit-btn" style="flex:1;background:rgba(88,168,255,.12);border-color:rgba(88,168,255,.25);color:#58a8ff">PICK ELEMENT</button>
              <button id="_vmEditDragBtn" onclick="window._vmEditToggleDrag()" class="edit-btn" style="flex:1;background:rgba(255,200,50,.08);border-color:rgba(255,200,50,.18);color:#ffc43c">DRAG: OFF</button>
              <button onclick="window._vmEditReset()" class="edit-btn" style="flex:1;background:rgba(255,80,80,.08);border-color:rgba(255,80,80,.18);color:#ff6464">RESET</button>
            </div>
            <div id="_vmEditSelected" style="font-size:10px;color:#7b7f88;padding:6px 10px;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.05);border-radius:7px;margin-bottom:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">nothing selected</div>
            <div style="font-size:10px;color:#7b7f88;letter-spacing:1.5px;margin-bottom:6px">BACKGROUND</div>
            <div class="pgrid3" style="gap:5px;margin-bottom:8px">
              <div class="ptgl" id="_vmElBgTgl-solid" onclick="window._vmElBgMode('solid')" style="height:30px;padding:0 8px"><span class="tlbl" style="font-size:9px">SOLID</span><div class="tpip"></div></div>
              <div class="ptgl" id="_vmElBgTgl-gradient" onclick="window._vmElBgMode('gradient')" style="height:30px;padding:0 8px"><span class="tlbl" style="font-size:9px">GRAD</span><div class="tpip"></div></div>
              <div class="ptgl" id="_vmElBgTgl-none" onclick="window._vmElBgMode('none')" style="height:30px;padding:0 8px"><span class="tlbl" style="font-size:9px">NONE</span><div class="tpip"></div></div>
            </div>
            <div id="_vmElSolid"><div class="edit-row"><label>COLOR</label><input type="color" id="_vmElBgColor" value="#111111"/></div></div>
            <div id="_vmElGrad" style="display:none">
              <div class="edit-row"><label>FROM</label><input type="color" id="_vmElGradA" value="#0f0c29"/></div>
              <div class="edit-row"><label>TO</label><input type="color" id="_vmElGradB" value="#24243e"/></div>
              <div class="edit-row"><label>ANGLE</label><input type="range" id="_vmElGradAngle" min="0" max="360" step="1" value="135" style="flex:1"/><span class="edit-val" id="_vmElGradAngleVal">135°</span></div>
            </div>
            <div style="font-size:10px;color:#7b7f88;letter-spacing:1.5px;margin:8px 0 6px">TEXT</div>
            <div class="edit-row"><label>COLOR</label><input type="color" id="_vmElTxtColor" value="#ffffff"/></div>
            <div class="edit-row"><label>SIZE</label><input type="range" id="_vmElFontSize" min="8" max="64" step="1" value="14" style="flex:1"/><span class="edit-val" id="_vmElFontSizeVal">14px</span></div>
            <div style="font-size:10px;color:#7b7f88;letter-spacing:1.5px;margin:8px 0 6px">BORDER + RADIUS</div>
            <div class="edit-row"><label>COLOR</label><input type="color" id="_vmElBorderColor" value="#36a7ff"/></div>
            <div class="edit-row"><label>WIDTH</label><input type="range" id="_vmElBorderW" min="0" max="10" step="1" value="0" style="flex:1"/><span class="edit-val" id="_vmElBorderWVal">0px</span></div>
            <div class="edit-row"><label>RADIUS</label><input type="range" id="_vmElRadius" min="0" max="40" step="1" value="0" style="flex:1"/><span class="edit-val" id="_vmElRadiusVal">0px</span></div>
            <div style="font-size:10px;color:#7b7f88;letter-spacing:1.5px;margin:8px 0 6px">SIZE + OPACITY</div>
            <div class="edit-row"><label>SCALE</label><input type="range" id="_vmElScale" min="0.1" max="3" step="0.05" value="1" style="flex:1"/><span class="edit-val" id="_vmElScaleVal">1.0×</span></div>
            <div class="edit-row"><label>OPACITY</label><input type="range" id="_vmElOpacity" min="0" max="1" step="0.01" value="1" style="flex:1"/><span class="edit-val" id="_vmElOpacityVal">1.0</span></div>
            <div style="display:flex;gap:6px;margin-top:8px">
              <button onclick="window._vmEditApply()" class="edit-btn" style="flex:1;padding:10px;background:linear-gradient(180deg,rgba(91,196,112,.18),rgba(60,160,80,.08));border-color:rgba(91,196,112,.3);color:#5bc470;font-size:11px">APPLY</button>
              <button onclick="window._vmEditDelete()" class="edit-btn" style="flex:1;padding:10px;background:rgba(255,80,80,.1);border-color:rgba(255,80,80,.25);color:#ff6464;font-size:11px">DELETE</button>
            </div>
          </div>
          <button onclick="window._vmReturnToNormal()" style="width:100%;margin-top:4px;padding:14px;border-radius:12px;border:1px solid rgba(255,120,50,.35);background:linear-gradient(180deg,rgba(255,120,50,.14),rgba(200,80,20,.08));color:#ff8c42;font:700 11px Inter,sans-serif;letter-spacing:1.2px;cursor:pointer;transition:.18s;" onmouseover="this.style.background='linear-gradient(180deg,rgba(255,120,50,.26),rgba(200,80,20,.16))'" onmouseout="this.style.background='linear-gradient(180deg,rgba(255,120,50,.14),rgba(200,80,20,.08))'">↩ RETURN TO NORMAL — reset background, positions &amp; colors</button>
        </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>

      <!-- 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.5</span>
              <span style="font-size:9px;color:#5bc470;letter-spacing:1px">LATEST</span>
            </div>
            <div style="font-size:11px;color:#7b7f88;line-height:1.8">
              <span style="color:#5bc470">+</span> Updated prediction + aimbot system — replaced with SimpleAim v2 engine: mouse proxy hook, frame-rate independent lerp smooth aim, glides back to real cursor when no target<br>
              <span style="color:#5bc470">+</span> EDIT tab — background image/video, element color editor, drag mode<br>
              <span style="color:#5bc470">+</span> 💾 Save Settings now persists background, element colors, drag positions and deletions across reloads<br>
              <span style="color:#5bc470">+</span> 🔄 Rescan button — re-scans scrambled game keys and re-applies all saved edits instantly<br>
              <span style="color:#5bc470">+</span> Element editor uses CSS injection (background-color only) — preserves background-image like potato logo<br>
              <span style="color:#5bc470">+</span> Proper box-shadow darkening for button bottom highlight
            </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.4</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>
            </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>
            </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>

    </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();};
window._vmRescan=function(){
  const btn=document.getElementById('_vmRescanBtn');
  if(btn)btn.classList.add('scanning');
  setTimeout(()=>{
    doRescan();
    if(btn)btn.classList.remove('scanning');
  },100);
};

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);
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);};

// ============================================================
//  BG EDITOR
// ============================================================
let _bgType='image';
let _bgEl=null;
window._vmBgSetType=function(t){_bgType=t;document.getElementById('_vmBgTgl-image').classList.toggle('on',t==='image');document.getElementById('_vmBgTgl-video').classList.toggle('on',t==='video');};
window._vmBgSetType('image');
function _bgUpdatePreview(url){
  const img=document.getElementById('_vmBgPreviewImg');const vid=document.getElementById('_vmBgPreviewVid');const lbl=document.getElementById('_vmBgPreviewLbl');const fit=document.getElementById('_vmBgFit')?.value||'cover';
  if(!url){img.style.display='none';vid.style.display='none';lbl.style.display='block';return;}
  lbl.style.display='none';
  if(_bgType==='video'){img.style.display='none';vid.style.display='block';vid.src=url;vid.style.objectFit=fit;vid.play().catch(()=>{});}
  else{vid.style.display='none';img.style.display='block';img.src=url;img.style.objectFit=fit;}
}
document.getElementById('_vmBgUrl').addEventListener('input',function(){_bgUpdatePreview(this.value.trim());});
document.getElementById('_vmBgDim').addEventListener('input',function(){document.getElementById('_vmBgDimVal').textContent=Math.round(this.value*100)+'%';const d=document.getElementById('_vmBgPreviewDim');if(d)d.style.opacity=this.value;});
let _bgObjUrl=null;
document.getElementById('_vmBgFile').addEventListener('change',function(){
  const f=this.files[0];if(!f)return;
  if(_bgObjUrl){URL.revokeObjectURL(_bgObjUrl);_bgObjUrl=null;}
  const isVid=f.type.startsWith('video/');
  window._vmBgSetType(isVid?'video':'image');
  document.getElementById('_vmBgFileName').textContent=f.name;
  document.getElementById('_vmBgFileLbl').style.background='rgba(91,196,112,.08)';
  document.getElementById('_vmBgFileLbl').style.borderColor='rgba(91,196,112,.3)';
  // Read as base64 so it persists across reloads (blob: URLs die on reload)
  const reader=new FileReader();
  reader.onload=function(ev){
    const dataUrl=ev.target.result;
    document.getElementById('_vmBgUrl').value=dataUrl;
    _bgUpdatePreview(dataUrl);
  };
  reader.readAsDataURL(f);
});
window._vmBgApply=function(){
  const url=(document.getElementById('_vmBgUrl').value||'').trim();
  if(!url){toast('Enter a URL or upload a file first');return;}
  const fit=document.getElementById('_vmBgFit').value||'cover';
  const dim=parseFloat(document.getElementById('_vmBgDim').value)||0;
  _bgSaved={type:_bgType,url,fit,dim};
  _bgApplyToPage(_bgType,url,fit,dim);
  toast('Background applied — hit 💾 to save');
};
function _bgApplyToPage(type,url,fit,dim){
  if(_bgEl&&document.body.contains(_bgEl))_bgEl.remove();
  if(!url)return;
  const target=document.getElementById('background');
  if(!target)return;
  _bgEl=document.createElement('div');
  _bgEl.id='vb-bg';
  _bgEl.style.cssText='position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;';
  if(type==='video'){
    const v=document.createElement('video');v.src=url;v.autoplay=true;v.muted=true;v.loop=true;
    v.style.cssText=`width:100%;height:100%;object-fit:${fit};display:block;`;
    _bgEl.appendChild(v);
  } else {
    _bgEl.style.backgroundImage=`url("${url}")`;
    _bgEl.style.backgroundSize=fit==='fill'?'100% 100%':fit;
    _bgEl.style.backgroundPosition='center';
    _bgEl.style.backgroundRepeat='no-repeat';
  }
  const dimDiv=document.createElement('div');
  dimDiv.style.cssText=`position:absolute;inset:0;background:#000;opacity:${dim};pointer-events:none`;
  _bgEl.appendChild(dimDiv);
  target.style.setProperty('position','absolute','important');
  target.style.setProperty('overflow','hidden','important');
  target.insertBefore(_bgEl,target.firstChild);
  // sync UI
  const urlEl=document.getElementById('_vmBgUrl');if(urlEl&&!urlEl.value)urlEl.value=url;
  _bgUpdatePreview(url);
}
window._vmBgClear=function(){
  if(_bgEl&&document.body.contains(_bgEl))_bgEl.remove();
  _bgEl=null;
  _bgSaved={type:'image',url:'',fit:'cover',dim:0};
  document.getElementById('_vmBgUrl').value='';
  const fn=document.getElementById('_vmBgFileName');if(fn)fn.textContent='';
  const lbl=document.getElementById('_vmBgFileLbl');if(lbl){lbl.style.background='rgba(255,255,255,.04)';lbl.style.borderColor='rgba(255,255,255,.12)';}
  const fi=document.getElementById('_vmBgFile');if(fi)fi.value='';
  const img=document.getElementById('_vmBgPreviewImg');if(img){img.style.display='none';img.src='';}
  const vid=document.getElementById('_vmBgPreviewVid');if(vid){vid.style.display='none';vid.src='';}
  document.getElementById('_vmBgPreviewLbl').style.display='block';
  toast('Background cleared');
};

// ============================================================
//  ELEMENT EDITOR
// ============================================================
let _elTarget=null,_elOrigStyle={},_elPicking=false,_elBgModeVal='solid',_elDragOn=false;
let _elDragState={active:false,startX:0,startY:0,origLeft:0,origTop:0};
const _vbBaseXform=new WeakMap();

window._vmElBgMode=function(mode){
  _elBgModeVal=mode;
  ['solid','gradient','none'].forEach(m=>{document.getElementById('_vmElBgTgl-'+m)?.classList.toggle('on',m===mode);});
  document.getElementById('_vmElSolid').style.display=mode==='solid'?'block':'none';
  document.getElementById('_vmElGrad').style.display=mode==='gradient'?'block':'none';
};
window._vmElBgMode('solid');

function _elDragMove(e){
  if(!_elDragState.active||!_elTarget)return;
  const cx=e.touches?e.touches[0].clientX:e.clientX;
  const cy=e.touches?e.touches[0].clientY:e.clientY;
  const newLeft=_elDragState.origLeft+(cx-_elDragState.startX);
  const newTop=_elDragState.origTop+(cy-_elDragState.startY);
  _elTarget.style.setProperty('position','fixed','important');
  _elTarget.style.setProperty('left',newLeft+'px','important');
  _elTarget.style.setProperty('top',newTop+'px','important');
  _elTarget.style.setProperty('margin','0','important');
  // Update drag record live so it's ready to save
  const key=_elTarget.getAttribute(VB_KEY_ATTR);
  if(key)_draggedEls[key]={left:newLeft,top:newTop};
}
function _elDragEnd(){_elDragState.active=false;}
function _elDragDown(e){
  if(!_elDragOn||!_elTarget)return;
  if(e.target.closest('#voidUI'))return;
  if(e.target!==_elTarget&&!_elTarget.contains(e.target))return;
  e.preventDefault();
  // Ensure element has a stable key before we start tracking its position
  _vbKey(_elTarget);
  const cx=e.touches?e.touches[0].clientX:e.clientX;
  const cy=e.touches?e.touches[0].clientY:e.clientY;
  const r=_elTarget.getBoundingClientRect();
  _elDragState={active:true,startX:cx,startY:cy,origLeft:r.left,origTop:r.top};
}

window._vmEditToggleDrag=function(){
  _elDragOn=!_elDragOn;
  const btn=document.getElementById('_vmEditDragBtn');
  if(btn){btn.textContent='DRAG: '+(_elDragOn?'ON':'OFF');btn.style.background=_elDragOn?'rgba(255,200,50,.22)':'rgba(255,200,50,.08)';btn.style.borderColor=_elDragOn?'rgba(255,200,50,.5)':'rgba(255,200,50,.18)';}
  if(_elTarget)_elTarget.style.cursor=_elDragOn?'grab':'';
  if(_elDragOn){
    document.addEventListener('mousedown',_elDragDown,true);document.addEventListener('mousemove',_elDragMove,true);document.addEventListener('mouseup',_elDragEnd,true);
    document.addEventListener('touchstart',_elDragDown,{passive:false,capture:true});document.addEventListener('touchmove',_elDragMove,{passive:false,capture:true});document.addEventListener('touchend',_elDragEnd,true);
    toast('Drag ON — grab element');
  } else {
    document.removeEventListener('mousedown',_elDragDown,true);document.removeEventListener('mousemove',_elDragMove,true);document.removeEventListener('mouseup',_elDragEnd,true);
    document.removeEventListener('touchstart',_elDragDown,true);document.removeEventListener('touchmove',_elDragMove,true);document.removeEventListener('touchend',_elDragEnd,true);
    toast('Drag OFF');
  }
};

function _elPickClick(e){
  if(e.target.closest('#voidUI')){_elStopPick();return;}
  e.preventDefault();e.stopPropagation();
  _elTarget=e.target;
  _elOrigStyle={};
  for(const p of _elTarget.style)_elOrigStyle[p]=_elTarget.style.getPropertyValue(p);
  if(!_vbBaseXform.has(_elTarget)){const cs=getComputedStyle(_elTarget).transform;_vbBaseXform.set(_elTarget,cs&&cs!=='none'?cs:'');}
  const tag=_elTarget.tagName.toLowerCase();
  const id=_elTarget.id?'#'+_elTarget.id:'';
  const cls=typeof _elTarget.className==='string'&&_elTarget.className.trim()?'.'+_elTarget.className.trim().split(/\s+/)[0]:'';
  const lbl=document.getElementById('_vmEditSelected');
  if(lbl)lbl.textContent=(tag+(id||cls))+' — selected ✓';
  if(_elDragOn)_elTarget.style.cursor='grab';
  _elStopPick();
  toast('Picked — style or drag it');
}
function _elStopPick(){
  _elPicking=false;
  document.removeEventListener('click',_elPickClick,true);
  document.body.style.cursor='';
  const btn=document.getElementById('_vmEditPickBtn');
  if(btn){btn.textContent='PICK ELEMENT';btn.style.background='rgba(88,168,255,.12)';}
}
window._vmEditStartPick=function(){
  if(_elPicking){_elStopPick();return;}
  _elPicking=true;
  document.addEventListener('click',_elPickClick,true);
  document.body.style.cursor='crosshair';
  const btn=document.getElementById('_vmEditPickBtn');
  if(btn){btn.textContent='CLICK AN ELEMENT…';btn.style.background='rgba(255,200,50,.18)';}
  toast('Click anything on the page');
};

window._vmEditApply=function(){
  if(!_elTarget){toast('Pick an element first');return;}
  const key=_vbKey(_elTarget);
  _vbRules[key]={
    bgMode:_elBgModeVal,
    bgColor:document.getElementById('_vmElBgColor')?.value??'#111111',
    gradA:document.getElementById('_vmElGradA')?.value??'#0f0c29',
    gradB:document.getElementById('_vmElGradB')?.value??'#24243e',
    gradAngle:Number(document.getElementById('_vmElGradAngle')?.value)||135,
    textColor:document.getElementById('_vmElTxtColor')?.value??'#ffffff',
    fontSize:Number(document.getElementById('_vmElFontSize')?.value)||0,
    borderColor:document.getElementById('_vmElBorderColor')?.value??'#36a7ff',
    borderW:Number(document.getElementById('_vmElBorderW')?.value)||0,
    borderR:Number(document.getElementById('_vmElRadius')?.value)||0,
    scale:Number(document.getElementById('_vmElScale')?.value)||1,
    opacity:parseFloat(document.getElementById('_vmElOpacity')?.value??'1'),
    baseTransform:_vbBaseXform.get(_elTarget)||''
  };
  _vbFlush();
  toast('Applied ✓ — hit 💾 to save');
};

window._vmEditReset=function(){
  if(!_elTarget){toast('Nothing selected');return;}
  const key=_elTarget.getAttribute(VB_KEY_ATTR);
  if(key){delete _vbRules[key];delete _draggedEls[key];_elTarget.removeAttribute(VB_KEY_ATTR);_vbFlush();}
  while(_elTarget.style.length)_elTarget.style.removeProperty(_elTarget.style[0]);
  for(const[p,v]of Object.entries(_elOrigStyle))_elTarget.style.setProperty(p,v);
  document.getElementById('_vmEditSelected').textContent='nothing selected';
  _elTarget=null;
  toast('Reset to original');
};

window._vmEditDelete=function(){
  if(!_elTarget){toast('Nothing selected');return;}
  const tag=_elTarget.tagName.toLowerCase();
  const id=_elTarget.id?'#'+_elTarget.id:'';
  const key=_vbKey(_elTarget); // stamp key before removing
  if(key){
    delete _vbRules[key];
    delete _draggedEls[key];
    // Record deletion so it can be restored on reload
    if(!_deletedKeys.includes(key))_deletedKeys.push(key);
    _vbFlush();
  }
  _elTarget.remove();
  _elTarget=null;
  document.getElementById('_vmEditSelected').textContent='nothing selected';
  toast('Deleted '+tag+id+' — hit 💾 to save');
};

window._vmReturnToNormal=function(){
  if(!confirm('Reset everything back to normal? This clears background, all moved/colored/deleted elements and wipes saved edit state.'))return;

  // 1. Remove background
  window._vmBgClear();

  // 2. Remove all CSS injection rules
  _vbRules={};
  _vbFlush();

  // 3. Restore all dragged elements to original position
  for(const key of Object.keys(_draggedEls)){
    const el=_elByKey(key);
    if(!el)continue;
    el.style.removeProperty('position');
    el.style.removeProperty('left');
    el.style.removeProperty('top');
    el.style.removeProperty('margin');
  }
  _draggedEls={};

  // 4. Restore deleted elements — reload page is cleanest since removed nodes are gone from DOM
  // We clear the list; if any were deleted this session they're already gone, page reload will fix
  const hadDeleted=_deletedKeys.length>0;
  _deletedKeys=[];

  // 5. Remove all data-vb-key stamps
  document.querySelectorAll(`[${VB_KEY_ATTR}]`).forEach(el=>el.removeAttribute(VB_KEY_ATTR));

  // 6. Clear selected target
  _elTarget=null;
  const selEl=document.getElementById('_vmEditSelected');
  if(selEl)selEl.textContent='nothing selected';

  // 7. Wipe saved edit state from localStorage
  localStorage.removeItem(EDIT_STORE);

  if(hadDeleted){
    toast('Done — reload page to restore deleted elements');
  } else {
    toast('Returned to normal ✓');
  }
};

[['_vmElGradAngle','_vmElGradAngleVal',v=>v+'°'],['_vmElFontSize','_vmElFontSizeVal',v=>v+'px'],['_vmElBorderW','_vmElBorderWVal',v=>v+'px'],['_vmElRadius','_vmElRadiusVal',v=>v+'px'],['_vmElScale','_vmElScaleVal',v=>parseFloat(v).toFixed(2)+'×'],['_vmElOpacity','_vmElOpacityVal',v=>parseFloat(v).toFixed(2)]].forEach(([rid,vid,fmt])=>{const r=document.getElementById(rid),v=document.getElementById(vid);if(r&&v)r.oninput=()=>{v.textContent=fmt(r.value);};});

// ── AUTO-MAPPER
function _autoMapperRun(){const R=runAutoMapper();if(R&&!R.error){applyMapperResult(R);console.log('[VB] Auto-mapper:',R._missing.length===0?'all keys found':`${R._missing.length} missing`);}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)velMap.clear();
  if(key==='stickyTarget'&&!cfg.stickyTarget)_stickyTarget=null;
  if(key==='lockOn'&&!cfg.lockOn){_saReturnToMouse();_saHasTarget=false;}
  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();}}};
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');});

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;}
  // Restore bg url in editor
  if(_bgSaved.url){const urlEl=document.getElementById('_vmBgUrl');if(urlEl)urlEl.value=_bgSaved.url;window._vmBgSetType(_bgSaved.type||'image');}
}

// ============================================================
//  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);
  // Cache DOM refs so we don't query every frame
  const _elFps=document.getElementById('fpsChip');
  const _elMs=document.getElementById('msChip');
  let _lastKillText='';
  let _slowTick=0; // counter for throttled work (velMap cleanup, DOM status)

  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);
    _slowTick++;

    // FPS chip — update once per second
    if(now-_fpsLast>=1000){_fps=_fpsFrames;_fpsFrames=0;_fpsLast=now;if(_elFps){_elFps.textContent=`${_fps} FPS`;_elFps.style.color=_fps>=55?'#5bc470':_fps>=30?'#e8c060':'#e87a7a';}}
    // MS chip — every 6 frames (~100ms) is plenty
    if(_slowTick%6===0&&_elMs){_elMs.textContent=`${_ms} MS`;_elMs.style.color=_ms<=16?'#5bc470':_ms<=33?'#e8c060':'#e87a7a';}
    // Kill count — DOM query throttled to every 30 frames
    if(_slowTick%30===0){const killEl=document.querySelector('.js-ui-player-kills');if(killEl&&killEl.textContent!==_lastKillText){_lastKillText=killEl.textContent;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();
    // X-ray every 10 frames — no need to spam it
    if(_slowTick%1===0)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;

    // Hook mouse proxy on first available input (SA2 style)
    if(input&&!_saInputHooked)_saHookMouse(input);

    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();}

    // VelMap cleanup throttled to every 60 frames
    if(_slowTick%60===0){const activeIds=new Set(players.filter(p=>p.active&&!isPlayerDead(p)).map(p=>p.__id));for(const[id] of velMap)if(!activeIds.has(id))velMap.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&&(targetHasLOS||!cfg.wallcheck)){
      _saHasTarget=true;
      let tsx=activeTarget.sx,tsy=activeTarget.sy;

      if(cfg.predict&&myWorldPos&&worldContainer){
        const aw=(me.weapTypeOld??'').toLowerCase();const bspd=(GUN_DATA[aw]??{bulletSpeed:100}).bulletSpeed;
        const lead=calcLeadWorld(activeTarget.p,myWorldPos,bspd,now);
        const psx=worldContainer.x+lead.wx*STUD;const psy=worldContainer.y-lead.wy*STUD;
        // draw prediction ripple
        const pulse=(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;
        const flightMs=Math.round(lead.t*1000);octx.font='bold 9px monospace';octx.textAlign='center';octx.textBaseline='top';octx.strokeStyle='rgba(0,0,0,0.8)';octx.lineWidth=2.5;octx.strokeText(`${flightMs}ms`,psx,psy+9);octx.fillStyle='rgba(200,160,255,0.85)';octx.fillText(`${flightMs}ms`,psx,psy+9);octx.restore();
        tsx=psx;tsy=psy;
      }

      // SA2: smooth lerp toward target — game reads _saAimX/Y via proxy
      const aimed=_saApplyAim(tsx,tsy);

      // Draw aim line from self to current lerped aim point
      octx.save();
      octx.beginPath();octx.moveTo(msx,msy);octx.lineTo(aimed.x,aimed.y);
      octx.setLineDash(cfg.predict?[]:[6,4]);
      octx.strokeStyle=cfg.predict?'rgba(168,85,247,0.7)':'rgba(255,80,80,0.75)';
      octx.lineWidth=2;octx.stroke();octx.setLineDash([]);
      octx.beginPath();octx.arc(aimed.x,aimed.y,6,0,Math.PI*2);
      octx.fillStyle=cfg.predict?'rgba(168,85,247,0.85)':'rgba(255,80,80,0.85)';
      octx.fill();octx.strokeStyle='#fff';octx.lineWidth=1.5;octx.stroke();
      octx.restore();
    } else {
      // No target or locked off — glide smoothly back to real mouse
      _saReturnToMouse();
    }

    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();}});
  // Restore edit state (CSS rules, drag positions, deletions, bg) — bg uses polling so it's fine to call early
  restoreEditState();
  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.5] ESC=panel | T=lock | E=magnet | Q=macro | N=sticky | HOLD-Y=layer | 💾=save all | 🔄=rescan','color:#a855f7;font-weight:bold');
})();