Score spoofer
// ==UserScript==
// @name Aced Helper v1.3
// @namespace https://omoggle.com/
// @version 1.3.0
// @description Score spoofer
// @match https://omoggle.com/*
// @match https://*.omoggle.com/*
// @grant unsafeWindow
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
const W = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window;
const LS_KEY = 'aced_cfg_v1';
const FileStore = {
DB: 'aced_files', STORE: 'media', KEY: 'afk', _db: null,
_open() {
if (this._db) return Promise.resolve(this._db);
return new Promise((res, rej) => {
const req = indexedDB.open(this.DB, 1);
req.onupgradeneeded = () => { const db = req.result; if (!db.objectStoreNames.contains(this.STORE)) db.createObjectStore(this.STORE); };
req.onsuccess = () => { this._db = req.result; res(this._db); };
req.onerror = () => rej(req.error);
});
},
async save(blob, meta) {
try { const db = await this._open(); return await new Promise((res, rej) => { const tx = db.transaction(this.STORE, 'readwrite'); tx.objectStore(this.STORE).put({ blob, meta }, this.KEY); tx.oncomplete = () => res(true); tx.onerror = () => rej(tx.error); }); } catch (e) { return false; }
},
async load() {
try { const db = await this._open(); return await new Promise((res) => { const tx = db.transaction(this.STORE, 'readonly'); const rq = tx.objectStore(this.STORE).get(this.KEY); rq.onsuccess = () => res(rq.result || null); rq.onerror = () => res(null); }); } catch (e) { return null; }
},
async clear() {
try { const db = await this._open(); return await new Promise((res) => { const tx = db.transaction(this.STORE, 'readwrite'); tx.objectStore(this.STORE).delete(this.KEY); tx.oncomplete = () => res(true); tx.onerror = () => res(false); }); } catch (e) { return false; }
},
};
const THEMES = {
red: { bg:'#0a0709', surf:'#110d0f', surf2:'#181215', border:'rgba(180,30,50,0.18)', border2:'rgba(180,30,50,0.32)', accent:'#c0192c', accent2:'#e02240', accent3:'#ff3355', accentDim:'rgba(192,25,44,0.12)', accentGlow:'rgba(224,34,64,0.22)', green:'#22a84a', green2:'#2fd65e', muted:'#5a4a50', muted2:'#8a7078', text:'#e8dde0', text2:'#c0aab0' },
blue: { bg:'#07090a', surf:'#0d1011', surf2:'#121518', border:'rgba(30,100,180,0.18)', border2:'rgba(30,100,180,0.32)', accent:'#1964c0', accent2:'#2278e0', accent3:'#3399ff', accentDim:'rgba(25,100,192,0.12)', accentGlow:'rgba(34,120,224,0.22)', green:'#22a84a', green2:'#2fd65e', muted:'#4a5060', muted2:'#7080a0', text:'#dde2e8', text2:'#aab8c0' },
green: { bg:'#07090a', surf:'#0d1110', surf2:'#121815', border:'rgba(30,160,80,0.18)', border2:'rgba(30,160,80,0.32)', accent:'#19a050', accent2:'#22c060', accent3:'#33ff99', accentDim:'rgba(25,160,80,0.12)', accentGlow:'rgba(34,192,96,0.22)', green:'#22a84a', green2:'#2fd65e', muted:'#4a6050', muted2:'#708070', text:'#dde8e0', text2:'#aac0b0' },
purple: { bg:'#09070a', surf:'#110d11', surf2:'#181218', border:'rgba(120,30,180,0.18)', border2:'rgba(120,30,180,0.32)', accent:'#7819c0', accent2:'#9022e0', accent3:'#cc33ff', accentDim:'rgba(120,25,192,0.12)', accentGlow:'rgba(144,34,224,0.22)', green:'#22a84a', green2:'#2fd65e', muted:'#584a60', muted2:'#887090', text:'#e4dde8', text2:'#b8aac0' },
gold: { bg:'#0a0900', surf:'#110f00', surf2:'#181500', border:'rgba(180,140,0,0.18)', border2:'rgba(180,140,0,0.32)', accent:'#b08800', accent2:'#d4a800', accent3:'#ffd700', accentDim:'rgba(180,140,0,0.12)', accentGlow:'rgba(212,168,0,0.22)', green:'#22a84a', green2:'#2fd65e', muted:'#605040', muted2:'#908060', text:'#e8e0cc', text2:'#c0b080' },
cyan: { bg:'#06090a', surf:'#0b1012', surf2:'#101619', border:'rgba(0,180,200,0.18)', border2:'rgba(0,180,200,0.34)', accent:'#0098b0', accent2:'#00c0d8', accent3:'#22e5ff', accentDim:'rgba(0,180,200,0.12)', accentGlow:'rgba(0,200,224,0.25)', green:'#22a84a', green2:'#2fd65e', muted:'#3a5560', muted2:'#5a8590', text:'#d8eef2', text2:'#a0c8d0' },
pink: { bg:'#0a060a', surf:'#120b12', surf2:'#191019', border:'rgba(220,40,140,0.18)', border2:'rgba(220,40,140,0.34)', accent:'#c01880', accent2:'#e022a0', accent3:'#ff44cc', accentDim:'rgba(220,40,140,0.12)', accentGlow:'rgba(224,34,160,0.25)', green:'#22a84a', green2:'#2fd65e', muted:'#603a55', muted2:'#905a85', text:'#f2d8ec', text2:'#d0a0c8' },
mono: { bg:'#08080a', surf:'#101012', surf2:'#161618', border:'rgba(200,200,210,0.14)', border2:'rgba(200,200,210,0.28)', accent:'#888890', accent2:'#b0b0b8', accent3:'#e8e8f0', accentDim:'rgba(200,200,210,0.10)', accentGlow:'rgba(220,220,230,0.20)', green:'#22a84a', green2:'#2fd65e', muted:'#55555c', muted2:'#85858c', text:'#e8e8ec', text2:'#b0b0b8' },
};
// Animated themes: a base palette + an animation class applied to #ac-root
const ANIM_THEMES = {
rainbow: { base:'red', anim:'ac-anim-rainbow', label:'Rainbow' },
aurora: { base:'cyan', anim:'ac-anim-aurora', label:'Aurora' },
sunset: { base:'pink', anim:'ac-anim-sunset', label:'Sunset' },
matrix: { base:'green',anim:'ac-anim-matrix', label:'Matrix' },
};
const DEFAULTS = {
enabled:true, liveSpoof:true, liveMode:'fluctuate', liveMin:9.1, liveMax:9.3,
finalMin:9.3, finalMax:9.6, realisticScoring:false, autoRequeue:false, autoStart:false,
afkCam:false, autofarmDelay:3500, mirrorOpponent:false, invertCam:false, loopVideo:true,
afkSource:'Video', zoomFit:false, guiHideKey:'h', liveSpoofKey:'1', finalScoreKey:'f',
profiles:{}, theme:'red',
// New tweaks
panicKey:'`', clickAnim:true, scoreDrift:0.05, opacity:100, compactMode:false, randomizeStart:false,
panelFX:true,
};
let CFG;
try { CFG = Object.assign({}, DEFAULTS, JSON.parse(W.localStorage.getItem(LS_KEY) || '{}')); }
catch (e) { CFG = Object.assign({}, DEFAULTS); }
if (typeof CFG.profiles !== 'object' || Array.isArray(CFG.profiles)) CFG.profiles = {};
if (!THEMES[CFG.theme] && !ANIM_THEMES[CFG.theme]) CFG.theme = 'red';
function saveCfg() { try { W.localStorage.setItem(LS_KEY, JSON.stringify(CFG)); } catch (e) {} }
let framesSent = 0;
let currentScore = (DEFAULTS.finalMin + DEFAULTS.finalMax) / 2;
const PARAMS = { FH:0.62,FW:0.394,JW:0.3597,JY:0.26,EY:0.058,IH:0.05,EW:0.0969,EH:0.029,T:0.0005,MOUTH:0.2103 };
function genNorm(P) {
const lm=Array.from({length:478},()=>({x:0.5,y:0.5,z:0}));
const cx=0.5,cy=0.5;
const set=(li,ri,hw,yy)=>{lm[li]={x:cx-hw,y:yy,z:0};lm[ri]={x:cx+hw,y:yy,z:0};};
lm[10]={x:cx,y:cy-P.FH/2,z:0};lm[152]={x:cx,y:cy+P.FH/2,z:0};
lm[234]={x:cx-P.FW/2,y:cy,z:0};lm[454]={x:cx+P.FW/2,y:cy,z:0};
set(172,397,P.JW/2,cy+P.JY);set(150,379,P.JW/2,cy+P.JY*0.9);set(171,396,P.JW/2,cy+P.JY*1.1);
const eyeY=cy-P.EY;
lm[133]={x:cx-P.IH,y:eyeY,z:0};lm[33]={x:cx-P.IH-P.EW,y:eyeY-P.T,z:0};
lm[362]={x:cx+P.IH,y:eyeY,z:0};lm[263]={x:cx+P.IH+P.EW,y:eyeY-P.T,z:0};
const lcx=cx-P.IH-P.EW/2,rcx=cx+P.IH+P.EW/2;
lm[159]={x:lcx,y:eyeY-P.EH/2,z:0};lm[145]={x:lcx,y:eyeY+P.EH/2,z:0};
lm[386]={x:rcx,y:eyeY-P.EH/2,z:0};lm[374]={x:rcx,y:eyeY+P.EH/2,z:0};
lm[1]={x:cx,y:cy,z:0};lm[0]={x:cx,y:eyeY+P.MOUTH,z:0};
const fill=[[70,300,.18,-.12],[63,293,.14,-.14],[105,334,.10,-.16],[46,276,.20,-.10],[116,345,.22,.02],[123,352,.20,.06],[50,280,.18,.10],[187,411,.16,.16],[132,361,.24,0],[174,399,.06,.20],[136,365,.12,.20],[148,377,.08,.24],[176,401,.10,.22],[58,288,.22,-.04]];
for(const f of fill){lm[f[0]]={x:cx-f[2],y:cy+f[3],z:0};lm[f[1]]={x:cx+f[2],y:cy+f[3],z:0};}
return lm;
}
const _NORM=genNorm(PARAMS);
const PEAK_DELTAS={0:[.00246,-.00153],133:[-.00555,-.00524],159:[.00505,.00195],176:[.00405,-.00590],187:[.00447,.00585],293:[.00173,-.00283],352:[-.00054,.00220],362:[.00590,.00180],386:[.00415,.00390]};
const LOW_DELTAS={0:[-.00589,.00354],33:[.00240,-.00215],50:[-.00312,-.00189],58:[-.00750,-.00572],105:[-.00128,.00165],116:[-.00368,.00270],123:[.00578,.00299],145:[.00243,.00522],159:[.00304,-.00501],171:[.00375,-.00190],263:[-.00048,.00605],300:[-.00572,.00073],334:[.00191,-.00197],352:[.00010,.00005],365:[.00352,-.00450],374:[.00455,-.00071],377:[.00595,.00473],379:[-.00028,.00566],386:[-.00028,-.01653],399:[-.00495,-.00209],401:[.00373,-.00379]};
function applyD(d){const a=_NORM.map(p=>({x:p.x,y:p.y,z:0}));for(const k in d)a[+k]={x:_NORM[+k].x+d[k][0],y:_NORM[+k].y+d[k][1],z:0};return a;}
const _LOW=applyD(LOW_DELTAS),_PEAK=applyD(PEAK_DELTAS);
function faceL(L){return _LOW.map((p,i)=>({x:p.x+(_PEAK[i].x-p.x)*L,y:p.y+(_PEAK[i].y-p.y)*L,z:0}));}
const _WP=[[33,263],[133,362],[70,300],[63,293],[105,334],[46,276],[116,345],[123,352],[50,280],[187,411],[132,361],[174,399],[150,379],[172,397],[136,365],[171,396],[148,377],[176,401],[58,288]];
const _F=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
const _L=(a,b)=>Math.atan2(b.y-a.y,b.x-a.x)*57.29578;
const _O=(e,t,r)=>Math.max(t,Math.min(r,e));
const _B=(e,t,r,a,n)=>e>=t&&e<=r?10:e<t?_O((e-a)/(t-a)*10,0,10):_O((n-e)/(n-r)*10,0,10);
function simpleScore(p){
const m=-((_L(p[33],p[133])+_L(p[362],p[263]))/2);
let jx=0;for(const pr of[[172,397],[150,379],[171,396]]){const r=_F(p[pr[0]],p[pr[1]]);if(r>jx)jx=r;}
const h=_F(p[234],p[454]),f=_F(p[10],p[152]),g=jx,b=f>0?g/f:0;
const v=(p[133].x+p[362].x)/2,w=(p[10].y+p[152].y)/2;
let _=0;for(const pr of _WP){const rr=p[pr[0]],q=p[pr[1]];_+=(h>0?Math.abs(Math.abs(rr.x-v)-Math.abs(q.x-v))/h:0)+(f>0?Math.abs(Math.abs(rr.y-w)-Math.abs(q.y-w))/f:0);}
const y=Math.round((1-_O(_/(2*_WP.length)/.09,0,1))*100);
const k=(p[133].y+p[33].y)/2,j=(p[362].y+p[263].y)/2,Nl=(k+j)/2;
const C=Math.abs(p[0].y-Nl),S=f>0?C/f:0,E=g>0?_F(p[234],p[454])/g:0;
const R=_F(p[33],p[133]),Ih=_F(p[159],p[145]),Mw=_F(p[263],p[362]),Uh=_F(p[386],p[374]);
const z=Math.max(.2,((R>0?Ih/R:0)+(Mw>0?Uh/Mw:0))/2);
const H=h>0?_F(p[133],p[362])/h:0;
const G=_B(m,2,6.5,-2,11),Y=_B(b,.58,.78,.42,.96),q2=_B(S,.27,.34,.2,.43);
const V=_B(E,1.04,1.24,.86,1.48),K=_B(z,.22,.3,.18,.43);
const J=Math.round((.18*Y+.24*q2+.18*V+.16*K+.24*_B(H,.22,.31,.15,.42))*10)/10;
return _O(.12*G+.14*Y+y/10*.24+.14*q2+.1*V+.08*K+.18*J,1.1,10);
}
const _TBL=[];for(let L=0;L<=1.0001;L+=0.02)_TBL.push([L,simpleScore(faceL(L))]);
const SCORE_MIN=_TBL[0][1],SCORE_MAX=_TBL[_TBL.length-1][1];
function solveL(score){
score=Math.max(SCORE_MIN,Math.min(SCORE_MAX,score));
for(let i=0;i<_TBL.length-1;i++){const a=_TBL[i],b=_TBL[i+1];if(score>=a[1]&&score<=b[1]){const f=(score-a[1])/((b[1]-a[1])||1);return a[0]+(b[0]-a[0])*f;}}
return 1;
}
function getAspect(){try{const v=document.querySelector('video');if(v&&v.videoWidth&&v.videoHeight)return v.videoWidth/v.videoHeight;}catch(e){}return 4/3;}
const SCORE_IDX=new Set([0,1,10,152,234,454,33,133,263,362,159,145,386,374,172,397,150,379,171,396]);
for(const pr of _WP){SCORE_IDX.add(pr[0]);SCORE_IDX.add(pr[1]);}
// Pre-allocated reusable buffer — avoids creating ~956 objects on every crypto.sign call.
const _idealBuf=Array.from({length:478},()=>({x:0,y:0,z:0}));
let _idealLastScore=NaN,_idealLastAspect=NaN;
function idealObjects(aspect){
const a=aspect||getAspect();
// Only recompute when the score or aspect actually changed (sign fires many times/sec)
if(currentScore===_idealLastScore&&a===_idealLastAspect)return _idealBuf;
_idealLastScore=currentScore;_idealLastAspect=a;
const cx=0.5,L=solveL(currentScore),base=faceL(L);
for(let i=0;i<base.length;i++){_idealBuf[i].x=cx+(base[i].x-cx)/a;_idealBuf[i].y=base[i].y;_idealBuf[i].z=0;}
return _idealBuf;
}
// ── Global match-state detection ──
// Live Spoof only injects while an actual match is in progress. The most reliable signal:
// omoggle only signs landmark frames (header byte 4) DURING a match. We timestamp each such
// frame; IN_MATCH is true while those frames are still arriving (within the last 1.5s).
let IN_MATCH = false;
let _lastFrameTs = 0;
setInterval(()=>{ IN_MATCH = (Date.now() - _lastFrameTs) < 1500; }, 300);
(function setup_sign_rewrite(){
if(!W.crypto?.subtle?.sign)return;
const _sign=W.crypto.subtle.sign;
W.crypto.subtle.sign=function(alg,key,data){
try{
let dv;
if(data instanceof ArrayBuffer)dv=new DataView(data);
else if(ArrayBuffer.isView(data))dv=new DataView(data.buffer,data.byteOffset,data.byteLength);
// A landmark frame (header byte 4) means we're actively in a match — timestamp it
// regardless of whether we spoof, so IN_MATCH tracks correctly.
if(dv&&dv.getUint8(0)===4&&dv.byteLength>=17){
_lastFrameTs=Date.now();
if(CFG.enabled&&CFG.liveSpoof&&!CFG.realisticScoring){
const s=dv.getUint16(13,true);
const aspect=dv.getFloat32(9,true)||getAspect();
const ideal=idealObjects(aspect);
const n=Math.min(s,ideal.length);
dv.setUint8(15,255);dv.setUint8(16,2);
let c=17;
for(let i=0;i<n;i++){dv.setFloat32(c,ideal[i].x,true);dv.setFloat32(c+4,ideal[i].y,true);c+=8;}
framesSent++;
}
}
}catch(e){}
return _sign.call(this,alg,key,data);
};
})();
const AFK={
mediaEl:null,mediaType:null,remoteVideo:null,canvas:null,ctx:null,
drawTimer:null,realSenders:[],afkTracks:[],W:640,H:480,
attachRemote(track){try{
if(!this.remoteVideo){const v=document.createElement('video');v.muted=true;v.playsInline=true;v.autoplay=true;v.style.cssText='position:fixed;left:-9999px;width:2px;height:2px;';document.body.appendChild(v);this.remoteVideo=v;}
// Release the PREVIOUS incoming stream/tracks before attaching the new one,
// otherwise each match leaves a dead decoded-video pipeline in memory.
const prev=this.remoteVideo.srcObject;
if(prev&&prev.getTracks){try{prev.getTracks().forEach(t=>{if(t!==track)t.stop();});}catch(e){}}
this.remoteVideo.srcObject=new MediaStream([track]);
this.remoteVideo.play().catch(()=>{});
}catch(e){}},
// Fully release the hidden opponent-mirror video element + its tracks
releaseRemote(){try{
if(this.remoteVideo){
const s=this.remoteVideo.srcObject;
if(s&&s.getTracks)s.getTracks().forEach(t=>{try{t.stop();}catch(e){}});
this.remoteVideo.srcObject=null;
this.remoteVideo.pause();
}
}catch(e){}},
draw(){const ctx=this.ctx;if(!ctx)return;let src,isVideo;if(CFG.mirrorOpponent&&this.remoteVideo&&this.remoteVideo.readyState>=2){src=this.remoteVideo;isVideo=true;}else{src=this.mediaEl;isVideo=this.mediaType==='video';}if(!src){ctx.fillStyle='#000';ctx.fillRect(0,0,this.W,this.H);return;}if(isVideo&&src===this.mediaEl){if(src.paused&&!src.ended)src.play().catch(()=>{});if(src.ended&&CFG.loopVideo){src.currentTime=0;src.play().catch(()=>{});}}const ready=isVideo?src.readyState>=2:(src.complete&&src.naturalWidth>0);if(!ready){ctx.fillStyle='#000';ctx.fillRect(0,0,this.W,this.H);return;}const sw=src.videoWidth||src.naturalWidth||this.W,sh=src.videoHeight||src.naturalHeight||this.H;ctx.save();if(CFG.invertCam){ctx.translate(this.W,0);ctx.scale(-1,1);}const scale=CFG.zoomFit?Math.max(this.W/sw,this.H/sh):Math.min(this.W/sw,this.H/sh);const dw=sw*scale,dh=sh*scale,dx=(this.W-dw)/2,dy=(this.H-dh)/2;ctx.fillStyle='#000';ctx.fillRect(0,0,this.W,this.H);try{ctx.drawImage(src,dx,dy,dw,dh);}catch(e){}ctx.restore();},
initCanvas(){if(this.canvas)return;this.canvas=document.createElement('canvas');this.canvas.width=this.W;this.canvas.height=this.H;this.ctx=this.canvas.getContext('2d',{alpha:false});this.ctx.fillStyle='#000';this.ctx.fillRect(0,0,this.W,this.H);this.drawTimer=setInterval(()=>{
if(this.mediaEl||this.remoteVideo)this.draw();
},1000/30);},
_captureStream:null,
freshVideoTrack(){
this.initCanvas();
// Reuse ONE capture stream for the canvas instead of making a new one each call.
// Each captureStream() allocates a new pipeline that never gets freed otherwise.
if(!this._captureStream||!this._captureStream.active){
this._captureStream=this.canvas.captureStream(30);
}
let t=this._captureStream.getVideoTracks()[0];
// If the site stopped our track, the stream is dead — rebuild it once.
if(!t||t.readyState!=='live'){
try{this._captureStream.getTracks().forEach(tr=>tr.stop());}catch(e){}
this._captureStream=this.canvas.captureStream(30);
t=this._captureStream.getVideoTracks()[0]||null;
}
// Track the single live track (no unbounded array growth)
this.afkTracks=t?[t]:[];
return t;
},
loadFile(file){const kind=CFG.afkSource==='Picture'?'image':'video';FileStore.save(file,{kind,name:file.name});this.loadBlob(file,kind);},
loadBlob(blob,kind){const url=URL.createObjectURL(blob);this._lastBlobUrl&&URL.revokeObjectURL(this._lastBlobUrl);this._lastBlobUrl=url;this.initCanvas();if(this.mediaEl){try{if(this.mediaType==='video'){this.mediaEl.pause();this.mediaEl.remove();}}catch(e){}this.mediaEl=null;this.mediaType=null;}if(kind==='image'){const img=new Image();img.style.cssText='position:fixed;left:-9999px;width:2px;height:2px;opacity:0;pointer-events:none;';document.body.appendChild(img);img.onload=()=>{
const nw=img.naturalWidth||640,nh=img.naturalHeight||480;
if(this.W!==nw||this.H!==nh){this.W=nw;this.H=nh;this.canvas.width=nw;this.canvas.height=nh;}
this.mediaType='image';this.mediaEl=img;
try{this.ctx.drawImage(img,0,0,this.W,this.H);}catch(e){}
};img.onerror=()=>{this.mediaEl=null;img.remove();};img.src=url;}else{const vid=document.createElement('video');vid.src=url;vid.muted=true;vid.playsInline=true;vid.autoplay=true;vid.loop=CFG.loopVideo;vid.setAttribute('playsinline','');vid.style.cssText='position:fixed;left:-9999px;width:2px;height:2px;';document.body.appendChild(vid);vid.addEventListener('loadeddata',()=>{
const nw=vid.videoWidth||640,nh=vid.videoHeight||480;
if(this.W!==nw||this.H!==nh){this.W=nw;this.H=nh;this.canvas.width=nw;this.canvas.height=nh;}
this.mediaType='video';this.mediaEl=vid;vid.play().catch(()=>{});
});vid.addEventListener('ended',()=>{if(CFG.loopVideo){vid.currentTime=0;vid.play().catch(()=>{});}});vid.load();}},
hasMedia(){return!!this.mediaEl||(CFG.mirrorOpponent&&!!this.remoteVideo);},
swapSendersToAfk(){if(!this.hasMedia())return;for(const pc of this.peerConns){try{for(const sender of pc.getSenders()){if(sender.track&&sender.track.kind==='video'){if(!this.realSenders.find(r=>r.sender===sender))this.realSenders.push({sender,realTrack:sender.track});const fresh=this.freshVideoTrack();if(fresh)sender.replaceTrack(fresh).catch(()=>{});}}}catch(e){}}},
restoreSenders(){
for(const{sender,realTrack}of this.realSenders){
try{if(realTrack&&realTrack.readyState==='live')sender.replaceTrack(realTrack).catch(()=>{});}catch(e){}
}
this.realSenders=[];
// Stop and discard all AFK tracks we handed out
this.afkTracks.forEach(t=>{try{t.stop();}catch(e){}});
this.afkTracks=[];
if(this._captureStream){try{this._captureStream.getTracks().forEach(t=>t.stop());}catch(e){}this._captureStream=null;}
},
peerConns:[],
pruneConns(){
// Drop references to dead peer connections so their senders/receivers/tracks can be GC'd
const before=this.peerConns.length;
this.peerConns=this.peerConns.filter(pc=>{
const dead=pc.connectionState==='closed'||pc.connectionState==='failed'||pc.iceConnectionState==='closed';
return !dead;
});
// If every connection is gone, the opponent feed is over — release the mirror video too
if(this.peerConns.length===0&&before>0)this.releaseRemote();
},
};
if(navigator.mediaDevices&&navigator.mediaDevices.getUserMedia){const _gum=navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);navigator.mediaDevices.getUserMedia=async function(constraints){const wantsVideo=!!(constraints&&constraints.video),wantsAudio=!!(constraints&&constraints.audio);if(!CFG.afkCam||!AFK.hasMedia()||!wantsVideo)return _gum(constraints);const composed=new MediaStream(),vt=AFK.freshVideoTrack();if(vt)composed.addTrack(vt);if(wantsAudio){try{const real=await _gum({audio:true});real.getAudioTracks().forEach(t=>composed.addTrack(t));real.getVideoTracks().forEach(t=>t.stop());}catch(e){}}return composed;};}
if(W.RTCPeerConnection){const _PC=W.RTCPeerConnection;const wrapped=function(...args){
const pc=new _PC(...args);
AFK.peerConns.push(pc);
pc.addEventListener('track',(ev)=>{try{if(ev.track&&ev.track.kind==='video')AFK.attachRemote(ev.track);}catch(e){}});
// Prune as soon as THIS connection dies (don't wait for the timer)
pc.addEventListener('connectionstatechange',()=>{
if(pc.connectionState==='closed'||pc.connectionState==='failed'||pc.connectionState==='disconnected'){
AFK.peerConns=AFK.peerConns.filter(c=>c!==pc);
if(AFK.peerConns.length===0)AFK.releaseRemote();
}
});
return pc;
};wrapped.prototype=_PC.prototype;try{W.RTCPeerConnection=wrapped;}catch(e){}try{W.webkitRTCPeerConnection=wrapped;}catch(e){}}
setInterval(()=>AFK.pruneConns(),3000); // always-on cleanup of dead peer connections
setInterval(()=>{if(!CFG.afkCam||!CFG.enabled||!AFK.hasMedia())return;for(const pc of AFK.peerConns){try{for(const sender of pc.getSenders()){const tr=sender.track;if(tr&&tr.kind==='video'&&!AFK.afkTracks.includes(tr)){if(!AFK.realSenders.find(r=>r.sender===sender))AFK.realSenders.push({sender,realTrack:tr});const fresh=AFK.freshVideoTrack();if(fresh)sender.replaceTrack(fresh).catch(()=>{});}}}catch(e){}}},1500);
FileStore.load().then(saved=>{if(saved&&saved.blob&&!AFK.hasMedia()){const kind=saved.meta?.kind||'video';CFG.afkSource=kind==='image'?'Picture':'Video';AFK.loadBlob(saved.blob,kind);}}).catch(()=>{});
let arenaSeenAt=0,autoStartLast=0;
function tryAutoStart(){if(!CFG.autoStart||!CFG.enabled){arenaSeenAt=0;return;}const h=[...document.querySelectorAll('h3')].find(x=>x.textContent.trim().toLowerCase()==='arena');if(!h){arenaSeenAt=0;return;}const r=h.getBoundingClientRect();if(r.width===0||r.height===0){arenaSeenAt=0;return;}const now=Date.now();if(arenaSeenAt===0){arenaSeenAt=now;return;}const delay=Math.max(1,CFG.autofarmDelay||1);if(now-arenaSeenAt<delay)return;if(now-autoStartLast<delay)return;const el=document.elementFromPoint(r.left+r.width/2,r.top+100);if(!el)return;autoStartLast=now;arenaSeenAt=0;el.click();}
setInterval(tryAutoStart,500);
let searchingSince=0;
setInterval(()=>{if(!CFG.autoStart||!CFG.enabled){searchingSince=0;return;}const now=Date.now(),text=(document.body?.innerText||'').toLowerCase();const searching=/awaiting challenger|connecting opponent|searching|finding|in queue|looking for/.test(text);const inMatchOrResult=/find new match|rematch|edged|victory|you win|you lose|overall score/.test(text);if(inMatchOrResult||!searching){searchingSince=0;return;}if(searchingSince===0){searchingSince=now;return;}if(now-searchingSince>=30000){searchingSince=0;const ABORT=['cancel','cancel search','stop','stop searching','return to menu','back','leave','leave queue'];const all=Array.from(document.querySelectorAll('button, a, [role="button"]'));let abortBtn=null;for(const label of ABORT){abortBtn=all.find(el=>(el.textContent||'').trim().toLowerCase()===label);if(abortBtn)break;}if(abortBtn){const br=abortBtn.getBoundingClientRect();(document.elementFromPoint(br.left+br.width/2,br.top+br.height/2)||abortBtn).click();}else document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',code:'Escape',keyCode:27,bubbles:true}));}},1000);
let buttonSeenAt=0,lastClickAt=0;
setInterval(()=>{if(!CFG.autoRequeue||!CFG.enabled){buttonSeenAt=0;return;}const TARGETS=['find new match','find new','rematch & chat','rematch'];const all=Array.from(document.querySelectorAll('button, a, [role="button"]'));let best=null;for(const label of TARGETS){best=all.find(el=>{const t=(el.textContent||'').trim().toLowerCase();if(t!==label)return false;const r=el.getBoundingClientRect();return r.width>10&&r.height>10&&!el.disabled&&el.getAttribute('aria-disabled')!=='true';});if(best)break;}if(!best){buttonSeenAt=0;return;}const now=Date.now();if(buttonSeenAt===0){buttonSeenAt=now;return;}const delay=Math.max(1,CFG.autofarmDelay||1);if(now-buttonSeenAt<delay)return;if(now-lastClickAt<delay)return;const r=best.getBoundingClientRect();(document.elementFromPoint(r.left+r.width/2,r.top+r.height/2)||best).click();lastClickAt=now;buttonSeenAt=0;},500);
let scoreVel=0;
setInterval(()=>{if(!CFG.enabled||!CFG.liveSpoof||CFG.realisticScoring)return;if(CFG.liveMode==='fixed'){currentScore=CFG.liveMin;scoreVel=0;return;}const lo=Math.min(CFG.liveMin,CFG.liveMax),hi=Math.max(CFG.liveMin,CFG.liveMax);if(lo===hi){currentScore=lo;scoreVel=0;return;}scoreVel+=(Math.random()-0.5)*(CFG.scoreDrift||0.05);scoreVel*=0.82;scoreVel=Math.max(-0.12,Math.min(0.12,scoreVel));currentScore+=scoreVel;if(currentScore<lo){currentScore=lo;scoreVel=Math.abs(scoreVel)*0.5;}if(currentScore>hi){currentScore=hi;scoreVel=-Math.abs(scoreVel)*0.5;}},200);
function applyTheme(t){
// Animated themes resolve to a base palette + a CSS animation class on #ac-root
let animClass='';
if(ANIM_THEMES[t]){animClass=ANIM_THEMES[t].anim;t=ANIM_THEMES[t].base;}
const rootEl=document.getElementById('ac-root');
if(rootEl){
['ac-anim-rainbow','ac-anim-aurora','ac-anim-sunset','ac-anim-matrix'].forEach(c=>rootEl.classList.remove(c));
if(animClass)rootEl.classList.add(animClass);
}
const T=THEMES[t]||THEMES.red;
const r=document.documentElement;
r.style.setProperty('--ac-bg',T.bg);r.style.setProperty('--ac-surf',T.surf);r.style.setProperty('--ac-surf2',T.surf2);
r.style.setProperty('--ac-border',T.border);r.style.setProperty('--ac-border2',T.border2);
r.style.setProperty('--ac-red',T.accent);r.style.setProperty('--ac-red2',T.accent2);r.style.setProperty('--ac-red3',T.accent3);
r.style.setProperty('--ac-red-dim',T.accentDim);r.style.setProperty('--ac-red-glow',T.accentGlow);
r.style.setProperty('--ac-green',T.green);r.style.setProperty('--ac-green2',T.green2);
r.style.setProperty('--ac-muted',T.muted);r.style.setProperty('--ac-muted2',T.muted2);
r.style.setProperty('--ac-text',T.text);r.style.setProperty('--ac-text2',T.text2);
r.style.setProperty('--ac-shadow','0 24px 60px rgba(0,0,0,0.8),0 0 0 1px '+T.border2+',inset 0 1px 0 rgba(255,255,255,0.04)');
}
function buildUI(){
if(document.getElementById('ac-root'))return;
const style=document.createElement('style');
style.id='ac-style';
style.textContent=`
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
:root{
--ac-bg:#0a0709;--ac-surf:#110d0f;--ac-surf2:#181215;
--ac-border:rgba(180,30,50,0.18);--ac-border2:rgba(180,30,50,0.32);
--ac-red:#c0192c;--ac-red2:#e02240;--ac-red3:#ff3355;
--ac-red-dim:rgba(192,25,44,0.12);--ac-red-glow:rgba(224,34,64,0.22);
--ac-green:#22a84a;--ac-green2:#2fd65e;
--ac-muted:#5a4a50;--ac-muted2:#8a7078;--ac-text:#e8dde0;--ac-text2:#c0aab0;
--ac-mono:'JetBrains Mono',monospace;--ac-font:'Outfit',system-ui,sans-serif;
--ac-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px rgba(180,30,50,0.15),inset 0 1px 0 rgba(255,255,255,0.04);
}
#ac-root{position:fixed;bottom:22px;right:22px;z-index:2147483647;width:310px;max-height:calc(100vh - 44px);display:flex;flex-direction:column;background:var(--ac-bg);border:1px solid var(--ac-border2);border-radius:20px;box-shadow:var(--ac-shadow);font-family:var(--ac-font);color:var(--ac-text);overflow:hidden;transition:transform 0.45s cubic-bezier(0.34,1.4,0.64,1),opacity 0.3s ease,box-shadow 0.3s ease;user-select:none;animation:ac-in 0.55s cubic-bezier(0.34,1.4,0.64,1) both;}
#ac-root.ac-hidden{transform:translateY(130%)!important;opacity:0!important;pointer-events:none!important;}
#ac-root.ac-panic{display:none!important;}
body.ac-panic-active .ac-ripple{display:none!important;}
#ac-root:hover{box-shadow:0 28px 70px rgba(0,0,0,0.85),0 0 0 1px var(--ac-red2),inset 0 1px 0 rgba(255,255,255,0.05);}
@keyframes ac-in{from{transform:translateY(60px);opacity:0;}to{transform:translateY(0);opacity:1;}}
/* ── MINIMIZED STATE ── */
#ac-root.ac-minimized{width:auto!important;min-width:0;border-radius:50px;}
#ac-root.ac-minimized>*:not(#ac-header){display:none!important;}
#ac-root.ac-minimized #ac-header{border-bottom:none;padding:10px 14px;}
#ac-root.ac-minimized #ac-logo-row{gap:7px;}
#ac-root.ac-minimized #ac-title{font-size:13px;}
#ac-root.ac-minimized #ac-badge{display:none;}
#ac-root.ac-minimized #ac-minimize-btn{transform:rotate(180deg);}
#ac-root.ac-minimized #ac-master{display:none;}
#ac-root.ac-minimized #ac-tabs{display:none;}
#ac-header{flex-shrink:0;display:flex;align-items:center;justify-content:space-between;padding:13px 16px 11px;cursor:move;background:linear-gradient(135deg,var(--ac-red-dim) 0%,transparent 65%);border-bottom:1px solid var(--ac-border);position:relative;overflow:hidden;}
#ac-header::before{content:'';position:absolute;top:-20px;left:-20px;width:80px;height:80px;background:radial-gradient(circle,var(--ac-red-dim) 0%,transparent 70%);border-radius:50%;pointer-events:none;}
#ac-logo-row{position:relative;z-index:2;display:flex;align-items:center;gap:9px;}
#ac-pulse{width:7px;height:7px;border-radius:50%;background:var(--ac-green2);box-shadow:0 0 8px var(--ac-green2);animation:ac-pulse 2.2s ease-in-out infinite;flex-shrink:0;}
@keyframes ac-pulse{0%,100%{box-shadow:0 0 5px var(--ac-green2);opacity:1;}50%{box-shadow:0 0 14px var(--ac-green2),0 0 4px var(--ac-green);opacity:0.75;}}
#ac-title{font-size:15px;font-weight:800;color:var(--ac-text);letter-spacing:-0.2px;}
#ac-title span{color:var(--ac-red3);}
#ac-ver{font-family:var(--ac-mono);font-size:9px;color:var(--ac-muted2);margin-left:1px;}
#ac-header-right{position:relative;z-index:2;display:flex;align-items:center;gap:6px;}
#ac-badge{display:flex;align-items:center;gap:4px;padding:4px 9px;border-radius:20px;font-size:10px;font-weight:700;letter-spacing:0.6px;transition:all 0.3s;font-family:var(--ac-mono);}
#ac-badge.on{background:rgba(34,168,74,0.14);border:1px solid rgba(34,168,74,0.35);color:var(--ac-green2);}
#ac-badge.off{background:rgba(192,25,44,0.14);border:1px solid rgba(192,25,44,0.35);color:var(--ac-red3);}
#ac-minimize-btn{background:none;border:none;cursor:pointer;color:var(--ac-muted2);font-size:14px;padding:2px 4px;border-radius:4px;transition:all 0.2s;line-height:1;display:flex;align-items:center;justify-content:center;}
#ac-minimize-btn:hover{color:var(--ac-text);background:rgba(255,255,255,0.08);}
#ac-master{position:relative;z-index:2;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;padding:11px 16px;border-bottom:1px solid var(--ac-border);}
.ac-master-lbl{font-size:14px;font-weight:700;color:var(--ac-text);letter-spacing:-0.1px;}
.ac-tog{position:relative;width:40px;height:22px;cursor:pointer;flex-shrink:0;}
.ac-tog input{position:absolute;opacity:0;width:0;height:0;}
.ac-trk{position:absolute;inset:0;border-radius:11px;background:rgba(255,255,255,0.07);border:1px solid rgba(255,255,255,0.1);transition:background 0.25s,border-color 0.25s,box-shadow 0.25s;}
.ac-tog input:checked+.ac-trk{background:var(--ac-red2);border-color:var(--ac-red2);box-shadow:0 0 10px var(--ac-red-glow);}
.ac-thb{position:absolute;top:3px;left:3px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 4px rgba(0,0,0,0.5);transition:left 0.25s cubic-bezier(0.34,1.4,0.64,1);}
.ac-tog input:checked~.ac-thb{left:21px;}
.ac-tog-lg{width:48px;height:26px;}.ac-tog-lg .ac-trk{border-radius:13px;}.ac-tog-lg .ac-thb{width:20px;height:20px;}.ac-tog-lg input:checked~.ac-thb{left:24px;}
#ac-tabs{position:relative;z-index:10;flex-shrink:0;display:flex;padding:8px 10px 0;gap:2px;border-bottom:1px solid var(--ac-border);background:var(--ac-surf);pointer-events:auto;}
.ac-tab{position:relative;z-index:11;pointer-events:auto!important;flex:1;padding:7px 2px;background:transparent;border:none;color:var(--ac-muted2);font-family:var(--ac-font);font-size:12px;font-weight:600;cursor:pointer;border-radius:7px 7px 0 0;border-bottom:2px solid transparent;transition:color 0.2s,background 0.2s,border-color 0.2s;letter-spacing:0.1px;}
.ac-tab:hover{color:var(--ac-text2);background:rgba(255,255,255,0.03);}
.ac-tab.on{color:var(--ac-text);border-bottom-color:var(--ac-red2);background:var(--ac-red-dim);}
.ac-panel{position:relative;z-index:2;display:none;flex:1 1 auto;min-height:0;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--ac-red2) transparent;padding-bottom:4px;animation:ac-panel-in 0.2s ease;}
.ac-panel::-webkit-scrollbar{width:3px;}.ac-panel::-webkit-scrollbar-thumb{background:var(--ac-red2);border-radius:2px;}
.ac-panel.on{display:block;}
@keyframes ac-panel-in{from{opacity:0;transform:translateY(5px);}to{opacity:1;transform:translateY(0);}}
.ac-row{display:flex;align-items:center;justify-content:space-between;padding:9px 16px;transition:background 0.15s;}
.ac-row:hover{background:rgba(255,255,255,0.02);}
.ac-lbl{font-size:13px;font-weight:500;color:var(--ac-text);}
.ac-sub{font-size:10px;color:var(--ac-muted2);font-family:var(--ac-mono);margin-top:2px;}
.ac-sect{display:flex;align-items:center;justify-content:space-between;padding:7px 16px 3px;margin-top:2px;}
.ac-sect-lbl{font-size:9px;font-weight:700;color:var(--ac-muted);letter-spacing:1.2px;text-transform:uppercase;}
.ac-sect-arr{font-size:9px;color:var(--ac-muted);}
.ac-input{background:var(--ac-surf2);color:var(--ac-text);border:1px solid var(--ac-border);border-radius:8px;padding:6px 9px;font-family:var(--ac-mono);font-size:12px;font-weight:500;outline:none;text-align:center;transition:border-color 0.2s,box-shadow 0.2s;width:64px;}
.ac-input:focus{border-color:var(--ac-red2);box-shadow:0 0 0 3px var(--ac-red-dim);}
.ac-input-sm{width:50px;}.ac-input-md{width:80px;}.ac-input-full{width:100%;box-sizing:border-box;}.ac-input-text{text-align:left;font-family:var(--ac-font);font-size:12px;}
.ac-keybadge{background:var(--ac-surf2);border:1px solid var(--ac-border2);border-radius:5px;padding:3px 10px;font-family:var(--ac-mono);font-size:11px;font-weight:600;color:var(--ac-text2);}
.ac-select{background:var(--ac-surf2);color:var(--ac-text);border:1px solid var(--ac-border);border-radius:8px;padding:6px 22px 6px 9px;font-family:var(--ac-font);font-size:12px;font-weight:500;cursor:pointer;outline:none;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='5'%3E%3Cpath d='M0 0l4.5 5L9 0z' fill='%238a7078'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 7px center;transition:border-color 0.2s;}
.ac-select:focus,.ac-select:hover{border-color:var(--ac-red2);}
.ac-incr{display:flex;align-items:center;gap:6px;flex:1;}
.ac-incr-inp{flex:1;background:var(--ac-surf2);color:var(--ac-text);border:1px solid var(--ac-border);border-radius:8px;padding:6px 9px;font-family:var(--ac-mono);font-size:12px;font-weight:500;outline:none;transition:border-color 0.2s;}
.ac-incr-inp:focus{border-color:var(--ac-red2);}
.ac-incr-btns{display:flex;flex-direction:column;gap:2px;}
.ac-incr-btn{width:20px;height:15px;border-radius:4px;border:1px solid;font-size:10px;font-weight:800;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.15s;line-height:1;font-family:var(--ac-mono);}
.ac-incr-btn.p{background:rgba(34,168,74,0.13);border-color:rgba(34,168,74,0.38);color:var(--ac-green2);}.ac-incr-btn.p:hover{background:rgba(34,168,74,0.28);}
.ac-incr-btn.m{background:var(--ac-red-dim);border-color:var(--ac-border2);color:var(--ac-red3);}.ac-incr-btn.m:hover{background:var(--ac-red2);color:#fff;}
.ac-notice{margin:6px 12px 2px;padding:8px 11px;border-radius:9px;font-size:11px;line-height:1.45;font-weight:500;}
.ac-notice-warn{background:var(--ac-red-dim);border:1px solid var(--ac-border2);color:var(--ac-red3);}
.ac-notice strong{font-weight:700;}
.ac-btn{display:flex;align-items:center;justify-content:center;gap:5px;width:calc(100% - 24px);margin:4px 12px;padding:9px 14px;border-radius:9px;border:1px solid;font-family:var(--ac-font);font-size:12px;font-weight:700;cursor:pointer;transition:all 0.2s;letter-spacing:0.2px;}
.ac-btn-default{background:var(--ac-red-dim);border-color:var(--ac-border2);color:var(--ac-red3);}.ac-btn-default:hover{background:var(--ac-red2);border-color:var(--ac-red2);color:#fff;}
.ac-btn-red{background:var(--ac-red-dim);border-color:var(--ac-border2);color:var(--ac-red3);}.ac-btn-red:hover{background:var(--ac-red2);border-color:var(--ac-red2);color:#fff;}
.ac-btn-green{background:rgba(34,168,74,0.1);border-color:rgba(34,168,74,0.3);color:var(--ac-green2);}.ac-btn-green:hover{background:rgba(34,168,74,0.22);}
.ac-upload-row{display:flex;align-items:center;gap:8px;padding:6px 16px;}
.ac-upload-btn{flex:1;padding:7px 10px;border-radius:8px;background:rgba(192,25,44,0.1);border:1px solid rgba(192,25,44,0.28);color:var(--ac-red3);font-family:var(--ac-font);font-size:12px;font-weight:600;cursor:pointer;transition:all 0.2s;}
.ac-upload-btn:hover{background:rgba(192,25,44,0.2);}
.ac-prow{display:flex;gap:5px;align-items:center;padding:4px 12px;}
.ac-prow .ac-input-full{flex:1;text-align:left;width:auto;font-size:12px;}
#ac-footer{position:relative;z-index:2;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;padding:7px 16px 11px;border-top:1px solid var(--ac-border);margin-top:4px;}
#ac-footer-l{font-size:11px;color:var(--ac-muted);}
#ac-footer-r{font-size:11px;color:var(--ac-muted2);}
#ac-footer-r span{color:var(--ac-red3);font-weight:600;}
#ac-root.ac-drag{transition:none!important;opacity:0.88;}
#ac-live-readout{display:flex;align-items:center;justify-content:space-between;margin:2px 12px 8px;padding:10px 14px;background:linear-gradient(135deg,var(--ac-red-dim),transparent);border:1px solid var(--ac-border2);border-radius:10px;}
.ac-live-label{font-family:var(--ac-mono);font-size:9px;letter-spacing:1.5px;color:var(--ac-muted2);font-weight:600;}
.ac-live-val{font-family:var(--ac-mono);font-size:22px;font-weight:700;color:var(--ac-red3);line-height:1;text-shadow:0 0 12px var(--ac-red-glow);transition:color 0.15s,transform 0.15s;min-width:54px;text-align:right;}
.ac-live-val.ac-pulse{transform:scale(1.06);}.ac-live-val.ac-off{color:var(--ac-muted2);text-shadow:none;}
/* ── THEME SWATCHES ── */
.ac-theme-row{display:flex;align-items:center;justify-content:space-between;padding:8px 16px;}
.ac-swatches{display:flex;gap:6px;}
.ac-swatch{width:20px;height:20px;border-radius:50%;cursor:pointer;border:2px solid transparent;transition:all 0.2s;position:relative;}
.ac-swatch:hover{transform:scale(1.15);}
.ac-swatch.active{border-color:#fff;box-shadow:0 0 8px rgba(255,255,255,0.3);}
.ac-swatch[data-t="red"]{background:#c0192c;}
.ac-swatch[data-t="blue"]{background:#1964c0;}
.ac-swatch[data-t="green"]{background:#19a050;}
.ac-swatch[data-t="purple"]{background:#7819c0;}
.ac-swatch[data-t="gold"]{background:#b08800;}
.ac-swatch[data-t="cyan"]{background:#0098b0;}
.ac-swatch[data-t="pink"]{background:#c01880;}
.ac-swatch[data-t="mono"]{background:#888890;}
.ac-swatch.anim{position:relative;overflow:hidden;}
.ac-swatch[data-t="rainbow"]{background:linear-gradient(90deg,#ff3355,#ffd700,#33ff99,#3399ff,#cc33ff,#ff3355);background-size:300% 100%;animation:ac-rainbow-sw 3s linear infinite;}
.ac-swatch[data-t="aurora"]{background:linear-gradient(120deg,#22e5ff,#33ff99,#3399ff);background-size:200% 200%;animation:ac-aurora-sw 4s ease infinite;}
.ac-swatch[data-t="sunset"]{background:linear-gradient(120deg,#ff44cc,#ffd700,#ff3355);background-size:200% 200%;animation:ac-aurora-sw 4s ease infinite;}
.ac-swatch[data-t="matrix"]{background:linear-gradient(180deg,#0a0,#3f9,#0a0);background-size:100% 200%;animation:ac-matrix-sw 2s linear infinite;}
@keyframes ac-rainbow-sw{0%{background-position:0% 50%;}100%{background-position:300% 50%;}}
@keyframes ac-aurora-sw{0%,100%{background-position:0% 50%;}50%{background-position:100% 50%;}}
@keyframes ac-matrix-sw{0%{background-position:0% 0%;}100%{background-position:0% 200%;}}
/* ── ANIMATED THEME EFFECTS on the whole widget ── */
#ac-root.ac-anim-rainbow{animation:ac-in 0.55s cubic-bezier(0.34,1.4,0.64,1) both, ac-rainbow-border 6s linear infinite;}
@keyframes ac-rainbow-border{0%{border-color:#ff3355;box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px #ff3355,0 0 18px rgba(255,51,85,0.4);}20%{border-color:#ffd700;box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px #ffd700,0 0 18px rgba(255,215,0,0.4);}40%{border-color:#33ff99;box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px #33ff99,0 0 18px rgba(51,255,153,0.4);}60%{border-color:#3399ff;box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px #3399ff,0 0 18px rgba(51,153,255,0.4);}80%{border-color:#cc33ff;box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px #cc33ff,0 0 18px rgba(204,51,255,0.4);}100%{border-color:#ff3355;box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px #ff3355,0 0 18px rgba(255,51,85,0.4);}}
#ac-root.ac-anim-rainbow #ac-header{background:linear-gradient(135deg,rgba(255,51,85,0.12),rgba(204,51,255,0.06) 50%,transparent 80%);background-size:200% 200%;animation:ac-aurora-sw 6s ease infinite;}
#ac-root.ac-anim-aurora{animation:ac-in 0.55s cubic-bezier(0.34,1.4,0.64,1) both, ac-aurora-glow 5s ease-in-out infinite;}
@keyframes ac-aurora-glow{0%,100%{box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px rgba(34,229,255,0.5),0 0 22px rgba(34,229,255,0.3);}50%{box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px rgba(51,255,153,0.5),0 0 22px rgba(51,255,153,0.3);}}
#ac-root.ac-anim-aurora #ac-header{background:linear-gradient(120deg,rgba(34,229,255,0.12),rgba(51,255,153,0.06) 50%,transparent 80%);background-size:200% 200%;animation:ac-aurora-sw 5s ease infinite;}
#ac-root.ac-anim-sunset{animation:ac-in 0.55s cubic-bezier(0.34,1.4,0.64,1) both, ac-sunset-glow 5s ease-in-out infinite;}
@keyframes ac-sunset-glow{0%,100%{box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px rgba(255,68,204,0.5),0 0 22px rgba(255,68,204,0.3);}50%{box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px rgba(255,215,0,0.5),0 0 22px rgba(255,215,0,0.3);}}
#ac-root.ac-anim-sunset #ac-header{background:linear-gradient(120deg,rgba(255,68,204,0.12),rgba(255,215,0,0.06) 50%,transparent 80%);background-size:200% 200%;animation:ac-aurora-sw 5s ease infinite;}
#ac-root.ac-anim-matrix{animation:ac-in 0.55s cubic-bezier(0.34,1.4,0.64,1) both, ac-matrix-glow 3s linear infinite;}
@keyframes ac-matrix-glow{0%,100%{box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px rgba(51,255,153,0.4),0 0 16px rgba(51,255,153,0.25);}50%{box-shadow:0 24px 60px rgba(0,0,0,0.8),0 0 0 1px rgba(0,255,80,0.6),0 0 24px rgba(0,255,80,0.4);}}
#ac-root.ac-anim-matrix #ac-pulse{animation:ac-pulse 1s ease-in-out infinite;}
/* ── PANEL VISUAL FX ── */
/* Animated gradient mesh behind header */
#ac-header{position:relative;}
#ac-fx-mesh{position:absolute;inset:0;pointer-events:none!important;z-index:0;opacity:0.5;background:radial-gradient(circle at 20% 30%,var(--ac-red-glow),transparent 45%),radial-gradient(circle at 80% 70%,var(--ac-red-dim),transparent 50%),radial-gradient(circle at 50% 50%,var(--ac-red-glow),transparent 60%);background-size:200% 200%;animation:ac-mesh 8s ease-in-out infinite;}
@keyframes ac-mesh{0%,100%{background-position:0% 0%,100% 100%,50% 50%;}33%{background-position:100% 0%,0% 100%,30% 70%;}66%{background-position:50% 100%,50% 0%,70% 30%;}}
#ac-root.ac-no-fx #ac-fx-mesh{display:none;}
/* Floating particle canvas in header */
#ac-fx-particles{position:absolute;inset:0;pointer-events:none!important;z-index:0;width:100%;height:100%;}
#ac-root.ac-no-fx #ac-fx-particles{display:none;}
/* CRT scanline shimmer overlay on whole panel */
#ac-fx-scan{position:absolute;inset:0;pointer-events:none!important;z-index:1;opacity:0.04;background:repeating-linear-gradient(0deg,transparent 0px,transparent 2px,#fff 3px,transparent 4px);animation:ac-scan 8s linear infinite;border-radius:inherit;}
@keyframes ac-scan{0%{background-position-y:0;}100%{background-position-y:100px;}}
#ac-root.ac-no-fx #ac-fx-scan{display:none;}
/* Drag glow trail */
#ac-root.ac-drag{box-shadow:0 0 0 1px var(--ac-red2),0 0 30px var(--ac-red-glow),0 24px 60px rgba(0,0,0,0.85)!important;}
/* Active tab glow pulse */
.ac-tab.on{position:relative;}
.ac-tab.on::after{content:'';position:absolute;bottom:0;left:50%;transform:translateX(-50%);width:60%;height:2px;background:var(--ac-red2);box-shadow:0 0 8px var(--ac-red2);animation:ac-tab-glow 2s ease-in-out infinite;}
@keyframes ac-tab-glow{0%,100%{opacity:0.6;box-shadow:0 0 6px var(--ac-red2);}50%{opacity:1;box-shadow:0 0 14px var(--ac-red2),0 0 4px var(--ac-red3);}}
#ac-root.ac-no-fx .ac-tab.on::after{animation:none;box-shadow:none;}
/* Staggered row entrance when switching tabs */
.ac-panel.on .ac-row,.ac-panel.on .ac-sect,.ac-panel.on .ac-theme-row,.ac-panel.on #ac-live-readout,.ac-panel.on .ac-upload-row,.ac-panel.on .ac-prow{animation:ac-row-in 0.35s cubic-bezier(0.34,1.4,0.64,1) both;}
.ac-panel.on>*:nth-child(1){animation-delay:0.02s;}
.ac-panel.on>*:nth-child(2){animation-delay:0.05s;}
.ac-panel.on>*:nth-child(3){animation-delay:0.08s;}
.ac-panel.on>*:nth-child(4){animation-delay:0.11s;}
.ac-panel.on>*:nth-child(5){animation-delay:0.14s;}
.ac-panel.on>*:nth-child(6){animation-delay:0.17s;}
.ac-panel.on>*:nth-child(7){animation-delay:0.20s;}
.ac-panel.on>*:nth-child(8){animation-delay:0.23s;}
@keyframes ac-row-in{from{opacity:0;transform:translateX(-8px);}to{opacity:1;transform:translateX(0);}}
#ac-root.ac-no-fx .ac-panel.on>*{animation:none!important;}
/* Title shimmer */
#ac-title span{background:linear-gradient(90deg,var(--ac-red3),var(--ac-red2),var(--ac-red3));background-size:200% 100%;-webkit-background-clip:text;background-clip:text;animation:ac-title-shimmer 4s linear infinite;}
@keyframes ac-title-shimmer{0%{background-position:0% 50%;}100%{background-position:200% 50%;}}
#ac-root.ac-no-fx #ac-title span{animation:none;background:var(--ac-red3);-webkit-background-clip:text;background-clip:text;}
/* Toggle switch glow when on */
.ac-tog input:checked+.ac-trk{box-shadow:0 0 10px var(--ac-red-glow),0 0 4px var(--ac-red2);}
/* COMPACT MODE */
/* ── COMPACT MODE ── */
#ac-root.ac-compact .ac-row{padding:5px 16px;}
#ac-root.ac-compact .ac-sect{padding:4px 16px 2px;}
#ac-root.ac-compact #ac-live-readout{padding:6px 14px;margin:2px 12px 5px;}
#ac-root.ac-compact .ac-live-val{font-size:18px;}
#ac-root.ac-compact .ac-lbl{font-size:12px;}
#ac-root.ac-compact #ac-master{padding:7px 16px;}
/* ── CLICK RIPPLE FX ── */
.ac-ripple{position:fixed;border-radius:50%;background:var(--ac-red3);opacity:0.5;transform:translate(-50%,-50%) scale(0);pointer-events:none;z-index:2147483646;animation:ac-ripple-go 0.55s ease-out forwards;}
@keyframes ac-ripple-go{to{transform:translate(-50%,-50%) scale(14);opacity:0;}}
@media(max-width:600px),(pointer:coarse){#ac-root{width:calc(100vw - 20px);max-width:360px;bottom:10px;right:10px;left:auto;border-radius:16px;}.ac-tog{width:46px;height:26px;}.ac-tog .ac-thb{width:20px;height:20px;}.ac-tog input:checked~.ac-thb{left:23px;}.ac-tab{padding:10px 2px;font-size:13px;}.ac-row{padding:12px 16px;}.ac-lbl{font-size:14px;}.ac-incr-btn{width:26px;height:20px;font-size:13px;}.ac-input,.ac-incr-inp,.ac-select{font-size:15px;padding:8px 10px;}.ac-btn{padding:11px 14px;font-size:13px;}.ac-upload-btn{padding:10px;font-size:13px;}.ac-panel{max-height:60vh;}}
`;
document.head.appendChild(style);
const fileInput=document.createElement('input');
fileInput.type='file';fileInput.style.display='none';
document.body.appendChild(fileInput);
const root=document.createElement('div');
root.id='ac-root';
root.innerHTML=`
<div id="ac-header">
<div id="ac-fx-mesh"></div>
<canvas id="ac-fx-particles"></canvas>
<div id="ac-logo-row">
<div id="ac-pulse"></div>
<div><div id="ac-title"><span>Aced</span> Helper <span id="ac-ver">v1.3</span></div></div>
</div>
<div id="ac-header-right">
<div id="ac-badge" class="${CFG.enabled?'on':'off'}">${CFG.enabled?'● ACTIVE':'○ OFF'}</div>
<button id="ac-minimize-btn" title="Minimize">▼</button>
</div>
</div>
<div id="ac-master">
<span class="ac-master-lbl">Master Enable</span>
<label class="ac-tog ac-tog-lg"><input type="checkbox" id="ac-en" ${CFG.enabled?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label>
</div>
<div id="ac-tabs">
<button class="ac-tab on" data-tab="ctrl">Control</button>
<button class="ac-tab" data-tab="score">Scoring</button>
<button class="ac-tab" data-tab="media">Media</button>
<button class="ac-tab" data-tab="cfg">Config</button>
</div>
<!-- CONTROL -->
<div class="ac-panel on" id="ac-p-ctrl">
<div class="ac-row"><span class="ac-lbl">Auto Requeue</span><label class="ac-tog"><input type="checkbox" id="ac-autoRequeue" ${CFG.autoRequeue?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-row"><span class="ac-lbl">Auto Start</span><label class="ac-tog"><input type="checkbox" id="ac-autoStart" ${CFG.autoStart?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-row"><span class="ac-lbl">AFK Cam</span><label class="ac-tog"><input type="checkbox" id="ac-afkCam" ${CFG.afkCam?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-row"><span class="ac-lbl">Autoqueue Delay</span><span style="display:flex;align-items:center;gap:5px;"><input class="ac-input ac-input-md" type="number" id="ac-farmDelay" value="${CFG.autofarmDelay}" min="1" max="9000" step="50"><span style="font-size:10px;color:var(--ac-muted2)">ms</span></span></div>
</div>
<!-- SCORING -->
<div class="ac-panel" id="ac-p-score">
<div class="ac-row" style="padding-bottom:4px;"><div><div class="ac-lbl" style="color:var(--ac-red3)">Realistic Scoring</div><div class="ac-sub">tracks real face movement</div></div><label class="ac-tog"><input type="checkbox" id="ac-realScore" ${CFG.realisticScoring?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-sect"><span class="ac-sect-lbl">Live Spoof</span><span class="ac-sect-arr">▼</span></div>
<div class="ac-row"><span class="ac-lbl">Live Spoof</span><label class="ac-tog"><input type="checkbox" id="ac-liveSpoof" ${CFG.liveSpoof?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div id="ac-live-readout"><span class="ac-live-label">CURRENT SCORE</span><span class="ac-live-val" id="ac-live-val">—</span></div>
<div class="ac-row"><span class="ac-lbl">Mode</span><select class="ac-select" id="ac-liveMode"><option value="fluctuate" ${CFG.liveMode==='fluctuate'?'selected':''}>Fluctuate</option><option value="fixed" ${CFG.liveMode==='fixed'?'selected':''}>Fixed</option></select></div>
<div class="ac-row" id="ac-row-liveMin"><span class="ac-lbl" id="ac-lbl-liveMin">Min</span><div class="ac-incr"><input class="ac-incr-inp" type="number" id="ac-liveMin" value="${CFG.liveMin}" step="0.1" min="1" max="10"><div class="ac-incr-btns"><button class="ac-incr-btn p" data-t="ac-liveMin" data-s="0.1">+</button><button class="ac-incr-btn m" data-t="ac-liveMin" data-s="-0.1">−</button></div></div></div>
<div class="ac-row" id="ac-row-liveMax"><span class="ac-lbl">Max</span><div class="ac-incr"><input class="ac-incr-inp" type="number" id="ac-liveMax" value="${CFG.liveMax}" step="0.1" min="1" max="10"><div class="ac-incr-btns"><button class="ac-incr-btn p" data-t="ac-liveMax" data-s="0.1">+</button><button class="ac-incr-btn m" data-t="ac-liveMax" data-s="-0.1">−</button></div></div></div>
<div class="ac-sect"><span class="ac-sect-lbl">Final Score</span><span class="ac-sect-arr">▼</span></div>
<div class="ac-row"><span class="ac-lbl">Min</span><div class="ac-incr"><input class="ac-incr-inp" type="number" id="ac-finalMin" value="${CFG.finalMin}" step="0.1" min="1" max="10"><div class="ac-incr-btns"><button class="ac-incr-btn p" data-t="ac-finalMin" data-s="0.1">+</button><button class="ac-incr-btn m" data-t="ac-finalMin" data-s="-0.1">−</button></div></div></div>
<div class="ac-row"><span class="ac-lbl">Max</span><div class="ac-incr"><input class="ac-incr-inp" type="number" id="ac-finalMax" value="${CFG.finalMax}" step="0.1" min="1" max="10"><div class="ac-incr-btns"><button class="ac-incr-btn p" data-t="ac-finalMax" data-s="0.1">+</button><button class="ac-incr-btn m" data-t="ac-finalMax" data-s="-0.1">−</button></div></div></div>
</div>
<!-- MEDIA -->
<div class="ac-panel" id="ac-p-media">
<div class="ac-notice ac-notice-warn">⚠ Upload a video/picture below, then turn on <strong>AFK Cam</strong> (Control tab).</div>
<div class="ac-row"><span class="ac-lbl">Mirror Opponent (Send Back)</span><label class="ac-tog"><input type="checkbox" id="ac-mirror" ${CFG.mirrorOpponent?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-row"><span class="ac-lbl">Invert Cam</span><label class="ac-tog"><input type="checkbox" id="ac-invert" ${CFG.invertCam?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-row"><span class="ac-lbl">Loop Video</span><label class="ac-tog"><input type="checkbox" id="ac-loop" ${CFG.loopVideo?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-sect"><span class="ac-sect-lbl">AFK Source</span><span class="ac-sect-arr">▼</span></div>
<div class="ac-row"><span class="ac-lbl">Source</span><select class="ac-select" id="ac-afkSrc"><option value="Video" ${CFG.afkSource==='Video'?'selected':''}>Video</option><option value="Picture" ${CFG.afkSource==='Picture'?'selected':''}>Picture</option></select></div>
<div class="ac-upload-row"><button class="ac-upload-btn" id="ac-upload-btn">📁 <span id="ac-upload-lbl">${CFG.afkSource==='Picture'?'Upload Picture':'Upload Video'}</span></button><span class="ac-lbl" style="flex-shrink:0">Zoom Fit</span><label class="ac-tog" style="margin-left:4px;"><input type="checkbox" id="ac-zoomFit" ${CFG.zoomFit?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div style="padding:2px 16px 6px;font-size:10px;color:var(--ac-muted2);font-family:var(--ac-mono);" id="ac-file-name">No file selected</div>
</div>
<!-- CONFIG -->
<div class="ac-panel" id="ac-p-cfg">
<div class="ac-row"><div><span class="ac-lbl">GUI Hide Key</span><div class="ac-sub">slide away / back</div></div><span class="ac-keybadge">h</span></div>
<div class="ac-row"><span class="ac-lbl">Live Spoof Key</span><span class="ac-keybadge">1</span></div>
<div class="ac-row"><span class="ac-lbl">Final Score Key</span><span class="ac-keybadge">f</span></div>
<div class="ac-row"><div><span class="ac-lbl">Panic Hide Key</span><div class="ac-sub">instant blackout</div></div><span class="ac-keybadge">${CFG.panicKey==='`'?'~':CFG.panicKey}</span></div>
<div class="ac-sect"><span class="ac-sect-lbl">Tweaks</span><span class="ac-sect-arr">▼</span></div>
<div class="ac-row"><span class="ac-lbl">Click Ripple FX</span><label class="ac-tog"><input type="checkbox" id="ac-clickAnim" ${CFG.clickAnim?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-row"><span class="ac-lbl">Compact Mode</span><label class="ac-tog"><input type="checkbox" id="ac-compact" ${CFG.compactMode?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-row"><span class="ac-lbl">Panel Effects</span><label class="ac-tog"><input type="checkbox" id="ac-panelFX" ${CFG.panelFX?'checked':''}><span class="ac-trk"></span><span class="ac-thb"></span></label></div>
<div class="ac-row">
<span class="ac-lbl">Drift Speed</span>
<div class="ac-incr">
<input class="ac-incr-inp" type="number" id="ac-drift" value="${CFG.scoreDrift}" step="0.01" min="0.01" max="0.2">
<div class="ac-incr-btns"><button class="ac-incr-btn p" data-t="ac-drift" data-s="0.01">+</button><button class="ac-incr-btn m" data-t="ac-drift" data-s="-0.01">−</button></div>
</div>
</div>
<div class="ac-row">
<span class="ac-lbl">Panel Opacity</span>
<span style="display:flex;align-items:center;gap:8px;flex:1;justify-content:flex-end;">
<input type="range" id="ac-opacity" min="40" max="100" value="${CFG.opacity}" style="width:90px;accent-color:var(--ac-red2);cursor:pointer;">
<span id="ac-opacity-val" style="font-family:var(--ac-mono);font-size:11px;color:var(--ac-muted2);min-width:32px;text-align:right;">${CFG.opacity}%</span>
</span>
</div>
<div class="ac-sect"><span class="ac-sect-lbl">Theme</span><span class="ac-sect-arr">▼</span></div>
<div class="ac-theme-row">
<span class="ac-lbl">Color</span>
<div class="ac-swatches">
<div class="ac-swatch ${CFG.theme==='red'?'active':''}" data-t="red" title="Red"></div>
<div class="ac-swatch ${CFG.theme==='blue'?'active':''}" data-t="blue" title="Blue"></div>
<div class="ac-swatch ${CFG.theme==='green'?'active':''}" data-t="green" title="Green"></div>
<div class="ac-swatch ${CFG.theme==='purple'?'active':''}" data-t="purple" title="Purple"></div>
<div class="ac-swatch ${CFG.theme==='gold'?'active':''}" data-t="gold" title="Gold"></div>
</div>
</div>
<div class="ac-theme-row" style="padding-top:0;">
<span class="ac-lbl" style="opacity:0;">.</span>
<div class="ac-swatches">
<div class="ac-swatch ${CFG.theme==='cyan'?'active':''}" data-t="cyan" title="Cyan"></div>
<div class="ac-swatch ${CFG.theme==='pink'?'active':''}" data-t="pink" title="Pink"></div>
<div class="ac-swatch ${CFG.theme==='mono'?'active':''}" data-t="mono" title="Mono"></div>
<div class="ac-swatch ${CFG.theme==='gold'?'':''}" style="opacity:0;pointer-events:none;"></div>
<div class="ac-swatch ${CFG.theme==='gold'?'':''}" style="opacity:0;pointer-events:none;"></div>
</div>
</div>
<div class="ac-theme-row" style="padding-top:2px;">
<span class="ac-lbl" style="font-size:11px;color:var(--ac-muted2);">Animated</span>
<div class="ac-swatches">
<div class="ac-swatch anim ${CFG.theme==='rainbow'?'active':''}" data-t="rainbow" title="Rainbow"></div>
<div class="ac-swatch anim ${CFG.theme==='aurora'?'active':''}" data-t="aurora" title="Aurora"></div>
<div class="ac-swatch anim ${CFG.theme==='sunset'?'active':''}" data-t="sunset" title="Sunset"></div>
<div class="ac-swatch anim ${CFG.theme==='matrix'?'active':''}" data-t="matrix" title="Matrix"></div>
</div>
</div>
<div class="ac-sect"><span class="ac-sect-lbl">Profiles</span><span class="ac-sect-arr">▼</span></div>
<div class="ac-prow"><input class="ac-input ac-input-full ac-input-text" type="text" id="ac-profName" placeholder="Profile name"><button class="ac-btn ac-btn-green" id="ac-prof-save" style="width:auto;margin:0;padding:6px 12px;">Save</button></div>
<div class="ac-prow" style="margin-top:4px;"><select class="ac-select" id="ac-prof-list" style="flex:1;"></select><button class="ac-btn ac-btn-default" id="ac-prof-load" style="width:auto;margin:0;padding:6px 10px;">Load</button><button class="ac-btn ac-btn-red" id="ac-prof-del" style="width:auto;margin:0;padding:6px 10px;">Del</button></div>
<button class="ac-btn ac-btn-red" id="ac-clear-cookies" style="margin-top:8px;">🍪 Clear Cookies</button>
</div>
<div id="ac-fx-scan"></div>
<div id="ac-footer">
<span id="ac-footer-l">Discord</span>
<span id="ac-footer-r">by <span>@celler1321</span></span>
</div>
`;
document.body.appendChild(root);
// Apply saved theme
applyTheme(CFG.theme);
// ── Apply persisted opacity + compact mode on load ──
function applyOpacity(){const r=document.getElementById('ac-root');if(r)r.style.opacity=(CFG.opacity/100);}
function applyCompact(){const r=document.getElementById('ac-root');if(r)r.classList.toggle('ac-compact',!!CFG.compactMode);}
applyOpacity(); applyCompact();
// ── Panel FX master toggle ──
function applyFX(){const r=document.getElementById('ac-root');if(r)r.classList.toggle('ac-no-fx',!CFG.panelFX);if(CFG.panelFX)startParticles();else stopParticles();}
// ── Header particle system (floating embers in theme color) ──
let _particleRAF=null, _particles=[];
function startParticles(){
const cv=$('ac-fx-particles'); if(!cv)return;
const ctx=cv.getContext('2d');
function resize(){const h=$('ac-header');if(h){cv.width=h.offsetWidth;cv.height=h.offsetHeight;}}
resize();
if(!_particles.length){
for(let i=0;i<14;i++)_particles.push({x:Math.random()*cv.width,y:Math.random()*cv.height,vx:(Math.random()-0.5)*0.3,vy:(Math.random()-0.5)*0.3,r:Math.random()*1.8+0.4,a:Math.random()*0.5+0.2});
}
function getAccent(){return getComputedStyle(document.documentElement).getPropertyValue('--ac-red3').trim()||'#ff3355';}
function tick(){
if(!CFG.panelFX||document.hidden){_particleRAF=null;return;}
ctx.clearRect(0,0,cv.width,cv.height);
const col=getAccent();
for(const p of _particles){
p.x+=p.vx; p.y+=p.vy;
if(p.x<0)p.x=cv.width; if(p.x>cv.width)p.x=0;
if(p.y<0)p.y=cv.height; if(p.y>cv.height)p.y=0;
ctx.beginPath();
ctx.arc(p.x,p.y,p.r,0,Math.PI*2);
ctx.fillStyle=col; ctx.globalAlpha=p.a;
ctx.fill();
}
ctx.globalAlpha=1;
_particleRAF=requestAnimationFrame(tick);
}
if(!_particleRAF)tick();
}
function stopParticles(){if(_particleRAF){cancelAnimationFrame(_particleRAF);_particleRAF=null;}const cv=$('ac-fx-particles');if(cv){const c=cv.getContext('2d');c.clearRect(0,0,cv.width,cv.height);}}
const $=id=>document.getElementById(id);
// FX must start AFTER $ is defined (these use $ internally)
applyFX();
document.addEventListener('visibilitychange',()=>{if(!document.hidden&&CFG.panelFX&&!_particleRAF)startParticles();});
// Minimize button
$('ac-minimize-btn').addEventListener('click', e => {
e.stopPropagation();
root.classList.toggle('ac-minimized');
});
// Theme swatches
root.querySelectorAll('.ac-swatch').forEach(sw => {
sw.addEventListener('click', () => {
const t = sw.dataset.t;
CFG.theme = t; saveCfg();
applyTheme(t);
root.querySelectorAll('.ac-swatch').forEach(s => s.classList.toggle('active', s.dataset.t === t));
});
});
function syncBadge(){const b=$('ac-badge');b.className=CFG.enabled?'on':'off';b.textContent=CFG.enabled?'● ACTIVE':'○ OFF';}
root.querySelectorAll('.ac-tab').forEach(tab=>{tab.onclick=()=>{root.querySelectorAll('.ac-tab').forEach(t=>t.classList.remove('on'));root.querySelectorAll('.ac-panel').forEach(p=>p.classList.remove('on'));tab.classList.add('on');$('ac-p-'+tab.dataset.tab)?.classList.add('on');};});
function bind(id,key,type,cb){const el=$(id);if(!el)return;el.addEventListener(type==='input'?'input':'change',function(){const val=this.type==='checkbox'?this.checked:this.type==='number'?+this.value:this.value;CFG[key]=val;saveCfg();if(cb)cb(val,this);});}
bind('ac-en','enabled','change',()=>syncBadge());
bind('ac-autoRequeue','autoRequeue','change');
bind('ac-autoStart','autoStart','change');
bind('ac-afkCam','afkCam','change',val=>{const n=$('ac-file-name');if(val){if(!AFK.hasMedia()){if(n)n.textContent='⚠ Upload a video/picture in Media tab';return;}AFK.swapSendersToAfk();}else{AFK.restoreSenders();}});
bind('ac-farmDelay','autofarmDelay','input',val=>{CFG.autofarmDelay=Math.max(1,Math.min(9000,val||1));saveCfg();});
bind('ac-realScore','realisticScoring','change');
bind('ac-liveSpoof','liveSpoof','change');
bind('ac-liveMin','liveMin','input',v=>{CFG.liveMin=Math.max(1,Math.min(10,v||1));saveCfg();});
bind('ac-liveMax','liveMax','input',v=>{CFG.liveMax=Math.max(1,Math.min(10,v||1));saveCfg();});
function syncLiveMode(){const fixed=CFG.liveMode==='fixed';const mr=$('ac-row-liveMax'),ml=$('ac-lbl-liveMin');if(mr)mr.style.display=fixed?'none':'';if(ml)ml.textContent=fixed?'Score':'Min';}
$('ac-liveMode')?.addEventListener('change',function(){CFG.liveMode=this.value;saveCfg();syncLiveMode();});
syncLiveMode();
setInterval(()=>{const el=$('ac-live-val');if(!el)return;const active=CFG.enabled&&CFG.liveSpoof&&!CFG.realisticScoring;if(!active){el.textContent='OFF';el.classList.add('ac-off');return;}if(!IN_MATCH){el.textContent='IDLE';el.classList.add('ac-off');return;}el.classList.remove('ac-off');const v=currentScore.toFixed(1);if(el.textContent!==v){el.textContent=v;el.classList.add('ac-pulse');setTimeout(()=>el.classList.remove('ac-pulse'),150);}},200);
bind('ac-finalMin','finalMin','input',v=>{CFG.finalMin=Math.max(1,Math.min(10,v||1));saveCfg();});
bind('ac-finalMax','finalMax','input',v=>{CFG.finalMax=Math.max(1,Math.min(10,v||1));saveCfg();});
bind('ac-mirror','mirrorOpponent','change',val=>{if(val){if(!AFK.remoteVideo){const n=$('ac-file-name');if(n)n.textContent='⚠ Mirror works only during a live match';}AFK.swapSendersToAfk();}else if(!CFG.afkCam){AFK.restoreSenders();}});
bind('ac-invert','invertCam','change');
bind('ac-loop','loopVideo','change');
bind('ac-zoomFit','zoomFit','change');
// ── New tweak binds ──
bind('ac-clickAnim','clickAnim','change');
bind('ac-compact','compactMode','change',()=>applyCompact());
bind('ac-panelFX','panelFX','change',()=>applyFX());
bind('ac-drift','scoreDrift','input',v=>{CFG.scoreDrift=Math.max(0.01,Math.min(0.2,v||0.05));saveCfg();});
const opSlider=$('ac-opacity');
if(opSlider)opSlider.addEventListener('input',function(){CFG.opacity=+this.value;const ov=$('ac-opacity-val');if(ov)ov.textContent=this.value+'%';applyOpacity();saveCfg();});
$('ac-afkSrc')?.addEventListener('change',function(){CFG.afkSource=this.value;saveCfg();const lbl=$('ac-upload-lbl');if(lbl)lbl.textContent=this.value==='Picture'?'Upload Picture':'Upload Video';fileInput.accept=this.value==='Picture'?'image/*':'video/*';});
$('ac-upload-btn')?.addEventListener('click',()=>{fileInput.accept=CFG.afkSource==='Picture'?'image/*':'video/*';fileInput.value='';fileInput.click();});
fileInput.addEventListener('change',()=>{const file=fileInput.files?.[0];if(!file)return;const n=$('ac-file-name');if(n)n.textContent=file.name+' ✓';AFK.loadFile(file);setTimeout(()=>{if(CFG.afkCam)AFK.swapSendersToAfk();},400);});
FileStore.load().then(saved=>{if(saved&&saved.blob){const kind=saved.meta?.kind||'video';CFG.afkSource=kind==='image'?'Picture':'Video';const sel=$('ac-afkSrc');if(sel)sel.value=CFG.afkSource;const lbl=$('ac-upload-lbl');if(lbl)lbl.textContent=CFG.afkSource==='Picture'?'Upload Picture':'Upload Video';const n=$('ac-file-name');if(n)n.textContent=(saved.meta?.name||'saved file')+' ✓ (restored)';if(!AFK.hasMedia())AFK.loadBlob(saved.blob,kind);setTimeout(()=>{if(CFG.afkCam)AFK.swapSendersToAfk();},800);}});
$('ac-loop')?.addEventListener('change',function(){if(AFK.mediaType==='video'&&AFK.mediaEl)AFK.mediaEl.loop=this.checked;});
root.querySelectorAll('.ac-incr-btn').forEach(btn=>{btn.onclick=()=>{const inp=$(btn.dataset.t);if(!inp)return;let nv=parseFloat(inp.value||0)+parseFloat(btn.dataset.s);const mn=parseFloat(inp.min)||1,mx=parseFloat(inp.max)||10;nv=Math.max(mn,Math.min(mx,nv));inp.value=nv.toFixed(1);inp.dispatchEvent(new Event('input'));};});
document.addEventListener('keydown',e=>{if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA'||e.target.tagName==='SELECT')return;if(e.key==='h')root.classList.toggle('ac-hidden');if(e.key==='1'){CFG.liveSpoof=!CFG.liveSpoof;const el=$('ac-liveSpoof');if(el)el.checked=CFG.liveSpoof;saveCfg();}if(e.key==='f'){CFG.realisticScoring=!CFG.realisticScoring;const el=$('ac-realScore');if(el)el.checked=CFG.realisticScoring;saveCfg();}if(e.key==='m')root.classList.toggle('ac-minimized');if(e.key===(CFG.panicKey||'`')){const hidden=root.classList.toggle('ac-panic');document.body.classList.toggle('ac-panic-active',hidden);}});
function refreshProfiles(){const keys=Object.keys(CFG.profiles);const sel=$('ac-prof-list');if(!sel)return;sel.innerHTML=keys.length?keys.map(k=>`<option value="${k}">${k}</option>`).join(''):'<option value="">No profiles</option>';}
refreshProfiles();
$('ac-prof-save')?.addEventListener('click',()=>{const n=$('ac-profName')?.value?.trim();if(!n)return;const snap=JSON.parse(JSON.stringify(CFG));delete snap.profiles;delete snap.sounds;CFG.profiles[n]=snap;saveCfg();refreshProfiles();$('ac-profName').value='';const btn=$('ac-prof-save');if(btn){btn.textContent='✓ Saved';setTimeout(()=>{btn.textContent='Save';},1500);}});
$('ac-prof-load')?.addEventListener('click',()=>{const n=$('ac-prof-list')?.value;if(!n||!CFG.profiles[n])return;const loaded=JSON.parse(JSON.stringify(CFG.profiles[n]));const profiles=CFG.profiles,sounds=CFG.sounds;Object.assign(CFG,loaded);CFG.profiles=profiles;CFG.sounds=sounds;saveCfg();location.reload();});
$('ac-prof-del')?.addEventListener('click',()=>{const n=$('ac-prof-list')?.value;if(!n)return;delete CFG.profiles[n];saveCfg();refreshProfiles();});
$('ac-clear-cookies')?.addEventListener('click',async function(){if(this.dataset.confirm!=='1'){this.dataset.confirm='1';this.textContent='⚠ Click again to confirm';setTimeout(()=>{this.dataset.confirm='0';this.textContent='🍪 Clear Cookies';},3000);return;}this.dataset.confirm='0';this.textContent='⏳ Wiping…';const keepCfg=(()=>{try{return localStorage.getItem(LS_KEY);}catch(e){return null;}})();const host=location.hostname,labels=host.split('.');const domains=[host,'.'+host,''];for(let i=0;i<labels.length-1;i++){const d=labels.slice(i).join('.');domains.push(d,'.'+d);}const paths=['/',location.pathname,''];const expire='Thu, 01 Jan 1970 00:00:00 GMT';document.cookie.split(';').forEach(c=>{const name=c.split('=')[0].trim();if(!name)return;domains.forEach(d=>paths.forEach(p=>{let s=name+'=; expires='+expire;if(p)s+='; path='+p;if(d)s+='; domain='+d;document.cookie=s;document.cookie=s+'; SameSite=Lax';document.cookie=s+'; SameSite=None; Secure';}));});try{localStorage.clear();if(keepCfg)localStorage.setItem(LS_KEY,keepCfg);}catch(e){}try{sessionStorage.clear();}catch(e){}try{if(indexedDB.databases){const dbs=await indexedDB.databases();for(const db of dbs){if(db.name&&db.name!==FileStore.DB){try{indexedDB.deleteDatabase(db.name);}catch(e){}}}}}catch(e){}try{if(W.caches&&caches.keys){const keys=await caches.keys();for(const k of keys){try{await caches.delete(k);}catch(e){}}}}catch(e){}try{if(navigator.serviceWorker&&navigator.serviceWorker.getRegistrations){const regs=await navigator.serviceWorker.getRegistrations();for(const r of regs){try{await r.unregister();}catch(e){}}}}catch(e){}this.textContent='✓ Cleared — reloading';setTimeout(()=>location.reload(),700);});
// ── Click ripple FX ──
document.addEventListener('click', e => {
if(!CFG.clickAnim)return;
if(e.target.closest('#ac-root'))return; // don't ripple on our own UI
const rip=document.createElement('div');
rip.className='ac-ripple';
rip.style.left=e.clientX+'px'; rip.style.top=e.clientY+'px';
rip.style.width='8px'; rip.style.height='8px';
document.body.appendChild(rip);
setTimeout(()=>rip.remove(),600);
}, true);
let drag=false,ox=0,oy=0;
function resetPosition(){
// Snap back to default bottom-right corner
root.style.left='auto'; root.style.top='auto';
root.style.right='22px'; root.style.bottom='22px';
}
function clampIntoView(){
// If the panel is positioned via left/top, make sure it's still on-screen
if(root.style.left&&root.style.left!=='auto'){
const w=root.offsetWidth,h=root.offsetHeight;
let nx=parseFloat(root.style.left)||0, ny=parseFloat(root.style.top)||0;
nx=Math.max(0,Math.min(window.innerWidth-w,nx));
ny=Math.max(0,Math.min(window.innerHeight-h,ny));
root.style.left=nx+'px'; root.style.top=ny+'px';
}
}
window.addEventListener('resize',clampIntoView);
// Drag only engages after the pointer moves past a small threshold — a plain
// click never enters drag mode (which previously could get stuck and block the UI).
let pendingDrag=false, startX=0, startY=0;
const DRAG_THRESHOLD=5;
function pointerDown(cx,cy){pendingDrag=true;drag=false;startX=cx;startY=cy;const r=root.getBoundingClientRect();ox=cx-r.left;oy=cy-r.top;}
function pointerMove(cx,cy){
if(!pendingDrag&&!drag)return;
if(!drag){
if(Math.abs(cx-startX)<DRAG_THRESHOLD&&Math.abs(cy-startY)<DRAG_THRESHOLD)return;
// Crossed threshold — begin actual drag
drag=true;root.classList.add('ac-drag');root.style.bottom='auto';root.style.right='auto';
}
const w=root.offsetWidth,h=root.offsetHeight;
let nx=cx-ox,ny=cy-oy;
nx=Math.max(0,Math.min(window.innerWidth-w,nx));
ny=Math.max(0,Math.min(window.innerHeight-h,ny));
root.style.left=nx+'px';root.style.top=ny+'px';
}
function pointerUp(){pendingDrag=false;drag=false;root.classList.remove('ac-drag');}
const hdr=$('ac-header');
hdr.addEventListener('mousedown',e=>{if(e.target.closest('#ac-minimize-btn'))return;pointerDown(e.clientX,e.clientY);});
hdr.addEventListener('dblclick',e=>{if(e.target.closest('#ac-minimize-btn'))return;resetPosition();});
document.addEventListener('mousemove',e=>pointerMove(e.clientX,e.clientY));
document.addEventListener('mouseup',pointerUp);
// Safety: if the window loses focus or the mouse leaves the document, cancel any drag
window.addEventListener('blur',pointerUp);
document.addEventListener('mouseleave',pointerUp);
hdr.addEventListener('touchstart',e=>{if(e.target.closest('#ac-minimize-btn'))return;const t=e.touches[0];if(t)pointerDown(t.clientX,t.clientY);},{passive:true});
document.addEventListener('touchmove',e=>{const t=e.touches[0];if(t&&(pendingDrag||drag)){pointerMove(t.clientX,t.clientY);if(drag)e.preventDefault();}},{passive:false});
document.addEventListener('touchend',pointerUp);
document.addEventListener('touchcancel',pointerUp);
}
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',buildUI);
else if(document.body)buildUI();
else document.addEventListener('DOMContentLoaded',buildUI);
})();