let csrfToken="";
    function removeLegacyThinkingToast(){document.getElementById("thinkingToast")?.remove()}
    removeLegacyThinkingToast();
    function setGlobalThinking(){removeLegacyThinkingToast()}
    function beginGlobalThinking(){setGlobalThinking();return()=>{}}
    const APP_VERSION=window.YSP_APP_VERSION||"__APP_VERSION__", API_BASE=window.YSP_API_BASE||"api.php", apiUrl=action=>API_BASE+"?action="+encodeURIComponent(action)+"&_="+Date.now(), $=id=>document.getElementById(id), delay=ms=>new Promise(resolve=>setTimeout(resolve,ms)), screens=["loading","loginStart","auth","teamPick","menu","managerSavesPage","optionsPage","trainingMenu","admin","manager","match"].map($), api=(action,data={},options={})=>{const done=beginGlobalThinking(options),h={"Content-Type":"application/json","Cache-Control":"no-cache"};if(csrfToken)h["X-CSRF-Token"]=csrfToken;return fetch(apiUrl(action),{method:"POST",cache:"no-store",headers:h,body:JSON.stringify(data)}).then(r=>r.json()).then(r=>{if(r?.csrfToken)csrfToken=r.csrfToken;return r}).finally(done)};
    const POLL_MS=260, MANAGER_POLL_MS=1500, MENU_INVITE_POLL_MS=1500, INTERP_MIN=150, INTERP_MAX=380, INTRO_MS=4300, LINEUP_MS=10000, FINAL_SCORERS_MS=10000, MANAGER_SIM_PLAYBACK_MS=60000, MANAGER_SIM_LOAD_SETTLE_MS=260, MAX_MATCH_SUBS=5;
    let me=null, match=null, poll=null, pollBusy=false, managerPoll=null, managerPollBusy=false, menuInvitePoll=null, menuInvitePollBusy=false, lastMatchAt=0, W=1,H=1,p={}, view=emptyView(), goalFx=null, goalBanner=null, refView=null, introMatchId=null, introUntil=0, crowdMatchId=null, crowdWanted=false, crowdActive=0, crowdFading=false, lineupMatchId=null, lineupUntil=0, finalTimer=null, finalMatchId=null, matchReturnBusy=false, audio=null;
    let trainingMode=false, trainingData=null, trainingSelection=[], trainingRace=null, trainingShot=null, trainingScenario="speed", trainingLast=0, trainingDivisionId="", trainingTeamId="";
    let managerUsers=[], managerPicked=[], managerSeason=null, managerFinance=null, managerSquad=null, managerMarket=[], managerTransferOffers={incoming:[],outgoing:[],notices:[]}, managerInvites=[], managerDivisions=[], managerViewDivision=null, managerAbortConfirm=false, managerMarketNotice="", managerMarketRoleFilter="", managerSquadSellRoleFilter="", managerBrowseLevel=5, managerBrowseTeam="", managerBrowseNotice="", managerBrowseRoleFilter="", managerBrowseTeamDetails=null, managerScorersScope="league", managerBusy=false, managerHasSeason=false, managerCanContinue=false, managerHasAnySave=false, squadSwapFirst=null, squadSwapSecond=null, squadSwapBusy=false, squadPlacementMode=false, squadRoleFilter="", squadRosterOpen={start:false,bench:false}, currentTeamDetailsUser="", currentTeamDetails=null, managerTeamBackTarget="", managerTeamInfoOpen=false, managerSaves=[], teamChoices=[], transferOffer=null, managerMsgTimer=null, managerSimTimer=null, managerSimRunning=false, managerSimWhistledGoals=new Set();
    let currentScreen="", appHistoryReady=false, appRestoringHistory=false, appHistoryDepth=0;
    const ROUTE_KEY="yspRoute", SOUND_KEY="yspSound", SOUND_FILE_KEY="yspSoundFiles", SOUND_FILES=[
      {id:"menuMusic",label:"MENU MUSIC",file:"menu.mp3"},
      {id:"crowdSound",label:"CROWD",file:"crowd.mp3"},
      {id:"crowdSoundB",label:"CROWD 2",file:"crowd2.mp3"},
      {id:"crowdGoalSound",label:"GOAL CHEER",file:"crowd_score.mp3"},
      {id:"critSound",label:"CRIT PLAYER",file:"crit_player.mp3"},
      {id:"whistleAudio",label:"WHISTLE",file:"whistle.ogg"},
      {id:"shotSound",label:"KICK / SHOT",file:"kick.ogg"},
    ];
    let soundSettings={};
    try{soundSettings=JSON.parse(localStorage.getItem(SOUND_FILE_KEY)||"{}")||{}}catch(e){soundSettings={}}
    let soundEnabled=true;
    const APP_PLACEHOLDER="__"+"APP_VERSION__";
    let appReloading=false;
    function realAppVersion(v=APP_VERSION){return v&&v!==APP_PLACEHOLDER?String(v):""}
    function cacheBustedUrl(version){const u=new URL(location.href);u.searchParams.set("v",version);u.searchParams.set("_",Date.now());return u.toString()}
    function forceAppReload(version=realAppVersion()){if(appReloading||!version)return true;appReloading=true;try{sessionStorage.setItem("yspReloadVersion",version)}catch(e){}location.replace(cacheBustedUrl(version));return true}
    function checkLoadedVersion(version){version=realAppVersion(version);const loaded=realAppVersion();if(!version||!loaded)return false;if(version!==loaded)return forceAppReload(version);return false}
    async function checkRemoteVersion(){try{const r=await fetch(apiUrl("version"),{cache:"no-store",headers:{"Cache-Control":"no-cache"}}).then(x=>x.json());if(r?.ok)checkLoadedVersion(r.appVersion)}catch(e){}}
    addEventListener("pageshow",e=>{if(e.persisted)location.reload();else checkRemoteVersion()});
    addEventListener("focus",()=>{checkRemoteVersion();refreshMenuInvite();refreshManager()});
    document.addEventListener("visibilitychange",()=>{if(!document.hidden){checkRemoteVersion();refreshMenuInvite();refreshManager()}});
    setInterval(checkRemoteVersion,30000);
    function show(id){currentScreen=id;screens.forEach(s=>s.classList.toggle("hide",s.id!==id));setGlobalThinking();syncScreenPolls();syncManagerMenuMusic()}
    function syncScreenPolls(){if(currentScreen==="menu"&&me)startMenuInvitePoll();else stopMenuInvitePoll()}
    function visibleScreen(){return currentScreen||screens.find(s=>s&&!s.classList.contains("hide"))?.id||""}
    function appHistoryState(screen=visibleScreen()){return{ysp:true,screen,managerSection:screen==="manager"?currentManagerSection():""}}
    function sameAppState(a,b){return !!a&&!!b&&a.ysp&&b.ysp&&a.screen===b.screen&&(a.managerSection||"")===(b.managerSection||"")}
    function replaceAppHistory(){if(!history.replaceState)return;const s=appHistoryState();try{history.replaceState(s,"",location.href)}catch(e){}}
    function pushAppHistory(){if(appRestoringHistory||!appHistoryReady||!history.pushState)return;const s=appHistoryState();if(s.screen==="menu"||sameAppState(history.state,s))return;try{history.pushState(s,"",location.href);appHistoryDepth++}catch(e){}}
    function syncHistoryFromState(){
      if(appRestoringHistory)return;
      if(!appHistoryReady){
        appHistoryReady=true;
        const screen=visibleScreen(),needsMenuBase=!!(me&&me.team&&screen&&screen!=="menu"&&screen!=="loginStart"&&screen!=="teamPick");
        if(needsMenuBase&&history.replaceState&&history.pushState){
          const current=appHistoryState(screen);
          try{history.replaceState({ysp:true,screen:"menu",managerSection:""},"",location.href);history.pushState(current,"",location.href);appHistoryDepth=1}catch(e){replaceAppHistory()}
          return
        }
        replaceAppHistory();return
      }
      replaceAppHistory()
    }
    function goBackOrMenu(){if(appHistoryDepth>0)history.back();else goMainMenu(true)}
    function resetTrainingView(){trainingMode=false;trainingRace=null;trainingShot=null;$("trainingPanel").classList.add("hide");$("toMenu").textContent="MENU"}
    function goMainMenu(replace=false){stopPoll();stopManagerPoll();stopCrowd();setMatch(null);resetTrainingView();setRoute("");renderUserTop();syncMenuControls();show("menu");renderMenuInvite();if(replace)replaceAppHistory()}
    async function restoreAppHistoryState(s){if(!s?.ysp)return;appRestoringHistory=true;try{if(!me&&s.screen!=="loginStart"&&s.screen!=="auth"){setRoute("");show("auth");replaceAppHistory();return}const leavingMatch=visibleScreen()==="match"&&match;if(leavingMatch){await api("leave");setMatch(null);stopPoll()}resetTrainingView();if(s.screen!=="manager")stopManagerPoll();if(s.screen==="manager"){setRoute("manager");if(!managerSeason)clearManagerView("Loading manager mode");show("manager");if(!managerSeason)await loadManager(false);else{renderUserTop();renderInviteTarget("managerInviteBox");renderManagerSeason()}if(s.managerSection)showManagerSection(s.managerSection,false);else showManagerSection("managerHome",false);return}if(s.screen==="managerSavesPage"){await openManagerSaves(false);return}if(s.screen==="menu"){goMainMenu(false);return}if(s.screen==="trainingMenu"){setRoute("");stopCrowd();setMatch(null);renderUserTop();msg("trainingMenuMsg","");show("trainingMenu");return}if(s.screen==="optionsPage"){setRoute("");renderUserTop();updateSoundButton();show("optionsPage");return}if(s.screen==="admin"){setRoute("");renderUserTop();show("admin");return}show(s.screen||"menu")}finally{appRestoringHistory=false}}
    addEventListener("popstate",e=>{if(e.state?.ysp){if(appHistoryDepth>0)appHistoryDepth--;restoreAppHistoryState(e.state)}})
    function setManagerMessage(text="",thinking=false){const el=$("managerMsg");if(!el)return;if(managerMsgTimer){clearTimeout(managerMsgTimer);managerMsgTimer=null}const hasText=!!String(text||"");el.classList.toggle("thinking",hasText&&!!thinking);el.textContent=text||"";el.classList.toggle("show",hasText);if(hasText&&!thinking)managerMsgTimer=setTimeout(()=>setManagerMessage("",false),20000)}
    function msg(id,text){const el=$(id);if(!el)return;if(id==="managerMsg"){setManagerMessage(text||"",false);return}el.textContent=text||""}
    function askConfirm(title,text,yes="YES",no="NO",danger=true){return new Promise(resolve=>{const modal=$("confirmModal"),yesBtn=$("confirmYes"),noBtn=$("confirmNo");$("confirmTitle").textContent=title;$("confirmText").textContent=text||"";yesBtn.textContent=yes;noBtn.textContent=no;yesBtn.classList.toggle("orange",danger);yesBtn.classList.toggle("green",!danger);modal.classList.remove("hide");const close=v=>{modal.classList.add("hide");yesBtn.onclick=null;noBtn.onclick=null;modal.onclick=null;removeEventListener("keydown",key);resolve(v)},key=e=>{if(e.key==="Escape")close(false);if(e.key==="Enter")close(true)};yesBtn.onclick=()=>close(true);noBtn.onclick=()=>close(false);modal.onclick=e=>{if(e.target===modal)close(false)};addEventListener("keydown",key)})}
    function managerStatus(text,thinking=false){setManagerMessage(text||"",!!thinking)}
    function setRoute(route){try{route?localStorage.setItem(ROUTE_KEY,route):localStorage.removeItem(ROUTE_KEY)}catch(e){}}
    function getRoute(){try{return localStorage.getItem(ROUTE_KEY)||""}catch(e){return ""}}
    function clamp(v,a,b){return Math.max(a,Math.min(b,v))}
    function lerp(a,b,t){return a+(b-a)*t}
    function safe(s){return String(s??"").replace(/[&<>"']/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]))}
    function teamHash(t){let s=String(t?.id||t?.name||"team"),h=2166136261;for(let i=0;i<s.length;i++){h^=s.charCodeAt(i);h=Math.imul(h,16777619)}return h>>>0}
    function isHalifaxTeam(t){return /(^|:)fc-halifax-town$/.test(String(t?.id||t?.sourceId||"").toLowerCase())||String(t?.name||"").toUpperCase()==="FC HALIFAX TOWN"}
    function flagSpec(t){if(isHalifaxTeam(t))return{cls:"halifax-crest",style:"--shirt:#0057b8;--trim:#ffffff;--accent:#d71920"};const h=teamHash(t),accent=`hsl(${h%360} 84% 54%)`;return{cls:`flag-p${h%10}`,style:`--shirt:${safe(t?.shirt||"#fff")};--trim:${safe(t?.trim||"#111")};--accent:${accent}`}}
    function teamFlag(t){const f=flagSpec(t);return `<i class="team-flag ${f.cls}" style="${f.style}"></i>`}
    function lineFlag(t){const f=flagSpec(t);return `<span class="line-flag ${f.cls}" style="${f.style}"></span>`}
    function teamLabel(t,r,away=false){let name=`<span>${safe(t?.name||"TEAM")}${r?" "+safe(r):""}</span>`;return away?name+teamFlag(t):teamFlag(t)+name}
    function soundFileEnabled(id){return soundSettings[id]!==false}
    function soundAnyEnabled(){return SOUND_FILES.some(f=>soundFileEnabled(f.id))}
    function soundAllEnabled(){return SOUND_FILES.length>0&&SOUND_FILES.every(f=>soundFileEnabled(f.id))}
    function saveSoundSettings(){soundEnabled=soundAnyEnabled();try{localStorage.setItem(SOUND_FILE_KEY,JSON.stringify(soundSettings));localStorage.setItem(SOUND_KEY,soundEnabled?"on":"off")}catch(e){}}
    function stopAudioId(id,reset=true){const a=$(id);if(!a)return;try{a.pause();if(reset)a.currentTime=0}catch(e){}}
    function stopAllSounds(){stopMenuMusic(true);stopCrowd();SOUND_FILES.forEach(f=>stopAudioId(f.id,true));if(audio?.state==="running")audio.suspend&&audio.suspend()}
    function renderSoundOptions(){const box=$("soundOptions");if(!box)return;box.innerHTML=SOUND_FILES.map(f=>{const on=soundFileEnabled(f.id);return `<div class="sound-option"><span><b>${safe(f.label)}</b><small>${safe(f.file)}</small></span><button class="${on?"green":"orange"}" data-sound-file="${safe(f.id)}" type="button">${on?"ON":"OFF"}</button></div>`}).join("");box.querySelectorAll("[data-sound-file]").forEach(b=>b.onclick=()=>setSoundFileEnabled(b.dataset.soundFile,!soundFileEnabled(b.dataset.soundFile)))}
    function updateSoundButton(){soundEnabled=soundAnyEnabled();const b=$("soundToggle");if(b){const all=soundAllEnabled();b.textContent=all?"ALL SOUND: ON":soundEnabled?"SOUND: CUSTOM":"ALL SOUND: OFF";b.classList.toggle("green",soundEnabled);b.classList.toggle("orange",!soundEnabled)}renderSoundOptions()}
    function setSoundEnabled(on){SOUND_FILES.forEach(f=>{soundSettings[f.id]=!!on});saveSoundSettings();if(!on)stopAllSounds();else{if(match&&["ready","active"].includes(match.status))playCrowd();syncManagerMenuMusic()}updateSoundButton()}
    function setSoundFileEnabled(id,on){if(!SOUND_FILES.some(f=>f.id===id))return;soundSettings[id]=!!on;saveSoundSettings();if(!on){stopAudioId(id,true);if(id==="crowdSound"||id==="crowdSoundB")stopCrowd();if(id==="menuMusic")stopMenuMusic(true)}else{if(match&&["ready","active"].includes(match.status)&&/^crowdSound/.test(id))playCrowd();syncManagerMenuMusic()}updateSoundButton()}
    function ensureAudio(){if(!soundAnyEnabled())return false;const C=window.AudioContext||window.webkitAudioContext;if(!C)return false;audio=audio||new C();if(audio.state==="suspended")audio.resume();return true}
    function tone(freq,dur,type="square",gain=.03,delay=0){if(!soundAnyEnabled()||!audio)return;const now=audio.currentTime+delay,o=audio.createOscillator(),g=audio.createGain();o.type=type;o.frequency.setValueAtTime(freq,now);g.gain.setValueAtTime(0,now);g.gain.linearRampToValueAtTime(gain,now+.01);g.gain.exponentialRampToValueAtTime(.0001,now+dur);o.connect(g);g.connect(audio.destination);o.start(now);o.stop(now+dur+.02)}
    function playAudio(id,vol=.6,overlap=false){if(!soundFileEnabled(id))return false;const a=$(id);if(!a)return false;try{const n=overlap?a.cloneNode(true):a;n.loop=false;n.volume=vol;n.currentTime=0;const p=n.play();if(p&&p.catch)p.catch(()=>{});if(overlap)n.addEventListener("ended",()=>n.remove&&n.remove(),{once:true});return true}catch(e){return false}}
    function kickSound(){if(!soundFileEnabled("shotSound"))return;tone(120,.045,"triangle",.035);tone(78,.035,"sine",.025,.025)}
    function shotSound(){if(!playAudio("shotSound",.95,true))kickSound()}
    function whistleSound(){if(!soundFileEnabled("whistleAudio"))return;if(!playAudio("whistleAudio",.75)){tone(1320,.12,"square",.024);tone(1660,.12,"square",.018,.10)}}
    function critSound(){if(!soundFileEnabled("critSound"))return;if(!playAudio("critSound",.78)){tone(440,.08,"sawtooth",.035);tone(660,.12,"square",.025,.07)}}
    function goalCheerSound(){playAudio("crowdGoalSound",.86,true)}
    function allCrowdTracks(){return[$("crowdSound"),$("crowdSoundB")].filter(Boolean)}
    function crowdTracks(){return allCrowdTracks().filter(a=>soundFileEnabled(a.id))}
    function playMenuMusic(){if(!soundFileEnabled("menuMusic"))return false;const a=$("menuMusic");if(!a)return false;try{a.loop=true;a.volume=.34;if(a.paused){const p=a.play();if(p&&p.catch)p.catch(()=>{})}return true}catch(e){return false}}
    function stopMenuMusic(reset=false){const a=$("menuMusic");if(!a)return;try{a.pause();if(reset)a.currentTime=0}catch(e){}}
    function menuMusicShouldPause(){const matchEl=$("match"),matchVisible=visibleScreen()==="match"||!!(matchEl&&!matchEl.classList.contains("hide")),liveMatch=matchVisible&&!!match&&!trainingMode&&["waiting","ready","active","finished"].includes(match.status||"");return liveMatch||managerSimRunning}
    function syncManagerMenuMusic(){if(menuMusicShouldPause())stopMenuMusic();else playMenuMusic()}
    ["pointerdown","touchstart","keydown"].forEach(type=>document.addEventListener(type,()=>syncManagerMenuMusic(),{passive:true}))
    function fadeAudio(a,from,to,ms,done){const start=performance.now();function step(){let t=clamp((performance.now()-start)/ms,0,1);a.volume=from+(to-from)*t;if(t<1)requestAnimationFrame(step);else if(done)done()}step()}
    function playCrowd(){stopMenuMusic();const tracks=crowdTracks();crowdWanted=tracks.length>0;const a=tracks[crowdActive]||tracks[0];if(!a)return false;try{tracks.forEach(x=>x.loop=false);a.volume=a.volume||.55;if(a.paused){if(Number.isFinite(a.duration)&&a.currentTime>=a.duration-.12)a.currentTime=0;const p=a.play();if(p&&p.catch)p.catch(()=>{})}return true}catch(e){return false}}
    function maintainCrowd(){if(!crowdWanted||!match||!["ready","active"].includes(match.status))return;playCrowd();const tracks=crowdTracks(),a=tracks[crowdActive],b=tracks[1-crowdActive];if(!a||!b||crowdFading||!Number.isFinite(a.duration))return;if(a.duration-a.currentTime<.55){crowdFading=true;try{b.pause();b.currentTime=0;b.volume=0;const p=b.play();if(p&&p.catch)p.catch(()=>{});fadeAudio(b,0,.55,420);fadeAudio(a,a.volume,0,520,()=>{a.pause();a.currentTime=0;crowdActive=1-crowdActive;crowdFading=false})}catch(e){crowdFading=false}}}
    function stopCrowd(){crowdWanted=false;crowdFading=false;allCrowdTracks().forEach(a=>{try{a.pause();a.currentTime=0;a.volume=.55}catch(e){}});crowdActive=0}
    allCrowdTracks().forEach(a=>a.addEventListener("ended",()=>{if(crowdWanted){crowdActive=1-crowdActive;playCrowd()}}));
    function emptyFrame(){return{ball:null,units:{home:{},away:{}}}}
    function emptyView(){return{id:null,prev:emptyFrame(),next:emptyFrame(),from:0,duration:1,lastAt:0}}
    function unitMap(units){const out={};(units||[]).forEach(u=>out[u.id]={x:u.x,y:u.y,weightClass:u.weightClass||"medium"});return out}
    function frameFromMatch(m){return m?{ball:m.ball?{...m.ball}:null,units:{home:unitMap(m.units?.home),away:unitMap(m.units?.away)}}:emptyFrame()}
    function interpAmount(now=performance.now()){return view.duration<=1?1:clamp((now-view.from)/view.duration,0,1)}
    function sampleBall(a,b,t,now){if(!b)return a;if(!a)return b;if(match?.engine?.owner)return{...b,z:0};if(match?.engine?.restart?.type)return{...b,x:lerp(a.x,b.x,t),y:lerp(a.y,b.y,t),z:Math.max(0,lerp(a.z||0,b.z||0,t))};let airPass=match?.engine?.passTarget?.type==="long",ground=!match?.engine?.shot&&!airPass,dx=b.x-a.x,dy=b.y-a.y;if(dx*dx+dy*dy>.09)return ground?{...b,z:0}:b;let over=t>=1&&match?.status==="active"?Math.min(.18,(now-view.from-view.duration)/1000):0;return{...b,x:clamp(lerp(a.x,b.x,t)+(b.vx||0)*over,.02,.98),y:clamp(lerp(a.y,b.y,t)+(b.vy||0)*over,-.16,1.16),z:ground?0:Math.max(0,lerp(a.z||0,b.z||0,t))}}
    function sampleFrame(now=performance.now()){if(!match||view.id!==match.id)return frameFromMatch(match);let t=interpAmount(now),out=emptyFrame();["home","away"].forEach(side=>{let ids=new Set([...Object.keys(view.prev.units[side]||{}),...Object.keys(view.next.units[side]||{})]);ids.forEach(id=>{let a=view.prev.units[side][id],b=view.next.units[side][id]||a;if(!a||!b)return;let dx=b.x-a.x,dy=b.y-a.y;out.units[side][id]=dx*dx+dy*dy>.03?{x:b.x,y:b.y}:{x:lerp(a.x,b.x,t),y:lerp(a.y,b.y,t)}})});out.ball=sampleBall(view.prev.ball,view.next.ball,t,now);return out}
    function showGoalBanner(goal,now){goalBanner={until:now+3300,name:goal?.scorer||"GOAL",team:goal?.team||"",assist:goal?.assist||""}}
    function showFinalScorers(goals,id,managerMatch=false){if(finalMatchId===id&&goalBanner?.final)return;finalMatchId=id;if(finalTimer)clearTimeout(finalTimer);goalBanner={final:true,goals:goals||[],until:performance.now()+FINAL_SCORERS_MS};finalTimer=setTimeout(()=>{if(match?.id===id&&match.status==="finished"){goalBanner=null;renderGoalBanner();returnFromMatch(managerMatch)}},FINAL_SCORERS_MS)}
    function goalAssistLine(g){return g?.assist?`A: ${g.assist}`:""}
    function goalScorerHtml(goals){return (goals||[]).map(g=>`<span class="score-goal"><b>${safe(Math.max(0,Math.round(g.at||0))+"' "+(g.scorer||"Unknown"))}</b>${g.assist?`<small>A: ${safe(g.assist)}</small>`:""}</span>`).join("")}
    function eventLineHtml(e){const text=String(e?.text||""),parts=text.split(" | ASSIST: "),main=parts[0]||text,assist=parts[1]||"";return `<div>${String(e?.t??"").padStart(2,"0")} ${safe(main)}${assist?`<br><small>ASSIST: ${safe(assist)}</small>`:""}</div>`}
    function renderGoalBanner(now=performance.now()){const el=$("goalBanner");if(!el)return;if(!goalBanner||now>goalBanner.until){el.classList.add("hide");el.classList.remove("final");return}el.classList.remove("hide");el.classList.toggle("final",!!goalBanner.final);if(goalBanner.final){let rows=(goalBanner.goals||[]).length?(goalBanner.goals||[]).map(g=>{const assist=goalAssistLine(g);return `<div class="goal-row"><b>${String(Math.max(0,Math.round(g.at||0))).padStart(2,"0")}</b><div>${safe(g.scorer||"Unknown")}${assist?`<br><span class="goal-assist">${safe(assist)}</span>`:""}<br><span>${safe(g.team||"")}</span></div></div>`}).join(""):`<div class="goal-row"><b>FT</b><div>No goals</div></div>`;el.innerHTML=`<div class="goal-word">FULL TIME</div><div class="goal-name">SCORERS</div><div class="goal-list">${rows}</div>`;return}el.innerHTML=`<div class="goal-word">GOAL</div><div class="goal-name">${safe(goalBanner.name)}</div><div class="goal-team">${safe(goalBanner.team)}${goalBanner.assist?`<br><span>A: ${safe(goalBanner.assist)}</span>`:""}</div>`}
    function goalScorerSummary(goals){return (goals||[]).map(g=>`${Math.max(0,Math.round(g.at||0))}' ${g.scorer||"Unknown"}${g.assist?` (A: ${g.assist})`:""}`).join("  |  ")}
    function isManagerMatch(m=match){return !!m?.managerFixture}
    async function returnFromMatch(managerMatch=false){if(matchReturnBusy)return;matchReturnBusy=true;try{setMatch(null);stopPoll();if(managerMatch){setRoute("manager");show("manager");await loadManager(false);replaceAppHistory();return}goMainMenu(true)}finally{matchReturnBusy=false}}
    function setMatch(next){
      const now=performance.now();
      if(!next){stopCrowd();if(finalTimer)clearTimeout(finalTimer);finalTimer=null;finalMatchId=null;match=null;view=emptyView();lastMatchAt=0;goalFx=null;goalBanner=null;renderGoalBanner(now);refView=null;introMatchId=null;introUntil=0;crowdMatchId=null;lineupMatchId=null;lineupUntil=0;return}
      const same=match&&view.id===next.id,old=match,oldScore=same&&old.score?old.score:null;
      let goalChanged=false;
      if(oldScore&&next.score){
        if(next.score[0]>oldScore[0]){goalFx={net:"top",at:now};goalChanged=true}
        else if(next.score[1]>oldScore[1]){goalFx={net:"bottom",at:now};goalChanged=true}
        if(goalChanged){showGoalBanner(next.engine?.lastGoal,now);goalCheerSound();whistleSound()}
      }
      if(same&&next.status==="active"){
        let os=(old?.engine?.shots?.home||0)+(old?.engine?.shots?.away||0),ns=(next?.engine?.shots?.home||0)+(next?.engine?.shots?.away||0),oldShot=old?.engine?.lastShotSeq||0,newShot=next?.engine?.lastShotSeq||0,ot=old?.engine?.lastTouchAt,nt=next?.engine?.lastTouchAt;
        if((next?.engine?.kickoff?.seq||0)>(old?.engine?.kickoff?.seq||0))whistleSound();
        if((next?.engine?.restart?.seq||0)>(old?.engine?.restart?.seq||0))whistleSound();
        if(ns>os||newShot>oldShot)shotSound();
        else if(nt!==null&&nt!==undefined&&nt!==ot)kickSound();
      }
      let ne=(next.events||[]).slice(-1)[0],oe=same?(old.events||[]).slice(-1)[0]:null;
      if(same&&ne&&(!oe||ne.t!==oe.t||ne.text!==oe.text)){
        if(!goalChanged&&/FREE KICK|PENALTY|YELLOW CARD|GOAL/.test(ne.text||""))whistleSound();
        if(/CRIT SHOT/.test(ne.text||""))critSound();
      }
      if(next.status==="finished"){stopPoll();stopCrowd();showFinalScorers(next.engine?.goalScorers||[],next.id,isManagerMatch(next))}
      if(!same){refView=null;introMatchId=null;introUntil=0;crowdMatchId=null;lineupMatchId=null;lineupUntil=0}
      const prev=same?sampleFrame(now):frameFromMatch(next),target=frameFromMatch(next),since=same&&view.lastAt?now-view.lastAt:POLL_MS,duration=next.status==="active"?clamp(since+45,INTERP_MIN,INTERP_MAX):1;
      view={id:next.id,prev,next:target,from:now,duration,lastAt:now};match=next;lastMatchAt=now;
    }
    function teamChoiceSkill(team){const n=Number(team?.baseRating??team?.skill??team?.rating);return Number.isFinite(n)?Math.round(n):""}
    function teamChoiceSkillHtml(team){const skill=teamChoiceSkill(team);return skill===""?"":`<span class="team-choice-skill">S ${safe(skill)}</span>`}
    function selectedTeamHtml(team){return `<span class="team-choice">${teamChoiceSkillHtml(team)}${teamFlag(team)}<span>${safe(team?.name||"CHOOSE NATIONAL LEAGUE TEAM")}</span></span>`}
    function chooseTeam(id){const t=teamChoices.find(x=>x.id===id);if(!t||t.taken)return;$("teamId").value=t.id;$("teamDropdown").innerHTML=selectedTeamHtml(t);$("teamDropdownList").classList.add("hide");msg("teamMsg","")}
    function fillTeams(teams){
      teamChoices=teams||[];
      const input=$("teamId"),button=$("teamDropdown"),list=$("teamDropdownList");
      if(!input||!button||!list)return;
      const selected=teamChoices.find(t=>t.id===input.value&&!t.taken);
      if(selected)button.innerHTML=selectedTeamHtml(selected);
      else{input.value="";button.innerHTML='<span class="team-choice"><span>CHOOSE NATIONAL LEAGUE TEAM</span></span>'}
      list.innerHTML=teamChoices.map(t=>`<button class="team-option ${t.taken?"taken":""}" data-team="${safe(t.id)}" type="button" ${t.taken?"disabled":""}>${teamChoiceSkillHtml(t)}${teamFlag(t)}<b>${safe(t.name)}</b>${t.taken?" TAKEN":""}</button>`).join("");
      list.querySelectorAll("[data-team]").forEach(b=>b.onclick=()=>chooseTeam(b.dataset.team));
    }
    function syncAdminControls(){const isAdmin=me?.name==="jdc",seasonDone=managerSeason?managerSeason.status==="finished":!managerHasSeason;if($("adminMenu"))$("adminMenu").classList.toggle("hide",!isAdmin);if($("adminTraining"))$("adminTraining").classList.toggle("hide",!isAdmin);if($("adminSimSeason")){$("adminSimSeason").classList.toggle("hide",!isAdmin);$("adminSimSeason").disabled=!isAdmin||seasonDone||managerInteractionBusy()}if($("resetGameData"))$("resetGameData").classList.add("hide")}
    function setManagerStateFlags(r={}){managerHasSeason=!!r.managerHasSeason;managerCanContinue=!!(r.managerCanContinue??r.managerHasSeason);managerHasAnySave=!!(r.managerHasAnySave??managerHasAnySave)}
    function syncMenuControls(){const has=!!managerCanContinue;$("continueManagerMode")?.classList.toggle("hide",!has);$("managerMode")?.classList.toggle("hide",has);$("startNewManagerMode")?.classList.toggle("hide",!managerHasAnySave)}
    function currentManagerDivision(){return managerSeason?.divisionName||managerFinance?.divisionName||me?.managerDivisionName||""}
    function stopAppTopMarquee(host=$("appTopWho")){if(!host)return;if(host._marqRaf){cancelAnimationFrame(host._marqRaf);host._marqRaf=0}}
    function setAppTopMarquee(text){const host=$("appTopWho");if(!host)return;text=String(text||"");if(host._marqText===text&&host.querySelector(".app-top-marquee-item"))return;stopAppTopMarquee(host);host._marqText=text;host.textContent="";host.title=text;if(!text)return;const tape=document.createElement("span"),item=document.createElement("span");tape.className="app-top-marquee-track";item.className="app-top-marquee-item";item.textContent=text;tape.append(item);host.appendChild(tape);const fit=()=>{if(!host.isConnected||!host.contains(tape)||host._marqText!==text)return;item.style.fontSize="";item.style.letterSpacing="";item.style.transform="translate3d(0,-50%,0)";const hostW=Math.max(1,host.getBoundingClientRect?.().width||host.clientWidth||0),textW=Math.max(1,item.getBoundingClientRect?.().width||item.scrollWidth||0),base=parseFloat(getComputedStyle(item).fontSize)||18;if(textW>hostW){const scale=Math.max(.62,Math.min(1,(hostW-2)/textW));item.style.fontSize=`${Math.floor(base*scale*10)/10}px`;if(scale<.88)item.style.letterSpacing="0"}};requestAnimationFrame(fit);setTimeout(fit,250)}
    function renderUserTop(){const division=currentManagerDivision(),name=(me?.name||"").toUpperCase(),text=me?.team?`${name} - ${me.team.name}${division?` - ${division}`:""}`:name,appText=division?`${name} - ${division}`:text;["whoFlag","managerWhoFlag","adminWhoFlag","optionsWhoFlag","trainingWhoFlag","savesWhoFlag","appTopFlag"].forEach(id=>{if($(id))$(id).innerHTML=me?.team?teamFlag(me.team):""});[["who",text],["managerWho",text],["adminWho",text],["optionsWho",text],["trainingWho",text],["savesWho",text]].forEach(([id,val])=>{if($(id))$(id).textContent=val});setAppTopMarquee(appText);$("appTopBar")?.classList.toggle("hide",!me)}
    async function state(quiet=false){
      const r=await api("state",{},quiet?{quiet:true}:{});if(!r.ok)return;if(checkLoadedVersion(r.appVersion))return;
      if(!r.match&&match?.status==="finished"&&goalBanner?.final)return;
      fillTeams(r.teams||[]);me=r.user;setManagerStateFlags(r);syncAdminControls();syncMenuControls();managerInvites=r.managerInvites||[];setMatch(r.match);
      if(me){
        renderUserTop();
        if(!me.team){$("pickWho").textContent=me.name.toUpperCase();show("teamPick");syncHistoryFromState();return}
        if(match){show("match");renderMatch();syncHistoryFromState();return}
        if(getRoute()==="manager"){clearManagerView("Loading manager mode");show("manager");try{await loadManager(false)}catch(e){console.error(e);setRoute("");goMainMenu(true);msg("menuMsg","Manager mode could not load. Try again.")}syncHistoryFromState();return}
        show("menu");renderMenuInvite();renderMatch();syncHistoryFromState();return
      }
      setRoute("");renderUserTop();show("auth");syncHistoryFromState()
    }
    function startupError(e){console.error(e);setRoute("");show("auth");msg("authMsg","Could not load page. Try login again.")}
    function speedKmh(p){return Math.round(Number(p?.speedKmh??((p?.stats?.speed??p?.rating??0)*.45))*10)/10}
    function shotKmh(p){return Math.round(Number(p?.shotKmh??((p?.stats?.shooting??p?.rating??0)*1.5))*10)/10}
    function trainingDivisions(){return trainingData?.divisions?.length?trainingData.divisions:[{id:"all",name:"Teams",teams:trainingData?.teams||[]}]}
    function currentTrainingDivision(){const divisions=trainingDivisions();return divisions.find(d=>d.id===trainingDivisionId)||divisions[0]}
    function currentTrainingTeam(){const d=currentTrainingDivision();return (d?.teams||[]).find(t=>t.id===trainingTeamId)||(d?.teams||[])[0]}
    function closeTrainingDropdowns(except=""){if(except!=="division")$("trainingDivisionList")?.classList.add("hide");if(except!=="team")$("trainingTeamList")?.classList.add("hide")}
    function renderTrainingDivisionDropdown(){const divisions=trainingDivisions();if(!divisions.length)return;if(!trainingDivisionId||!divisions.some(d=>d.id===trainingDivisionId))trainingDivisionId=divisions[0].id;const current=currentTrainingDivision(),button=$("trainingDivisionButton"),list=$("trainingDivisionList");if(button)button.innerHTML=`<span class="team-choice"><span>${safe(current?.name||"DIVISION")}</span></span>`;if(list){list.innerHTML=divisions.map(d=>`<button class="team-option ${d.id===trainingDivisionId?"green":""}" data-training-division="${safe(d.id)}" type="button"><b>${safe(d.name)}</b></button>`).join("");list.querySelectorAll("[data-training-division]").forEach(b=>b.onclick=()=>{trainingDivisionId=b.dataset.trainingDivision||"";trainingTeamId="";closeTrainingDropdowns();renderTrainingDivisionDropdown();renderTrainingTeams()})}}
    async function loadTrainingData(){if(trainingData)return trainingData;const r=await api("trainingData");if(!r.ok)return null;trainingData=r.training||{divisions:[],teams:[]};renderTrainingDivisionDropdown();renderTrainingTeams();return trainingData}
    function renderTrainingTeams(){const division=currentTrainingDivision(),teams=division?.teams||[],button=$("trainingTeamButton"),list=$("trainingTeamList");if(!division)return;if(!trainingTeamId||!teams.some(t=>t.id===trainingTeamId))trainingTeamId=teams[0]?.id||"";const current=currentTrainingTeam();if(button)button.innerHTML=`<span class="team-choice">${current?teamFlag(current):""}<span>${safe(current?.name||"TEAM")}</span></span>`;if(list){list.innerHTML=teams.map(t=>`<button class="team-option ${t.id===trainingTeamId?"green":""}" data-training-team="${safe(t.id)}" type="button">${teamFlag(t)} <b>${safe(t.name)}</b></button>`).join("");list.querySelectorAll("[data-training-team]").forEach(b=>b.onclick=()=>{trainingTeamId=b.dataset.trainingTeam||"";closeTrainingDropdowns();renderTrainingTeams();renderTrainingPlayers()})}renderTrainingPlayers()}
    function allTrainingPlayers(){return trainingDivisions().flatMap(d=>(d.teams||[]).flatMap(t=>(t.players||[]).map(p=>({...p,team:t,division:d}))))}
    function selectedTrainingPlayers(){const ids=new Set(trainingSelection);return allTrainingPlayers().filter(p=>ids.has(p.id))}
    function playerMetric(p){return trainingScenario==="shoot"?`${shotKmh(p)} km/h`:`${speedKmh(p)} km/h`}
    function renderTrainingPlayers(){const team=currentTrainingTeam(),list=$("trainingPlayers");if(!list)return;if(!team){list.innerHTML="";renderTrainingSelection();return}const picked=new Set(trainingSelection),stat=trainingScenario==="shoot"?"SHT":"SPD";list.innerHTML=(team.players||[]).map(p=>`<button class="training-player ${picked.has(p.id)?"green":""}" data-player="${safe(p.id)}" type="button"><b>${safe(p.name)}</b><br>${safe(p.role)} ${stat} ${playerMetric(p)}</button>`).join("");list.querySelectorAll("[data-player]").forEach(b=>b.onclick=()=>toggleTrainingPlayer(b.dataset.player));renderTrainingSelection()}
    function renderTrainingSelection(){const box=$("trainingSelected"),speedStart=$("startSpeedTest"),shootStart=$("startShootTest"),players=selectedTrainingPlayers();if(!box)return;box.innerHTML=players.length?players.map(p=>`<span class="training-chip" style="--shirt:${safe(p.shirt||p.team?.shirt||"#fff")};--trim:${safe(p.trim||p.team?.trim||"#111")}">${safe(p.name)} ${playerMetric(p)}</span>`).join(""):(trainingScenario==="shoot"?"Select 1+ players":"Select 2+ players");if(speedStart){speedStart.classList.toggle("hide",trainingScenario!=="speed");speedStart.disabled=players.length<2}if(shootStart){shootStart.classList.toggle("hide",trainingScenario!=="shoot");shootStart.disabled=players.length<1}}
    function setTrainingScenario(mode){trainingScenario=mode;trainingRace=null;trainingShot=null;$("speedScenario").classList.toggle("green",mode==="speed");$("shootScenario").classList.toggle("green",mode==="shoot");$("speedSetup").classList.remove("hide");$("trainingMsg").textContent="";$("trainingResult").textContent="";renderTrainingPlayers();renderTraining()}
    function toggleTrainingPlayer(id){if(!id)return;trainingRace=null;trainingShot=null;const i=trainingSelection.indexOf(id);if(i>=0)trainingSelection.splice(i,1);else trainingSelection.push(id);renderTrainingPlayers();renderTraining()}
    function clearTrainingSelection(){trainingSelection=[];trainingRace=null;trainingShot=null;$("trainingResult").textContent="";renderTrainingPlayers();renderTraining()}
    function startSpeedRace(){const players=selectedTrainingPlayers();if(players.length<2)return;const count=players.length,usable=.78,left=.5-usable/2;trainingLast=0;trainingShot=null;trainingRace={started:performance.now(),done:false,runners:players.map((p,i)=>{let lane=count===1?.5:left+usable*(i/(count-1)),kmh=speedKmh(p),real=kmh>0?105/(kmh/3.6):0;return{...p,x:lane,y:.965,v:0,kmh,realTime:real,finish:null}})};$("trainingPanel").classList.add("hide");$("trainingResult").textContent=""}
    function startShootTest(){const players=selectedTrainingPlayers();if(!players.length)return;const count=players.length,usable=.78,left=.5-usable/2;trainingLast=0;trainingRace=null;trainingShot={started:performance.now(),done:false,shots:players.map((p,i)=>{let lane=count===1?.5:left+usable*(i/(count-1)),kmh=shotKmh(p),duration=clamp(1.72-kmh/150*1.05,.55,1.72);return{...p,x:lane,y:.84,ballX:lane,ballY:.84,ballZ:0,kmh,duration,delay:0,finish:null}})};$("trainingPanel").classList.add("hide");$("trainingResult").textContent="";shotSound()}
    function trainingUnits(){if(trainingRace?.runners?.length)return trainingRace.runners;if(trainingShot?.shots?.length)return trainingShot.shots.map(s=>({...s,y:.84,rating:s.stats?.shooting??s.rating??50}));return selectedTrainingPlayers().map((p,i,arr)=>{let usable=.78,left=.5-usable/2,lane=arr.length===1?.5:left+usable*(i/(arr.length-1||1));return{...p,x:lane,y:trainingScenario==="shoot"?.84:.965,rating:trainingScenario==="shoot"?(p.stats?.shooting??p.rating??50):(p.stats?.speed??p.rating??50)}})}
    function trainingBall(){if(trainingShot?.shots?.length)return{x:-1,y:-1,z:0,vx:0,vy:0,vz:0};return{x:.5,y:.5,z:0,vx:0,vy:0,vz:0}}
    function trainingRenderMatch(){const units=trainingUnits();return{id:"training",status:"training",teams:{home:{name:"TRAINING",shirt:"#ffffff",trim:"#222"},away:{name:"",shirt:"#ffffff",trim:"#222"}},units:{home:units.map((r,i)=>({id:r.id,name:r.name,role:r.role||"ST",rating:r.rating??r.stats?.speed??50,shirt:r.shirt||r.team?.shirt,trim:r.trim||r.team?.trim,x:r.x,y:r.y})),away:[]},ball:trainingBall(),referee:null,engine:{freeKick:null},score:[0,0],events:[]}}
    function updateTrainingRace(now){if(!trainingRace)return;let dt=trainingLast?Math.min(.05,(now-trainingLast)/1000):0;trainingLast=now;if(!dt||trainingRace.done)return;let finished=0;trainingRace.runners.forEach(r=>{if(r.finish!==null){finished++;return}let target=Math.max(.035,r.kmh/45*.31);r.v+=(target-r.v)*Math.min(1,dt*3.2);r.y-=r.v*dt;if(r.y<=.035){r.y=.035;r.finish=(now-trainingRace.started)/1000;finished++}});if(finished===trainingRace.runners.length&&!trainingRace.done){trainingRace.done=true;let rows=[...trainingRace.runners].sort((a,b)=>b.kmh-a.kmh);$("trainingResult").textContent=rows.map((r,i)=>`${i+1}. ${r.name} ${r.kmh.toFixed(1)} km/h`).join("   ")}}
    function updateTrainingShot(now){if(!trainingShot)return;let dt=trainingLast?Math.min(.05,(now-trainingLast)/1000):0;trainingLast=now;if(!dt||trainingShot.done)return;let finished=0;trainingShot.shots.forEach(s=>{let elapsed=(now-trainingShot.started)/1000-s.delay;if(elapsed<0)return;if(s.finish!==null){finished++;return}let t=clamp(elapsed/s.duration,0,1),ease=1-Math.pow(1-t,2.2);s.ballY=.84-(.78*ease);s.ballX=clamp(s.x+Math.sin(t*Math.PI)*(.015+(100-(s.stats?.shotcontrol??s.rating??50))/3200),.04,.96);s.ballZ=Math.sin(t*Math.PI)*(.035+s.kmh/150*.055);if(t>=1){s.ballY=.06;s.ballZ=0;s.finish=elapsed;finished++;shotSound()}});if(finished===trainingShot.shots.length&&!trainingShot.done){trainingShot.done=true;let rows=[...trainingShot.shots].sort((a,b)=>b.kmh-a.kmh);$("trainingResult").textContent=rows.map((s,i)=>`${i+1}. ${s.name} ${s.kmh.toFixed(1)} km/h`).join("   ")}}
    function drawTrainingBall(g,x,y,z,br){let bx=p.x+p.w*x,by=p.y+p.h*y-bz(z),r=br||Math.max(4,p.w*.012);g.fillStyle="rgba(0,0,0,.28)";g.beginPath();g.ellipse(bx,by+r*1.45,r*1.15,r*.35,0,0,7);g.fill();g.fillStyle="#fff";g.strokeStyle="#111";g.lineWidth=1.3;g.beginPath();g.arc(bx,by,r,0,7);g.fill();g.stroke()}
    function bz(z){return(z||0)*p.h*.45}
    function drawTrainingOverlay(){const g=$("field").getContext("2d");if(!p.w)return;g.save();if(trainingRace?.runners?.length){g.strokeStyle="rgba(255,221,69,.9)";g.lineWidth=Math.max(2,p.w*.006);g.beginPath();g.moveTo(p.x,p.y+p.h*.965);g.lineTo(p.x+p.w,p.y+p.h*.965);g.moveTo(p.x,p.y+p.h*.035);g.lineTo(p.x+p.w,p.y+p.h*.035);g.stroke();g.font=Math.max(9,p.w*.025)+"px Arial";g.textAlign="center";g.textBaseline="bottom";trainingRace.runners.forEach(r=>{let x=p.x+p.w*r.x,y=p.y+p.h*r.y;g.fillStyle="#fff";g.strokeStyle="#000";g.lineWidth=3;g.strokeText(`${r.name} ${r.kmh.toFixed(1)}`,x,y-18);g.fillText(`${r.name} ${r.kmh.toFixed(1)}`,x,y-18)})}if(trainingShot?.shots?.length){trainingShot.shots.forEach(s=>{let sx=p.x+p.w*s.x,sy=p.y+p.h*.84;drawTrainingBall(g,s.ballX,s.ballY,s.ballZ);g.font=Math.max(9,p.w*.022)+"px Arial";g.textAlign="center";g.fillStyle="#fff";g.strokeStyle="#000";g.lineWidth=3;g.strokeText(`${s.name} ${s.kmh.toFixed(1)} km/h`,sx,sy-18);g.fillText(`${s.name} ${s.kmh.toFixed(1)} km/h`,sx,sy-18)})}g.restore()}
    function renderTraining(){if(!trainingMode)return;const now=performance.now();if(trainingRace)updateTrainingRace(now);else if(trainingShot)updateTrainingShot(now);$("wait").classList.add("hide");$("hud").classList.add("hide");$("controls").classList.add("hide");$("lineup").classList.add("hide");$("events").innerHTML="";$("goalBanner").classList.add("hide");$("toMenu").classList.remove("hide");const old=match,tm=trainingRenderMatch();match=tm;drawField(tm.teams.home,tm.teams.away,{ball:tm.ball,units:{home:unitMap(tm.units.home),away:{}}});drawTrainingOverlay();match=old}
    function openTrainingMenu(){setRoute("");stopPoll();stopCrowd();setMatch(null);trainingMode=false;renderUserTop();msg("trainingMenuMsg","");show("trainingMenu");pushAppHistory()}
    async function openTraining(mode="speed"){stopPoll();stopCrowd();setMatch(null);trainingMode=true;trainingScenario=mode;trainingLast=0;trainingRace=null;trainingShot=null;show("match");pushAppHistory();$("trainingPanel").classList.remove("hide");$("speedSetup").classList.remove("hide");$("toMenu").textContent="MENU";await loadTrainingData();setTrainingScenario(mode);renderTraining()}
    function closeTraining(){goBackOrMenu()}
    function toggleTrainingMenu(){const p=$("trainingPanel");p.classList.toggle("hide");if(!p.classList.contains("hide")){renderTrainingPlayers();renderTrainingSelection()}}
    function money(v){return "£"+Math.round(Number(v||0)).toLocaleString("en-GB")}
    function signedMoney(v){const n=Math.round(Number(v||0));return `${n<0?"-":""}£${Math.abs(n).toLocaleString("en-GB")}`}
    function shortDate(v){if(!v)return "";const d=new Date(Number(v)*1000);return isNaN(d)?String(v):d.toLocaleString("sv-SE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})}
    function transferInfo(p){if(!p?.purchasedFrom)return "";const when=shortDate(p.purchasedAt),price=p.purchasePrice?` for ${money(p.purchasePrice)}`:"";return `Bought from ${safe(p.purchasedFrom)}${p.purchasedFromDivision?` - ${safe(p.purchasedFromDivision)}`:""}${when?` on ${safe(when)}`:""}${price}`}
    function skillChangeText(c){if(!c)return "";const before=Number(c.before||0),after=Number(c.after||0),delta=Number(c.delta||0),raw=Number(c.rawDelta||0);return delta?`<br>Team skill ${before} -> ${after} (${delta>0?"+":""}${delta})`:`<br>Team skill unchanged at ${after}${raw>0?" (raw skill improved, rounded S stayed the same)":""}`}
    function signingNotice(p,c){const buttons=`<br><button data-open-lineup-after-signing type="button">SET LINEUP</button><button data-dismiss-signing-notice type="button">LATER</button>`;if(!p)return `<b>SIGNING COMPLETE</b>Player joined your squad${buttons}`;const info=transferInfo(p);return `<b>SIGNING COMPLETE</b>${safe(p.name||"Player")} joined your squad${info?`<br>${info}`:""}${skillChangeText(c)}${buttons}`}
    function bindSigningNoticeAction(box){if(!box)return;box.querySelectorAll("[data-open-lineup-after-signing]").forEach(b=>b.onclick=()=>{clearSquadSwap();managerBrowseNotice="";managerMarketNotice="";openManagerTeam(me?.name,{backTarget:"league"})});box.querySelectorAll("[data-dismiss-signing-notice]").forEach(b=>b.onclick=()=>{managerBrowseNotice="";managerMarketNotice="";box.innerHTML="";box.classList.add("hide")})}
    function benchFullText(r){return `Bench is full (max ${Number(r?.maxBench||9)}). Sell or include a player in the deal before buying more.`}
    function normalizedManagerUsers(){return (managerUsers||[]).map(u=>({...u,name:String(u?.name||"")}))}
    function readyManagerUsers(){return normalizedManagerUsers().filter(u=>u.ready)}
    function managerTeamLine(u){return u?.team?`${u.team.name}`:"NO TEAM"}
    function fixtureOddsLine(f){const o=f?.odds;if(!o)return "";const cell=value=>`<span><em class="odds-percent">${safe(Math.round(Number(value||0)))}%</em></span>`;return `<div class="manager-odds"><div class="manager-odds-line fixture-odds-line"><span></span>${cell(o.home)}${cell(o.draw)}${cell(o.away)}</div></div>`}
    function fixtureTeamHtml(team,id,skill){const n=Number(skill),humans=new Set(managerSeason?.humanManagers||[]),isSelf=me?.name&&id===me.name,isHuman=!isSelf&&humans.has(id),cls=isSelf?" fixture-team-self":isHuman?" fixture-team-human":"",skillHtml=Number.isFinite(n)?`<small class="manager-team-skill">(${Math.round(n)})</small>`:"";return `${teamFlag(team)}<span class="manager-team-copy"><span class="manager-team-name${cls}">${safe(team?.name||id)}</span>${skillHtml}</span>`}
    function managerTeamName(user){return managerSeason?.teamsByUser?.[user]?.team?.name||user||"TEAM"}
    function fixtureDecisionText(f){if(f?.status!=="played"||f?.decidedBy!=="PEN")return "";const p=f.penaltyScore,score=Array.isArray(p)?` ${p[0]}-${p[1]}`:"";return `PEN${score} ${managerTeamName(f.winner).toUpperCase()} WINS`}
    function fixtureDecisionLine(f){const text=fixtureDecisionText(f);return text?`<div class="manager-odds">${safe(text)}</div>`:""}
    function managerFixtureHtml(f){let h=managerSeason?.teamsByUser?.[f.home]?.team,a=managerSeason?.teamsByUser?.[f.away]?.team,sc=f.score?`${f.score[0]}-${f.score[1]}`:"VS",status=f.status==="played"?"FT":(f.status==="watching"||f.status==="simulating")?"LIVE":"",statusCls=status==="LIVE"?" live":status==="FT"?" played":" upcoming";return `<div class="manager-fixture"><span class="manager-fixture-status${statusCls}">${status}</span><span class="manager-fixture-team manager-fixture-home">${fixtureTeamHtml(h,f.home,f?.strength?.home)}</span><b class="manager-fixture-score">${sc}</b><span class="manager-fixture-team manager-fixture-away">${fixtureTeamHtml(a,f.away,f?.strength?.away)}</span></div>${fixtureDecisionLine(f)}${fixtureOddsLine(f)}`}
    function managerRoundMeta(r,i,fixture=null){const f=fixture||(r?.fixtures||[]).find(x=>x.status==="pending"||x.status==="watching")||(r?.fixtures||[]).find(x=>x.home===me?.name||x.away===me?.name)||(r?.fixtures||[])[0]||{};return{round:r?.round,roundIndex:i,phase:r?.phase||f.phase||"league",playoff:r?.playoff,cupName:r?.cupName||f.cupName||"",fixture:f}}
    function managerActiveRoundIndex(){const vote=managerSeason?.simVote||managerSeason?.watchVote,vi=Number(vote?.roundIndex);if(Number.isInteger(vi)&&vi>=0&&(managerSeason?.rounds||[])[vi])return vi;for(let i=0;i<(managerSeason?.rounds||[]).length;i++){if((managerSeason.rounds[i].fixtures||[]).some(f=>f.status==="pending"||f.status==="watching"))return i}return-1}
    function managerPendingRound(){const i=managerActiveRoundIndex(),r=(managerSeason?.rounds||[])[i];return r?managerRoundMeta(r,i):null}
    function nextManagerFixture(){const user=me?.name,i=managerActiveRoundIndex(),r=(managerSeason?.rounds||[])[i],vote=effectiveRoundVote();if(!user||!r)return null;if(Number(vote?.roundIndex)===i&&vote.mineReady&&!vote.continueRequired)return null;for(const f of r.fixtures||[]){if(f.status==="pending"&&(f.home===user||f.away===user))return managerRoundMeta(r,i,f)}return null}
    function nextManagerFixturePreview(){const user=me?.name;if(!user)return null;for(let i=0;i<(managerSeason?.rounds||[]).length;i++){const r=managerSeason.rounds[i];for(const f of r.fixtures||[]){if((f.status==="pending"||f.status==="watching")&&(f.home===user||f.away===user))return managerRoundMeta(r,i,f)}}return null}
    function localRoundVote(){
      const humans=new Set(managerSeason?.humanManagers||[]),user=me?.name||"";
      if(!humans.size)return null;
      for(const r of managerSeason?.rounds||[]){
        const pending=(r.fixtures||[]).some(f=>f.status==="pending"||f.status==="watching");
        if(!pending)continue;
        const ready=[],waiting=[],seen=new Set();
        for(const f of r.fixtures||[]){
          [f.home,f.away].forEach(name=>{
            if(!humans.has(name)||seen.has(name))return;
            seen.add(name);
            if(f.status==="played")ready.push(name);else waiting.push(name);
          });
        }
        if(!ready.length&&!waiting.length)return null;
        return{round:r.round,ready,waiting,readyCount:ready.length,neededCount:ready.length+waiting.length,mineReady:ready.includes(user),allReady:waiting.length===0};
      }
      return null;
    }
    function effectiveRoundVote(){return managerSeason?.simVote||managerSeason?.watchVote||localRoundVote()}
    function effectiveSimRoundVote(){return managerSeason?.simVote||localRoundVote()}
    function managerCpuOnlyActionLabel(){const p=managerPendingRound();if(!p)return "FINISH SEASON";const phase=String(p.phase||"").toLowerCase(),round=String(p.round||"").toUpperCase();if(phase==="cup")return "SIM OTHER CUP";if(phase==="playoff"||p.playoff)return round.includes("FINAL")?"FINISH SEASON":"SIM PLAYOFFS";return "FINISH SEASON"}
    function managerNextRoundPayload(meta=null){const m=meta||managerPendingRound()||{},payload={simKey:managerSeason?.simVote?.key||""},roundIndex=Number(m.roundIndex??m.fixture?.roundIndex),phase=String(m.phase||m.fixture?.phase||"");if(Number.isInteger(roundIndex)&&roundIndex>=0)payload.roundIndex=roundIndex;if(phase)payload.roundPhase=phase;const fixtureIndex=Number(m.fixtureIndex??m.fixture?.fixtureIndex);if(Number.isInteger(fixtureIndex)&&fixtureIndex>=0)payload.fixtureIndex=fixtureIndex;return payload}
    function managerRoundContinuePayload(meta=null){const m=meta||managerSeason?.simVote||{},payload={},rawRoundIndex=m.roundIndex??m.fixture?.roundIndex,roundIndex=rawRoundIndex===""||rawRoundIndex===null||rawRoundIndex===undefined?NaN:Number(rawRoundIndex);if(Number.isInteger(roundIndex)&&roundIndex>=0){payload.roundIndex=roundIndex;const round=(managerSeason?.rounds||[])[roundIndex]||{},phase=String(m.roundPhase||m.phase||m.fixture?.phase||round.phase||"");if(phase)payload.roundPhase=phase}const simKey=String(m.simKey||m.key||"");if(simKey)payload.simKey=simKey;return payload}
    function managerRoundLabel(r){return (r.phase||"")==="cup"?(r.cupName||"CUP"):r.playoff?"PLAYOFF":"ROUND"}
    function managerRoundTitle(r){return (r.phase||"")==="cup"?(r.round||r.cupName||"CUP"):`${managerRoundLabel(r)} ${r.round||""}`}
    function managerMatchTypeLine(meta={},fixture=null){const phase=String(meta.phase||fixture?.phase||"league").toLowerCase(),roundText=String(meta.round||fixture?.round||"").trim();if(phase==="cup")return `CUP MATCH${roundText?` | ${roundText}`:""}`;if(phase==="playoff"||meta.playoff||fixture?.playoff)return `PLAYOFF MATCH${roundText?` | ${roundText}`:""}`;if(phase==="friendly")return "FRIENDLY MATCH";if(phase==="training")return "TRAINING MATCH";return `LEAGUE MATCH${roundText?` | ROUND ${roundText}`:""}`}
    function managerMatchCardKey(match){const f=match?.fixture||{};return `${match?.round??""}:${f.roundIndex??""}:${f.fixtureIndex??""}:${f.home??""}:${f.away??""}`}
    function managerLastMatchCard(label,match){if(!match?.fixture)return "";const meta={round:match.round,phase:match.fixture?.phase||"league",playoff:match.fixture?.phase==="playoff"};return `<div class="manager-last"><b><span class="manager-inline-label">${safe(label)}</span> ${safe(managerMatchTypeLine(meta,match.fixture))}</b>${managerFixtureHtml(match.fixture)}</div>`}
    function seasonTeamLine(id,fallbackTeam=null,extraClass=""){const t=fallbackTeam||managerSeason?.teamsByUser?.[id]?.team,cls=extraClass?` ${safe(extraClass)}`:"";return id||t?`<span class="season-team${cls}">${teamFlag(t)}<span>${safe(t?.name||id||"TEAM")}</span></span>`:`<span class="season-team${cls}"><span>None</span></span>`}
    function movementTeamLine(item){const t=item.team||item,method=item.method?`<small>${safe(String(item.method).toUpperCase())}</small>`:"";return `<span class="season-team">${teamFlag(t)}<span>${safe(t?.name||item.user||"TEAM")}</span>${method}</span>`}
    function movementTeamsHtml(items){const rows=(items||[]).map(movementTeamLine);if(!rows.length)return `<span class="season-team"><span>None</span></span>`;if(rows.length===2)return `${rows[0]}<span class="season-report-gap"></span>${rows[1]}`;return rows.join("")}
    function seasonReportRow(label,html,teamClass="season-report-teams"){return `<div class="season-report-row"><div class="season-report-label">${safe(label)}</div><div class="${safe(teamClass)}">${html}</div></div>`}
    function playoffSummaryHtml(){const rows=(managerSeason?.rounds||[]).filter(r=>r.playoff).flatMap(r=>(r.fixtures||[]).map(f=>({round:r.round,...f}))).filter(f=>f.status==="played");if(!rows.length)return "";return `<div class="season-report-section"><b>PLAYOFF MATCHES</b><div class="season-report-list">${rows.map(f=>{const score=f.score?`${f.score[0]}-${f.score[1]}`:"v",decision=fixtureDecisionText(f);return `<div class="season-match"><span class="season-round">${safe(f.round)}</span>${seasonTeamLine(f.home,null,"season-home")}<span class="season-score">${safe(score)}</span>${seasonTeamLine(f.away,null,"season-away")}${decision?`<div class="season-decision">${safe(decision)}</div>`:""}</div>`}).join("")}</div></div>`}
    function cupFinalFixture(c){const winner=String(c?.winner||""),runner=String(c?.runnerUp||""),key=String(c?.key||"");let found=null;(managerSeason?.rounds||[]).forEach(r=>{if((r.phase||"")!=="cup"||(key&&String(r.cupKey||"")!==key))return;(r.fixtures||[]).forEach(f=>{const home=String(f.home||""),away=String(f.away||"");if(f.status==="played"&&winner&&runner&&((home===winner&&away===runner)||(home===runner&&away===winner)))found=f})});return found}
    function cupScoreText(c,f){if(!f?.score)return "v";return String(f.home||"")===String(c?.winner||"")?`${f.score[0]}-${f.score[1]}`:`${f.score[1]}-${f.score[0]}`}
    function cupReportHtml(r){const cups=(r?.cups&&r.cups.length?r.cups:(r?.cup?[r.cup]:[])).filter(c=>c&&c.status==="finished");if(!cups.length)return "";return `<div class="season-report-section"><b>CUPS</b><div class="season-report-list">${cups.map(c=>{const f=cupFinalFixture(c),decision=fixtureDecisionText(f);return `<div class="season-match"><span class="season-round">${safe(c.name||"Cup")}</span>${seasonTeamLine(c.winner,null,"season-home")}<span class="season-score">${safe(cupScoreText(c,f))}</span>${seasonTeamLine(c.runnerUp,null,"season-away")}${decision?`<div class="season-decision">${safe(decision)}</div>`:""}</div>`}).join("")}</div></div>`}
    function seasonPlayerReportLine(p,mode){return `<div class="season-report-row"><span class="season-report-label">${safe(p.role||"")}</span><div><b>${safe(p.name||"Player")}</b> (${safe(p.teamName||p.user||"")}) | ${mode==="work"?"distance "+safe(p.distanceMeters||0)+"m":"avg impact "+safe(p.avgImpact??0)} | games ${safe(p.reports||0)} | touch ${safe(p.touches||0)} | shot ${safe(p.shots||0)} | tackle ${safe(p.tackles||0)}</div></div>`}
    function seasonPlayerReportHtml(r){const pr=r?.playerReport||{},work=pr.workhorses||[],best=pr.best||[];if(!work.length&&!best.length)return "";return `<div class="season-report-section"><b>HUMAN PLAYER REPORT</b>${work.length?`<div class="season-report-section"><b>WORKHORSES</b>${work.map(p=>seasonPlayerReportLine(p,"work")).join("")}</div>`:""}${best.length?`<div class="season-report-section"><b>BEST PLAYERS</b>${best.map(p=>seasonPlayerReportLine(p,"best")).join("")}</div>`:""}</div>`}
    function seasonReportHtml(){const r=managerSeason?.seasonReport;if(managerSeason?.status!=="finished"||!r)return "";const promoted=movementTeamsHtml(r.promoted||[]),relegated=movementTeamsHtml(r.relegatedFromAbove||[]);return `<div class="manager-playoff season-report"><div class="season-report-title">SEASON REPORT</div><div class="season-report-section"><b>SEASON MOVEMENT</b>${seasonReportRow(`Promoted to ${r.promotedToDivision||"division above"}`,promoted,"season-report-teams season-movement-teams")}${seasonReportRow(`Relegated from ${r.relegatedFromDivision||"division above"}`,relegated,"season-report-teams season-movement-teams")}</div>${cupReportHtml(r)}${playoffSummaryHtml()}${seasonPlayerReportHtml(r)}</div>`}
    function statMap(){return [["shooting","SHT"],["passing","PAS"],["tackling","TAC"],["heading","HEA"],["passcontrol","PCL"],["shotcontrol","SCL"],["speed","SPD"],["crit","CRT"],["stamina","STA"],["strength","STR"],["positioning","POS"],["vision","VIS"],["composure","CMP"],["aggression","AGR"],["discipline","DIS"],["crossing","CRS"],["dribbling","DRB"],["workrate","WRK"],["keeping","KEEP"]]}
    function statLine(p){const s=p.stats||{};return statMap().filter(([k])=>s[k]!==undefined).map(([k,l])=>`${l} ${s[k]}`).join(" ")}
    function statChips(p){const s=p.stats||{};return statMap().filter(([k])=>s[k]!==undefined).map(([k,l])=>`<div class="team-stat"><span>${l}</span><span>${safe(s[k])}</span></div>`).join("")}
    function playerDisplayRole(p){return p?.displayRole||p?.naturalRole||p?.role||"-"}
    function playerSlotRole(p){return p?.slotRole||p?.role||p?.naturalRole||"-"}
    function playerRoleTokens(p){const raw=[p?.displayRole,p?.naturalRole,p?.role,p?.slotRole].filter(Boolean).join("/");return [...new Set(String(raw||"-").toUpperCase().split("/").map(r=>r.trim()).filter(Boolean))]}
    function playerPrimaryRole(p){return playerRoleTokens(p)[0]||String(playerSlotRole(p)||"").toUpperCase()||"-"}
    function playerRating(p){return Number(p?.effectiveRating??p?.rating??0)}
    function playerBaseRating(p){return Number(p?.baseRating??p?.rating??p?.effectiveRating??0)}
    function playerFormGeneratedRating(p){const base=playerBaseRating(p),pct=dailyFormPct(p);return pct?Math.max(0,Math.min(100,Math.round(base*(100+pct)/100))):base}
    function formationRatingHtml(p){const base=playerBaseRating(p),pct=dailyFormPct(p),formed=playerFormGeneratedRating(p);return `OVR ${safe(base)}${pct?` (${safe(formed)})<small class="formation-form-ovr ${pct>0?"up":"down"}">${pct>0?"+":""}${safe(pct)}% OVR</small>`:""}`}
    function benchRatingHtml(p){const base=playerBaseRating(p),pct=dailyFormPct(p),formed=playerFormGeneratedRating(p);return `<span>OVR ${safe(base)}${pct?` (${safe(formed)})`:""}</span>${pct?`<em class="bench-form-ovr ${pct>0?"up":"down"}">${pct>0?"+":""}${safe(pct)}% OVR</em>`:""}`}
    function playerFootText(p){const f=String(p?.foot||"").toUpperCase();return f==="L"?"L-foot":f==="R"?"R-foot":f==="B"?"Both feet":""}
    function playerBonusText(p){const b=Number(p?.footBonus||0);return b>0?`OVR +${b}`:b<0?`OVR ${b}`:""}
    function canonicalUiRole(role){const r=String(role||"").toUpperCase().trim(),map={GOALKEEPER:"GK",KEEPER:"GK","LEFT BACK":"LB","RIGHT BACK":"RB","CENTRE BACK":"CB","CENTER BACK":"CB","LEFT MIDFIELD":"LM","RIGHT MIDFIELD":"RM",MIDFIELD:"CM",MIDFIELDER:"CM",FORWARD:"ST",ATTACKER:"ST",STRIKER:"ST",CF:"ST",FW:"ST"};return map[r]||(["GK","LB","RB","CB","LM","RM","CM","ST"].includes(r)?r:"")}
    function uiRolePair(role){const r=canonicalUiRole(role);if(["LB","RB"].includes(r))return "fullback";if(["LM","RM"].includes(r))return "wide-mid";return r}
    function playerPositionPenalty(p){const natural=canonicalUiRole(p?.naturalRole||p?.role||p?.displayRole),slot=canonicalUiRole(p?.slotRole||p?.role||natural);if(natural&&slot&&(natural===slot||uiRolePair(natural)===uiRolePair(slot)))return 0;return Number(p?.positionPenalty||0)}
    function playerMetaText(p){return [playerFootText(p),playerBonusText(p)].filter(Boolean).join(" | ")}
    function dailyFormPct(p){return Number(p?.dailyFormPct||0)}
    function dailyFormLabel(p){const v=dailyFormPct(p);return v?`FORM ${v>0?"+":""}${v}%`:""}
    function dailyFormDot(p,extra=""){const v=dailyFormPct(p);if(!v)return "";const cls=v>0?"up":"down";return `<span class="daily-form-dot ${cls}${extra?` ${safe(extra)}`:""}" title="${safe(dailyFormLabel(p))}"></span>`}
    function dailyFormText(p){const v=dailyFormPct(p);if(!v)return "";const cls=v>0?"up":"down";return `<span class="daily-form-text ${cls}">${safe(dailyFormLabel(p))}</span>`}
    function playerCardInfo(p){const c=p?.matchCards||p?.cards||{};let yellow=Math.max(Number(c.yellow??c.yellows??0),Number(p?.yellowCards??p?.yellows??0)),red=Math.max(Number(c.red??c.reds??0),Number(p?.redCards??p?.reds??0)),secondYellow=!!c.secondYellow||Number(p?.secondYellowReds||0)>0,directRed=!!c.directRed||Number(p?.directReds||0)>0;if(secondYellow){yellow=Math.max(2,yellow);red=Math.max(1,red)}if(yellow>=2){red=Math.max(1,red);if(!directRed)secondYellow=true}return yellow||red?{yellow,red,secondYellow,directRed}:null}
    function playerCardText(p){const c=playerCardInfo(p);if(!c)return "";const parts=[];if(c.yellow)parts.push(`${c.yellow} yellow`);if(c.red)parts.push(c.secondYellow?"red (2 yellows)":"red");return parts.join(", ")}
    function playerCardBadges(p,extra=""){const c=playerCardInfo(p);if(!c)return "";return `<span class="player-card-badges${extra?` ${safe(extra)}`:""}" title="${safe(playerCardText(p))}">${c.yellow?`<span class="player-card-badge yellow">${c.yellow>1?safe(c.yellow):""}</span>`:""}${c.red?`<span class="player-card-badge red">${c.red>1?safe(c.red):""}</span>`:""}</span>`}
    function injuryInfo(p){return p?.injury||null}
    function injuryLabel(p){const i=injuryInfo(p);if(!i)return "";const n=Number(i.remaining||0);return `INJURED ${n} MATCH${n===1?"":"ES"}${i.reason?` - ${i.reason}`:""}`}
    function injuryBadge(p,extra=""){const label=injuryLabel(p);return label?`<span class="injury-badge${extra?` ${safe(extra)}`:""}" title="${safe(label)}">!</span>`:""}
    function injuryText(p){const label=injuryLabel(p);return label?`<span class="injury-text">${safe(label)}</span>`:""}
    function formationStatPreview(p){const s=p.stats||{},role=playerPrimaryRole(p),sets={GK:[["keeping","KEEP"],["passcontrol","PCL"],["speed","SPD"]],ST:[["shooting","SHT"],["shotcontrol","SCL"],["speed","SPD"]],CB:[["tackling","TAC"],["heading","HEA"],["strength","STR"]],LB:[["speed","SPD"],["tackling","TAC"],["crossing","CRS"]],RB:[["speed","SPD"],["tackling","TAC"],["crossing","CRS"]],CM:[["passing","PAS"],["vision","VIS"],["tackling","TAC"]],LM:[["speed","SPD"],["crossing","CRS"],["dribbling","DRB"]],RM:[["speed","SPD"],["crossing","CRS"],["dribbling","DRB"]]};return (sets[role]||[["passing","PAS"],["speed","SPD"],["workrate","WRK"]]).filter(([k])=>s[k]!==undefined).map(([k,l])=>`${l} ${s[k]}`).join(" ")}
    function formationCoord(value,scale,min,max){const n=Number(value);return Math.max(min,Math.min(max,50+(((Number.isFinite(n)?n:.5)*100)-50)*scale))}
    function formationBoardY(value){const n=Number(value),y=Number.isFinite(n)?n:.7,top=.47,bottom=.94;return Math.max(2,Math.min(77,5+((y-top)/(bottom-top))*78-6))}
    function formationSlotYOffset(role){const r=String(role||"").toUpperCase();if(r==="ST")return -4;if(["CB","LB","RB","LWB","RWB"].includes(r))return -7;if(["CM","LM","RM","DM","CDM","AM","CAM"].includes(r))return -4;return -5}
    function shirtStyle(team){return `style="--shirt:${safe(team?.shirt||"#ffffff")};--trim:${safe(team?.trim||"#111111")}"`}
    function shortPlayerName(name){const parts=String(name||"Player").trim().split(/\s+/);return parts.length>1?parts[parts.length-1]:parts[0]}
    function formationNameStyle(name){const len=[...shortPlayerName(name)].length,scale=len>=13?.64:len>=11?.72:len>=9?.82:len>=7?.92:1;return `--formation-name-size:${(13*scale).toFixed(1)}px;--formation-name-mobile-size:${(9.5*scale).toFixed(1)}px`}
    function squadPickAttrs(p,editable,source="list"){return editable?` data-squad-player="${safe(p.id)}" data-squad-source="${safe(source)}" role="button" tabindex="0"`:""}
    function squadPickClass(p,editable,extra=""){const picked=squadSwapFirst&&String(squadSwapFirst)===String(p.id),second=squadSwapSecond&&String(squadSwapSecond)===String(p.id),active=picked||second,penalty=playerPositionPenalty(p)<0,injured=!!injuryInfo(p);return `${extra}${editable?" squad-pick":""}${active?" selected":""}${active&&squadSwapBusy?" swapping":""}${penalty?" position-penalty":""}${injured?" injured":""}`}
    function setSquadPlacementMode(on){squadPlacementMode=!!on;$("managerTeamPage")?.classList.toggle("squad-placement-active",squadPlacementMode)}
    function clearSquadSwap(){squadSwapFirst=null;squadSwapSecond=null;squadSwapBusy=false;setSquadPlacementMode(false)}
    function managerSkillStatKeys(p){const keys=["shooting","passing","tackling","heading","passcontrol","shotcontrol","speed","crit","stamina","strength","positioning","vision","composure","aggression","discipline","crossing","dribbling","workrate"];if(playerPrimaryRole(p)==="GK")keys.push("keeping");return keys}
    function playerSkillPoints(p){const s=p?.stats||{},keys=managerSkillStatKeys(p).filter(k=>s[k]!==undefined);return keys.length?keys.reduce((n,k)=>n+Number(s[k]||0),0):playerRating(p)*8}
    function formationDisplaySkill(players){const list=players||[];if(!list.length)return 0;const raw=list.reduce((n,p)=>n+playerSkillPoints(p),0)*(16/list.length);return Math.max(0,Math.min(100,Math.round(raw/29000*100)))}
    function formationHtml(players,team,editable=false){
      if(!players.length)return "";
      const skill=formationDisplaySkill(players);
      return `<div class="team-formation"><div class="formation-skill-label">S = ${safe(skill)}</div>${players.map(p=>{
        const x=formationCoord(p.homeX??p.x??.5,1.22,5,95),y=formationBoardY(p.homeY??p.y??.5),stats=formationStatPreview(p),role=playerDisplayRole(p),slot=playerSlotRole(p),yOffset=formationSlotYOffset(slot),rating=playerBaseRating(p),formedRating=playerFormGeneratedRating(p),meta=playerMetaText(p),form=dailyFormLabel(p),cards=playerCardText(p),injury=injuryLabel(p),penalty=playerPositionPenalty(p);
        return `<div class="${squadPickClass(p,editable,"formation-player")}"${squadPickAttrs(p,editable,"formation")} title="${safe(p.name||"Player")} | ${safe(role)}${role!==slot?` in ${safe(slot)} slot`:""} | OVR ${safe(rating)}${form?` (${safe(formedRating)})`:""}${injury?` | ${safe(injury)}`:""}${form?` | ${safe(form)}`:""}${cards?` | ${safe(cards)}`:""}${penalty?` | ${safe(penalty)}% position`:""}${meta?` | ${safe(meta)}`:""}${stats?` | ${safe(stats)}`:""}" style="--x:${x}%;--y:${y}%;--formation-y-offset:${yOffset}mm;--shirt:${safe(team?.shirt||"#ffffff")};--trim:${safe(team?.trim||"#111111")}">${injuryBadge(p,"formation-injury-badge")}${dailyFormDot(p,"formation-form-dot")}${playerCardBadges(p,"formation-card-badges")}<span class="formation-head">${safe(slot)}</span><b style="${formationNameStyle(p.name)}">${safe(shortPlayerName(p.name))}</b><span class="formation-rating">${formationRatingHtml(p)}</span>${stats?`<span class="formation-mini">${safe(stats)}</span>`:""}<span class="formation-penalty ${penalty<0?"":"empty"}">${penalty<0?safe(penalty):"&nbsp;"}%</span></div>`
      }).join("")}</div>`
    }
    function squadFilterDefs(){return[{id:"keeper",label:"KEEPER",roles:["GK"]},{id:"backs",label:"DEFENDERS",roles:["CB","LB","RB","LWB","RWB"]},{id:"mid",label:"MID",roles:["CM","LM","RM","DM","CDM","AM","CAM"]},{id:"striker",label:"STRIKER",roles:["ST","CF","LW","RW","LF","RF"]}]}
    function squadFilterButtonsHtml(){return `<div class="squad-filter-row">${squadFilterDefs().map(f=>`<button class="${squadRoleFilter===f.id?"active":""}" data-squad-filter="${safe(f.id)}" type="button">${safe(f.label)}</button>`).join("")}</div>`}
    function squadMatchActionsHtml(editable){
      if(!editable||!managerSeason||managerSeason.status==="finished")return "";
      const own=nextManagerFixture(),preview=own||nextManagerFixturePreview(),pending=managerPendingRound(),squad=`<button id="managerTeamFullSquad" type="button">SQUAD</button>`,vote=effectiveRoundVote(),needsContinue=!!vote?.mineContinuePending,waitNames=(vote?.waiting||[]).map(u=>String(u||"").toUpperCase()).filter(Boolean),mineWaiting=!!(vote&&!vote.allReady&&vote.mineReady&&!needsContinue),waiting=!!(vote&&!needsContinue&&waitNames.length),waitText=waitNames.length?`WAITING FOR ${waitNames.join(", ")}`:"WAITING";
      if(mineWaiting)return `<div class="manager-squad-match-actions"><button id="managerTeamBestLineup" class="green" type="button">BEST</button><button type="button" data-waiting="1" disabled>${safe(waitText)}</button>${squad}</div>`;
      if(preview)return `<div class="manager-squad-match-actions">${own?`<button id="managerTeamWatchMatch" type="button">WATCH</button>`:""}<button id="managerTeamBestLineup" class="green" type="button">BEST</button><button id="managerTeamNextRound" type="button" aria-label="SIM ROUND" title="SIM ROUND">SIM ROUND</button>${squad}</div>`;
      if(needsContinue)return `<div class="manager-squad-match-actions"><button id="managerTeamBestLineup" class="green" type="button">BEST</button><button id="managerTeamRoundContinue" class="green" type="button">CONTINUE</button>${squad}</div>`;
      if(waiting)return `<div class="manager-squad-match-actions"><button id="managerTeamBestLineup" class="green" type="button">BEST</button><button type="button" data-waiting="1" disabled>${safe(waitText)}</button>${squad}</div>`;
      if(pending)return `<div class="manager-squad-match-actions"><button id="managerTeamBestLineup" class="green" type="button">BEST</button><button id="managerTeamCpuRound" class="green" type="button">${safe(managerCpuOnlyActionLabel())}</button>${squad}</div>`;
      return `<div class="manager-squad-match-actions"><button id="managerTeamBestLineup" class="green" type="button">BEST</button>${squad}</div>`
    }
    function squadRoleMatches(p,filter=squadRoleFilter){if(!filter)return true;const def=squadFilterDefs().find(f=>f.id===filter),roles=playerRoleTokens(p);return !!def&&roles.some(role=>def.roles.includes(role))}
    function filteredSquadPlayers(players){return (players||[]).filter(p=>squadRoleMatches(p))}
    function squadRosterButtonHtml(key,label,count){const open=!!squadRosterOpen[key];return `<button class="squad-section-toggle ${open?"open":""}" data-squad-section="${safe(key)}" type="button" aria-expanded="${open?"true":"false"}"><span>${safe(label)}</span><small>${safe(count)}</small></button>`}
    function squadRosterButtonsHtml(starters,bench){return `<div class="squad-roster-row">${squadRosterButtonHtml("start","STARTING",(starters||[]).length)}${squadRosterButtonHtml("bench","BENCH",(bench||[]).length)}</div>`}
    function squadOpenRosterHtml(starters,team,editable,filterName=""){let html="",emptyRole=filterName?filterName.toLowerCase()+" ":"";if(squadRosterOpen.start)html+=`<div class="squad-section open"><div class="team-section-title">STARTING</div>${starters.length?`<div class="team-player-list">${starters.map(p=>managerPlayerHtml(p,"START",team,editable)).join("")}</div>`:`<div class="manager-meta">No ${safe(emptyRole)}starting players</div>`}</div>`;return html}
    function benchTrayPlayerHtml(p,team,editable){const role=playerDisplayRole(p);return `<div class="${squadPickClass(p,editable,"manager-bench-chip")}"${squadPickAttrs(p,editable,"bench")} ${shirtStyle(team)}><span class="bench-chip-role">${safe(role)}</span><b>${injuryBadge(p)}${safe(shortPlayerName(p.name))}</b><small class="bench-chip-rating">${dailyFormDot(p,"bench-form-dot")}${benchRatingHtml(p)}</small></div>`}
    function squadBenchTrayHtml(bench,team,editable){return bench.length?`<div class="manager-bench-tray"><div class="manager-bench-strip">${bench.map(p=>benchTrayPlayerHtml(p,team,editable)).join("")}</div></div>`:""}
    function toggleSquadSection(key){if(!Object.prototype.hasOwnProperty.call(squadRosterOpen,key))return;squadRosterOpen[key]=!squadRosterOpen[key];refreshSquadSetup()}
    function renderManagerTeamSquadList(starters,bench,team,editable){
      const box=$("managerTeamSquadList");if(!box)return;
      setSquadPlacementMode(!!(editable&&squadPlacementMode&&squadSwapFirst));
      squadRoleFilter="";
      squadRosterOpen.start=false;
      squadRosterOpen.bench=false;
      const rosterHtml="",benchTrayHtml=squadBenchTrayHtml(bench,team,editable),matchActions=squadMatchActionsHtml(editable),topMenu=matchActions?`<div class="manager-squad-top-menu">${matchActions}</div>`:"";
      box.innerHTML=(starters.length||bench.length)?`<div class="manager-squad-layout">${topMenu}<div class="manager-formation-pane">${formationHtml(starters,team,editable)}</div><div class="manager-roster-pane">${rosterHtml}${benchTrayHtml}</div></div>`:`<div class="manager-meta">No players</div>`;
      if(squadPlacementMode)requestAnimationFrame(()=>{box.scrollTop=0});
      document.querySelectorAll("#managerTeamPage [data-squad-filter]").forEach(b=>b.onclick=()=>setSquadRoleFilter(b.dataset.squadFilter));
      document.querySelectorAll("#managerTeamPage [data-squad-section]").forEach(b=>b.onclick=()=>toggleSquadSection(b.dataset.squadSection));
      $("managerTeamWatchMatch")?.addEventListener("click",watchManagerMatch);
      $("managerTeamBestLineup")?.addEventListener("click",optimizeManagerLineup);
      $("managerTeamNextRound")?.addEventListener("click",playManagerRound);
      $("managerTeamCpuRound")?.addEventListener("click",playManagerCpuRound);
      $("managerTeamRoundContinue")?.addEventListener("click",continueManagerRound);
      $("managerTeamFullSquad")?.addEventListener("click",renderManagerFullSquadPage);
      updateManagerReadyIndicators();
      setManagerBusy(managerBusy)
    }
    function setSquadRoleFilter(filter){clearSquadSwap();squadRoleFilter=squadRoleFilter===filter?"":filter;const sq=currentTeamDetails?.squad||{},team=currentTeamDetails?.team||{},editable=!!currentTeamDetails?.editable;renderManagerTeamSquadList(sq.starters||[],sq.bench||[],team,editable);if(editable)bindSquadPickers();renderReplacementList()}
    function managerPlayerHtml(p,group,team,editable=false){
      const transfer=transferInfo(p),role=playerDisplayRole(p),rating=playerRating(p),meta=playerMetaText(p),form=dailyFormText(p),injury=injuryText(p),source=group==="BENCH"?"bench":"list";
      return `<div class="${squadPickClass(p,editable,`team-player-card ${group==="BENCH"?"bench":""}`)}"${squadPickAttrs(p,editable,source)} ${shirtStyle(team)}><div class="team-role">${safe(role)}</div><div class="team-player-main"><b>${injuryBadge(p)}${dailyFormDot(p)}${safe(p.name)}</b><small>${safe(group)}${injury?` | ${injury}`:""}${form?` | ${form}`:""}${meta?` | ${safe(meta)}`:""} | Goals ${safe(p.goals||0)} | Value ${money(p.value||0)}${transfer?`<br>${transfer}`:""}</small></div><div class="team-rating">${safe(rating)}<span>OVR</span></div><div class="team-stats">${statChips(p)}</div></div>`
    }
    function fullSquadStarterGroup(p){const role=String(playerSlotRole(p)||playerPrimaryRole(p)||"").toUpperCase();if(role==="GK")return "gk";if(["CB","LB","RB","LWB","RWB"].includes(role))return "def";if(["CM","LM","RM","DM","CDM","AM","CAM"].includes(role))return "mid";return "st"}
    function playerFormAdjustedValue(value,p){const base=Number(value||0),pct=dailyFormPct(p);return pct?Math.max(0,Math.min(100,Math.round(base*(100+pct)/100))):base}
    function fullSquadStatValueHtml(p,value){const base=Number(value||0),pct=dailyFormPct(p);return `${safe(base)}${pct?` <small class="${pct>0?"up":"down"}">(${safe(playerFormAdjustedValue(base,p))})</small>`:""}`}
    function fullSquadRatingHtml(p){const base=playerBaseRating(p),pct=dailyFormPct(p),formed=playerFormGeneratedRating(p);return `OVR ${safe(base)}${pct?` (${safe(formed)})`:""}`}
    function fullSquadStatChips(p){const s=p.stats||{},seen=new Set(statMap().map(([k])=>k));let rows=statMap().filter(([k])=>s[k]!==undefined).map(([k,l])=>`<div class="team-stat"><span>${safe(l)}</span><span>${fullSquadStatValueHtml(p,s[k])}</span></div>`);Object.keys(s).filter(k=>!seen.has(k)).sort().forEach(k=>rows.push(`<div class="team-stat"><span>${safe(k.toUpperCase())}</span><span>${fullSquadStatValueHtml(p,s[k])}</span></div>`));return rows.join("")}
    function fullSquadPlayerRowHtml(p,group,team,index){const key=`${String(p.id||p.name||"player").replace(/[^a-z0-9_-]/gi,"_")}_${index}`,role=playerDisplayRole(p),roleClass=`role-${fullSquadStarterGroup(p)}`,markers=`${dailyFormDot(p)}${playerCardBadges(p)}${injuryBadge(p)}`;return `<div class="manager-full-squad-item" ${shirtStyle(team)}><button class="manager-full-squad-player" data-full-squad-player="${safe(key)}" type="button"><span class="manager-full-squad-role ${safe(roleClass)}">${safe(role)}</span><b><em>${safe(p.name||"Player")}</em>${markers?`<i>${markers}</i>`:""}</b><strong>${fullSquadRatingHtml(p)}</strong></button><div class="manager-full-squad-stats hide" data-full-squad-stats="${safe(key)}"><div class="team-stats">${fullSquadStatChips(p)}</div></div></div>`}
    function bindManagerFullSquadRows(){document.querySelectorAll("[data-full-squad-player]").forEach(btn=>{btn.onclick=()=>{const key=btn.dataset.fullSquadPlayer,stats=document.querySelector(`[data-full-squad-stats="${CSS.escape(key)}"]`);if(!stats)return;stats.classList.toggle("hide");btn.classList.toggle("open",!stats.classList.contains("hide"))}})}
    function renderManagerFullSquadPage(){const details=currentManagerSection()==="managerTeamPage"?currentTeamDetails:null,src=details?.squad||managerSquad||{},team=details?.team||me?.team||{},starters=src.starters||[],bench=src.bench||[],defs=[["gk","GK"],["def","DEFENDERS"],["mid","MIDFIELDERS"],["st","STRIKERS"]];let idx=0,html=defs.map(([id,label])=>{const players=starters.filter(p=>fullSquadStarterGroup(p)===id);return players.length?`<div class="manager-full-squad-section"><div class="team-section-title">${safe(label)}</div>${players.map(p=>fullSquadPlayerRowHtml(p,"START",team,idx++)).join("")}</div>`:""}).join("");if(bench.length)html+=`<div class="manager-full-squad-section"><div class="team-section-title">BENCH</div>${bench.map(p=>fullSquadPlayerRowHtml(p,"BENCH",team,idx++)).join("")}</div>`;$("managerFullSquadList").innerHTML=html||`<div class="manager-meta">No players</div>`;showManagerSection("managerFullSquadPage");bindManagerFullSquadRows()}
    function ownMarketListing(p){return !!(p&&me?.name&&String(p.marketListedByUser||"")===String(me.name)&&p.marketPayoutSettled===false)}
    function managerTransferPlayerHtml(p,action,group,team){
      const isBuy=action==="buy",ownListing=isBuy&&ownMarketListing(p),amount=isBuy?money(p.price||p.value||0):money(managerSaleBasePrice(p)),attr=ownListing?`data-remove-market="${safe(p.id)}"`:isBuy?`data-buy="${safe(p.id)}"`:`data-sell="${safe(p.id)}"`,label=ownListing?"REMOVE":isBuy?"BUY PLAYER":"SELL PLAYER",source=isBuy&&p.sourceTeam?` | ${safe(p.sourceTeam)}${p.sourceDivision?` - ${safe(p.sourceDivision)}`:""}${ownListing?" | LISTED BY YOU":""}`:"",transfer=!isBuy?transferInfo(p):"",role=playerDisplayRole(p),rating=playerRating(p),meta=playerMetaText(p),form=dailyFormText(p),injury=injuryText(p),amountLabel=ownListing?"ASKING":isBuy?"COST":"BASE SELL";
      return `<div class="team-player-card market-player-card ${group==="BENCH"?"bench":""}${injuryInfo(p)?" injured":""}" ${attr} role="button" tabindex="0" ${shirtStyle(team)}><div class="team-role">${safe(role)}</div><div class="team-player-main"><b>${injuryBadge(p)}${dailyFormDot(p)}${safe(p.name)}</b><small>${safe(group)}${source}${injury?` | ${injury}`:""}${form?` | ${form}`:""}${meta?` | ${safe(meta)}`:""} | ${ownListing?"Asking":isBuy?"Price":"Base sell"} ${amount}${transfer?`<br>${transfer}`:""}</small></div><div class="team-rating">${safe(rating)}<span>OVR</span></div><div class="team-stats">${statChips(p)}</div><div class="team-player-action"><b>${safe(label)}</b><span>${amountLabel} ${amount}</span></div></div>`
    }
    function trophyVisualClass(t){const kind=String(t?.visual||t?.kind||"").toLowerCase();if(kind.includes("runner")||kind.includes("silver"))return"medal silver";if(kind.includes("promotion")||kind.includes("medal")||kind.includes("top_scorer")||kind.includes("golden"))return"medal";return"cup"}
    function trophyIconHtml(t){return`<span class="team-trophy-icon ${trophyVisualClass(t)}" aria-hidden="true"><i></i></span>`}
    function trophiesHtml(items){items=items||[];return items.length?`<div class="team-trophies">${items.map(t=>`<div class="team-trophy-row">${trophyIconHtml(t)}<b>${safe(t.seasonYear||"")}</b><div><span>${safe(t.title||"Trophy")}</span><br>${safe(t.detail||"")}</div></div>`).join("")}</div>`:`<div class="team-trophy-empty">No trophies yet</div>`}
    function allSquadPlayers(){return[...(managerSquad?.starters||[]).map(p=>({...p,group:"START"})),...(managerSquad?.bench||[]).map(p=>({...p,group:"BENCH"}))]}
    function sumPlayers(players,key){return players.reduce((n,p)=>n+Number(p[key]||0),0)}
    function economyCard(label,value,cls=""){return `<div class="manager-economy-card ${cls}"><b>${safe(value)}</b><span>${safe(label)}</span></div>`}
    function arenaExpansionHtml(){const f=managerFinance||{},build=f.arenaBuild||null,next=f.arenaNext||null,level=Number(f.arenaLevel||0),max=Number(f.arenaMaxLevel||10),avg=Number(f.arenaAverageAttendance||0),last=Number(f.arenaLastAttendance||0);let action="";if(build){action=`<div class="arena-build-grid"><span><b>LEVEL ${safe(build.level||level+1)}</b>BUILDING</span><span><b>+${Number(build.seats||0).toLocaleString("en-GB")}</b>SEATS</span><span><b>${safe(build.roundsLeft||0)}/${safe(build.roundsTotal||0)}</b>ROUNDS LEFT</span></div>`}else if(next){action=`<div class="arena-build-grid"><span><b>LEVEL ${safe(next.level)}</b>NEXT</span><span><b>+${Number(next.seats||0).toLocaleString("en-GB")}</b>SEATS</span><span><b>${money(next.cost||0)}</b>COST</span><span><b>${safe(next.rounds||0)}</b>ROUNDS</span></div><button id="arenaBuildButton" class="arena-build-action green" type="button">BUILD ARENA</button>`}else{action=`<div class="manager-meta">Arena is fully expanded.</div>`}return `<h3>EXPAND ECONOMY</h3><div class="arena-build-grid"><span><b>${safe(level)}/${safe(max)}</b>ARENA LEVEL</span><span><b>${Number(f.arenaCapacity||0).toLocaleString("en-GB")}</b>SEATS</span><span><b>${avg?avg.toLocaleString("en-GB"):"-"}</b>AVG VISITORS</span><span><b>${last?last.toLocaleString("en-GB"):"-"}</b>LAST VISITORS</span></div>${action}`}
    function bindArenaExpansion(){const b=$("arenaBuildButton");if(b)b.onclick=startArenaExpansion}
    function transferEconomyHtml(){const players=allSquadPlayers(),f=managerFinance||{},squadValue=Number(f.squadValue||0)||sumPlayers(players,"value");return [["BALANCE",money(f.balance||0)],["SPENT",signedMoney(-Number(f.transferSpent||0))],["SQUAD VALUE",money(squadValue)]].map(([label,value])=>`<span><b>${safe(value)}</b>${safe(label)}</span>`).join("")}
    function renderTransferEconomy(id){const box=$(id);if(!box)return;box.classList.toggle("hide",!managerFinance);box.innerHTML=managerFinance?transferEconomyHtml():""}
    function bidStep(base){return Math.max(1,Math.round(Number(base||0)*.05))}
    function managerSaleBasePrice(p){let saleBase=Number(p?.value||0),purchase=Number(p?.purchasePrice||0);if(purchase>0&&purchase<saleBase*.6)saleBase=purchase;return Math.max(25000,Math.round((saleBase*.62)/25000)*25000)}
    function managerSaleChance(base,ask){base=Math.max(1,Number(base||0));const ratio=Number(ask||0)/base;return clamp(Math.round(78-(ratio-1)*95),5,96)}
    function transferOfferSquadPlayers(){return allSquadPlayers().sort((a,b)=>Number(b.value||0)-Number(a.value||0)||playerRating(b)-playerRating(a))}
    function transferOfferPlayerValue(id){const p=transferOfferSquadPlayers().find(x=>String(x.id)===String(id));return p?Number(p.value||0):0}
    function transferOfferPlayerById(id){return transferOfferSquadPlayers().find(x=>String(x.id)===String(id))||null}
    function maxTransferOfferPlayers(){return transferOffer?.kind==="browse"&&String(transferOffer?.teamUser||"").startsWith("cpu:")?3:1}
    function transferOfferSelectedPlayerIds(){return String($("transferOfferPlayerSelect")?.value||"").split(",").map(x=>x.trim()).filter(Boolean)}
    function setTransferOfferSelectedPlayerIds(ids){const input=$("transferOfferPlayerSelect");if(input)input.value=[...new Set((ids||[]).map(String).filter(Boolean))].slice(0,maxTransferOfferPlayers()).join(",")}
    function transferOfferPlayersByIds(ids){return (ids||[]).map(transferOfferPlayerById).filter(Boolean)}
    function transferOfferPlayersValue(ids){return transferOfferPlayersByIds(ids).reduce((sum,p)=>sum+Number(p.value||0),0)}
    function transferOfferPayload(){const ids=transferOfferSelectedPlayerIds(),payload={cashOffer:Math.max(0,Math.round(Number(transferOffer?.cashOffer||0))),offerPlayerId:ids[0]||""};if(transferOffer?.kind==="browse")payload.offerPlayerIds=ids;return payload}
    function closeTransferOfferPlayerMenu(){const list=$("transferOfferPlayerList");if(list)list.classList.add("hide")}
    function transferOfferChoiceHtml(players,maxPlayers=1){players=players||[];if(!players.length)return `<span class="transfer-player-choice"><b>NO PLAYER INCLUDED</b><small>Cash bid only${maxPlayers>1?` | max ${maxPlayers} players`:""}</small></span>`;return `<span class="transfer-player-choice"><b>${players.length===1?safe(players[0].name):`${players.length} PLAYERS INCLUDED`}</b><small>${players.map(p=>safe(p.name)).join(" + ")} | ${money(players.reduce((sum,p)=>sum+Number(p.value||0),0))}</small></span>`}
    function renderTransferOfferPlayerPicker(){const input=$("transferOfferPlayerSelect"),button=$("transferOfferPlayerButton"),list=$("transferOfferPlayerList");if(!input||!button||!list)return;const ids=transferOfferSelectedPlayerIds(),selected=transferOfferPlayersByIds(ids),players=transferOfferSquadPlayers(),maxPlayers=maxTransferOfferPlayers();button.innerHTML=transferOfferChoiceHtml(selected,maxPlayers);list.innerHTML=`<button class="transfer-player-option ${!ids.length?"active":""}" data-transfer-offer-player="" type="button"><b>NO PLAYER INCLUDED</b><small>Cash bid only</small></button>`+players.map(p=>{const active=ids.includes(String(p.id));return `<button class="transfer-player-option ${active?"active":""}" data-transfer-offer-player="${safe(p.id)}" type="button"><b>${safe(p.name)}${active?" | SELECTED":""}</b><small>${safe(p.group||"SQUAD")} | ${safe(playerDisplayRole(p))} | ${money(p.value||0)}</small></button>`}).join("")}
    function chooseTransferOfferPlayer(id){const maxPlayers=maxTransferOfferPlayers();if(!id){setTransferOfferSelectedPlayerIds([]);closeTransferOfferPlayerMenu();renderTransferOfferModal();return}let ids=transferOfferSelectedPlayerIds();if(maxPlayers<=1){setTransferOfferSelectedPlayerIds([id]);closeTransferOfferPlayerMenu();renderTransferOfferModal();return}if(ids.includes(id))ids=ids.filter(x=>x!==id);else if(ids.length<maxPlayers)ids.push(id);else transferOffer.maxPlayerMsg=`Max ${maxPlayers} players in one bid`;setTransferOfferSelectedPlayerIds(ids);renderTransferOfferModal();$("transferOfferPlayerList")?.classList.remove("hide")}
    function renderTransferOfferModal(){
      if(!transferOffer)return;
      const player=transferOffer.player||{},base=Number(transferOffer.basePrice||0),cash=Number(transferOffer.cashOffer||0),selling=transferOffer.kind==="sell",countering=transferOffer.kind==="counter",humanListing=selling&&((managerSeason?.humanManagers||[]).length>1),offerIds=transferOfferSelectedPlayerIds(),maxPlayers=maxTransferOfferPlayers(),offerPlayers=(selling||countering)?[]:transferOfferPlayersByIds(offerIds),offerValue=(selling||countering)?0:transferOfferPlayersValue(offerIds),total=cash+offerValue,balance=Number(managerFinance?.balance||0);
      $("transferBidLabel").textContent=countering?"COUNTER PRICE":selling?"ASKING PRICE":"CASH BID";
      $("transferOfferPlayerLabel").textContent=maxPlayers>1?`PLAYERS TO INCLUDE (MAX ${maxPlayers})`:"PLAYER TO INCLUDE";
      $("transferOfferPlayerLabel").classList.toggle("hide",selling||countering);
      $("transferOfferPlayerPicker").classList.toggle("hide",selling||countering);
      $("transferOfferSubmit").textContent=countering?"SEND COUNTER":selling?(humanListing?"LIST PLAYER":"TRY SELL"):"SEND BID";
      $("transferOfferPlayer").innerHTML=`<b>${safe(player.name||"Player")}</b>${safe(playerDisplayRole(player))} | OVR ${safe(playerRating(player))} | ${countering?"current bid":selling?"base sell":"asking"} ${money(base)}`;
      $("transferBidValue").textContent=money(cash);
      if(countering){
        const old=Number(transferOffer.currentOffer||base);
        $("transferOfferSummary").innerHTML=[["CURRENT",money(old)],["COUNTER",money(cash)],["STATUS","SEND"]].map(([l,v])=>`<div class="transfer-offer-card"><b>${v}</b><span>${l}</span></div>`).join("");
        $("transferOfferMsg").textContent=cash<=old?"Raise above current offer":"Counter offer";
        $("transferOfferSubmit").disabled=cash<=old;
        return;
      }
      if(selling){
        const chance=managerSaleChance(base,cash),hint=cash>base?"Higher price lowers the chance of a deal":cash<base?"Lower price improves the chance of a deal":"Normal asking price";
        if(humanListing){
          $("transferOfferSummary").innerHTML=[["BASE SELL",money(base)],["ASKING",money(cash)],["CPU INTEREST",`${chance}%`]].map(([l,v])=>`<div class="transfer-offer-card"><b>${v}</b><span>${l}</span></div>`).join("");
          $("transferOfferMsg").textContent=hint;
          $("transferOfferSubmit").disabled=false;
          return;
        }
        $("transferOfferSummary").innerHTML=[["BASE SELL",money(base)],["ASKING",money(cash)],["CHANCE",`${chance}%`]].map(([l,v])=>`<div class="transfer-offer-card"><b>${v}</b><span>${l}</span></div>`).join("");
        $("transferOfferMsg").textContent=hint;
        $("transferOfferSubmit").disabled=false;
        return;
      }
      $("transferOfferSummary").innerHTML=[["ASKING",money(base)],["CASH",`${money(cash)} / ${money(balance)}`],["PLAYERS",offerPlayers.length?`${offerPlayers.length}/${maxPlayers} ${money(offerValue)}`:"NONE"],["TOTAL BID",money(total)]].map(([l,v])=>`<div class="transfer-offer-card"><b>${v}</b><span>${l}</span></div>`).join("");
      $("transferOfferMsg").textContent=transferOffer.maxPlayerMsg|| (total<base?"Total bid is below asking price":cash>balance?"Not enough cash balance":"");
      transferOffer.maxPlayerMsg="";
      $("transferOfferSubmit").disabled=cash>balance;
      renderTransferOfferPlayerPicker();
    }
    function openTransferOffer(kind,player,teamUser=""){const base=kind==="sell"?managerSaleBasePrice(player):Number(player?.price||player?.value||0);transferOffer={kind,player,teamUser,basePrice:base,cashOffer:base,maxPlayerMsg:""};$("transferOfferTitle").textContent=kind==="browse"?"MAKE TRANSFER BID":kind==="sell"?"SELL PLAYER":"BUY PLAYER";$("transferOfferPlayerSelect").value="";$("transferOfferMsg").textContent="";closeTransferOfferPlayerMenu();$("transferOfferModal").classList.remove("hide");renderTransferOfferModal()}
    function openDealCounterOffer(id){const o=[...(managerTransferOffers?.incoming||[])].find(x=>String(x.id)===String(id));if(!o)return;const current=Number(o.cashOffer||0),base=Number(o.basePrice||current),cash=Math.max(base,current+bidStep(base||current));transferOffer={kind:"counter",offerId:id,player:o.player||{},basePrice:current,currentOffer:current,cashOffer:cash};$("transferOfferTitle").textContent="COUNTER OFFER";$("transferOfferPlayerSelect").value="";$("transferOfferMsg").textContent="";closeTransferOfferPlayerMenu();$("transferOfferModal").classList.remove("hide");renderTransferOfferModal()}
    function closeTransferOffer(){transferOffer=null;closeTransferOfferPlayerMenu();$("transferOfferModal").classList.add("hide");$("transferOfferMsg").textContent=""}
    function changeTransferBid(dir){if(!transferOffer)return;const min=transferOffer.kind==="sell"?Math.max(1,Math.round(Number(transferOffer.basePrice||0)*.25)):0;transferOffer.cashOffer=Math.max(min,Number(transferOffer.cashOffer||0)+dir*bidStep(transferOffer.basePrice));renderTransferOfferModal()}
    function openMarketTransferOffer(id){const p=(managerMarket||[]).find(x=>String(x.id)===String(id));if(p)openTransferOffer("market",p)}
    async function removeMarketListing(id){if(!id||managerBusy)return;setManagerBusy(true);try{const r=await api("removeMarketListing",{playerId:id});if(!r.ok)return msg("managerMsg",r.error==="listing_locked"?"This player has already been sold":r.error==="forbidden"?"You can only remove your own listing":"Could not remove listing");managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;managerMarket=r.managerMarket||managerMarket;setManagerTransferOffers(r.managerTransferOffers||managerTransferOffers);renderManagerMarketPage();renderManagerSeason();msg("managerMsg","Player removed from market")}finally{setManagerBusy(false)}}
    function openSquadSellOffer(id){const p=allSquadPlayers().find(x=>String(x.id)===String(id));if(p)openTransferOffer("sell",p)}
    function browseOfferPlayerById(id){const sq=managerBrowseTeamDetails?.squad||{};return[...(sq.starters||[]),...(sq.bench||[])].find(p=>String(p.id)===String(id))||null}
    function openBrowseTransferOffer(teamUser,id){const p=browseOfferPlayerById(id);if(p)openTransferOffer("browse",p,teamUser)}
    function managerEconomyPlayerHtml(p,team){const meta=playerMetaText(p);return `<div class="team-player-card ${p.group==="BENCH"?"bench":""}" ${shirtStyle(team)}><div class="team-role">${safe(playerDisplayRole(p))}</div><div class="team-player-main"><b>${safe(p.name)}</b><small>${safe(p.group)}${meta?` | ${safe(meta)}`:""} | Value ${money(p.value||0)} | Wage ${money(p.wage||0)} | Goals ${safe(p.goals||0)}</small></div><div class="team-rating">${safe(playerRating(p))}<span>OVR</span></div><div class="team-stats">${statChips(p)}</div></div>`}
    function managerBrowsePlayerHtml(p,group,team){const amount=money(p.price||p.value||0),source=p.sourceTeam?` | ${safe(p.sourceTeam)}${p.sourceDivision?` - ${safe(p.sourceDivision)}`:""}`:"",role=playerDisplayRole(p),meta=playerMetaText(p);return `<div class="team-player-card market-player-card ${group==="BENCH"?"bench":""}" data-browse-buy="${safe(p.id)}" role="button" tabindex="0" ${shirtStyle(team)}><div class="team-role">${safe(role)}</div><div class="team-player-main"><b>${safe(p.name)}</b><small>${safe(group)}${source}${meta?` | ${safe(meta)}`:""} | Asking ${amount} | Wage ${money(p.wage||0)}</small></div><div class="team-rating">${safe(playerRating(p))}<span>OVR</span></div><div class="team-stats">${statChips(p)}</div><div class="team-player-action"><b>MAKE BID</b><span>BASE ${amount}</span></div></div>`}
    function bindTransferCards(list,attr,handler){list.querySelectorAll(`[${attr}]`).forEach(c=>{const run=()=>handler(c.getAttribute(attr));c.onclick=run;c.onkeydown=e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();run()}}})}
    function managerTeamRecord(userId){const table=managerSeason?.table||[],row=table.find(r=>r.user===userId);return row?{rank:table.indexOf(row)+1,...row}:null}
    function managerDivisionRecord(userId){const d=managerViewDivision,idx=(d?.teams||[]).findIndex(t=>t.id===userId);return idx>=0?{rank:idx+1,divisionName:d.name}:null}
    function currentTeamPlayers(){const sq=currentTeamDetails?.squad||{};return[...(sq.starters||[]).map(p=>({...p,group:"START"})),...(sq.bench||[]).map(p=>({...p,group:"BENCH"}))]}
    function currentTeamPlayer(id){return currentTeamPlayers().find(p=>String(p.id)===String(id))}
    function fallbackRoles(role){role=String(role||"").toUpperCase().split("/")[0];if(role==="GK")return [["GK"],["CB","LB","RB","CM","LM","RM","ST"]];if(["CB","LB","RB"].includes(role))return [[role],["CB","LB","RB"],["CM","LM","RM"],["ST"]];if(["CM","LM","RM","LW","RW"].includes(role))return [[role],["CM","LM","RM"],["ST"],["CB","LB","RB"]];return [[role],["ST"],["CM","LM","RM"],["CB","LB","RB"]]}
    function playerMatchesAnyRole(p,roles){const tokens=playerRoleTokens(p);return tokens.some(r=>roles.includes(r))}
    function replacementCandidates(target){const all=currentTeamPlayers().filter(p=>String(p.id)!==String(target?.id)),targetRole=playerPrimaryRole(target);for(const roles of fallbackRoles(targetRole)){const group=all.filter(p=>playerMatchesAnyRole(p,roles));if(group.length)return group.sort((a,b)=>marketRoleScore(b,targetRole)-marketRoleScore(a,targetRole)||playerRating(b)-playerRating(a))}return all.sort((a,b)=>playerRating(b)-playerRating(a))}
    function renderReplacementList(){const box=$("managerReplacementList");if(!box)return;box.classList.add("hide");box.innerHTML=""}
    function refreshSquadSetup(){const sq=currentTeamDetails?.squad||{},team=currentTeamDetails?.team||{},editable=!!currentTeamDetails?.editable;renderManagerTeamSquadList(sq.starters||[],sq.bench||[],team,editable);if(editable)bindSquadPickers();renderReplacementList()}
    function bindSquadPickers(){const box=$("managerTeamSquadList");box.querySelectorAll("[data-squad-player]").forEach(el=>{const run=()=>pickSquadPlayer(el.getAttribute("data-squad-player"),el.getAttribute("data-squad-source")||"list");el.onclick=run;el.onkeydown=e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();run()}}})}
    async function pickSquadPlayer(id,source="list"){if(!id||currentTeamDetailsUser!==me?.name||squadSwapBusy)return;if(!squadSwapFirst){squadSwapFirst=id;squadSwapSecond=null;setSquadPlacementMode(source!=="formation");refreshSquadSetup();msg("managerMsg",squadPlacementMode?"Choose player on pitch to replace":"");return}if(String(squadSwapFirst)===String(id)){clearSquadSwap();refreshSquadSetup();msg("managerMsg","");return}const first=squadSwapFirst;squadSwapSecond=id;squadSwapBusy=true;refreshSquadSetup();msg("managerMsg","Updating formation...");const r=await api("swapSquadPlayers",{firstId:first,secondId:id});clearSquadSwap();if(!r.ok){refreshSquadSetup();return msg("managerMsg","Could not swap players")}squadRosterOpen.bench=false;managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;await openManagerTeam(currentTeamDetailsUser);renderManagerSeason();msg("managerMsg","Formation updated")}
    function renderFormationControls(d,editable){const old=$("managerFormationControls");if(old){old.classList.add("hide");old.innerHTML=""}const box=$("managerTeamFormationControls");if(!box)return;if(!editable){box.classList.add("hide");box.innerHTML="";return}const current=d.formation||d.squad?.formation||managerSquad?.formation||"4-4-2",options=d.formationOptions||d.squad?.formationOptions||managerSquad?.formationOptions||["4-4-2","5-3-2","4-3-3","4-5-1","3-5-2","4-2-3-1"];box.classList.remove("hide");box.innerHTML=options.map(f=>`<button class="${f===current?"active":""}" data-formation="${safe(f)}" type="button">${safe(f)}</button>`).join("");box.querySelectorAll("[data-formation]").forEach(b=>b.onclick=()=>setManagerFormation(b.dataset.formation))}
    function formationErrorText(r,fallback){return r?.error==="not_enough_fit_players"?`Not enough fit players for a full XI${Number.isFinite(Number(r.fitPlayers))?` (${safe(r.fitPlayers)}/11 fit)`:""}`:fallback}
    async function setManagerFormation(formation){clearSquadSwap();const r=await api("setSquadFormation",{formation});if(!r.ok)return msg("managerMsg",formationErrorText(r,"Could not set formation"));managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;await openManagerTeam(me?.name);renderManagerSeason();msg("managerMsg",`Formation ${formation}`)}
    async function optimizeManagerLineup(){if(managerBusy||currentTeamDetailsUser!==me?.name)return;clearSquadSwap();const formation=currentTeamDetails?.squad?.formation||managerSquad?.formation||"4-4-2";setManagerBusy(true);msg("managerMsg","Optimizing lineup...");try{const r=await api("setSquadFormation",{formation});if(!r.ok)return msg("managerMsg",formationErrorText(r,"Could not optimize lineup"));managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;await openManagerTeam(me?.name,{record:false});renderManagerSeason();msg("managerMsg","Best lineup set")}finally{setManagerBusy(false)}}
    function setManagerTeamInfoOpen(on){managerTeamInfoOpen=!!on;const meta=$("managerTeamMeta"),btn=$("managerTeamInfoToggle");if(meta)meta.classList.toggle("hide",!managerTeamInfoOpen);if(btn){btn.classList.toggle("open",managerTeamInfoOpen);btn.setAttribute("aria-expanded",managerTeamInfoOpen?"true":"false")}}
    function toggleManagerTeamInfo(){setManagerTeamInfoOpen(!managerTeamInfoOpen)}
    async function openManagerTeam(userId,options={}){if(!userId)return;if(currentTeamDetailsUser&&currentTeamDetailsUser!==userId)clearSquadSwap();if(Object.prototype.hasOwnProperty.call(options,"backTarget"))managerTeamBackTarget=options.backTarget||"";else if(currentManagerSection()!=="managerTeamPage")managerTeamBackTarget="";currentTeamDetailsUser=userId;const r=await api("managerTeamDetails",{teamUser:userId});if(!r.ok)return msg("managerMsg","Could not load team");const d=r.teamDetails||{},team=d.team||{},sq=d.squad||{},starters=sq.starters||[],bench=sq.bench||[],record=managerTeamRecord(userId)||managerDivisionRecord(userId),type=d.managerType==="human"?"USER TEAM":"LEAGUE TEAM",division=d.divisionName||managerSeason?.divisionName||managerViewDivision?.name||"",editable=d.managerType==="human"&&userId===me?.name;currentTeamDetails={team,squad:sq,editable,details:d};$("managerTeamTitle").innerHTML=`<div class="team-profile-static"><div class="team-profile-hero">${teamFlag(team)}<div><div class="team-profile-name">${safe(team.name||userId)}</div><div class="team-profile-sub">${safe(type)}${division?` | ${safe(division)}`:""}</div></div></div></div>`;$("managerTeamMeta").innerHTML=`<div class="team-metrics"><div class="team-metric"><b>${record?record.rank:"-"}</b><span>POSITION</span></div><div class="team-metric"><b>${Math.round(Number(d.strength||0))}</b><span>SKILL</span></div><div class="team-metric"><b>${record?.won!==undefined?`${record.won||0}-${record.drawn||0}-${record.lost||0}`:"-"}</b><span>W-D-L</span></div><div class="team-metric"><b>${record?.gf!==undefined?record.gf||0:"-"}</b><span>GOALS</span></div><div class="team-metric"><b>${record?.ga!==undefined?record.ga||0:"-"}</b><span>GOALS AGAINST</span></div><div class="team-metric"><b>${record?.points!==undefined?record.points||0:"-"}</b><span>POINTS</span></div><div class="team-metric"><b>${starters.length+bench.length}</b><span>PLAYERS</span></div></div><div id="managerTeamFormationControls" class="manager-filter-row manager-formation-inline hide"></div>`;setManagerTeamInfoOpen(true);renderFormationControls(d,editable);renderManagerTeamSquadList(starters,bench,team,editable);if(editable){bindSquadPickers();renderReplacementList()}else renderReplacementList();showManagerSection("managerTeamPage",options.record!==false);if(managerTeamBackTarget==="league")$("managerBack").textContent="BACK TO LEAGUE"}
    function backFromManagerTeam(){if(managerTeamBackTarget==="league"){managerTeamBackTarget="";clearSquadSwap();msg("managerMsg","");renderManagerSeason();showManagerSection("managerHome",false);replaceAppHistory();return}goBackOrMenu()}
    function inviteStatusHtml(inv){const ready=(inv.ready||Object.keys(inv.accepted||{})).filter(Boolean),waiting=inv.waiting||[];return `${safe(inv.readyCount??ready.length)}/${safe(inv.neededCount??(inv.users||[]).length)} ready${ready.length?`<br>Ready: ${safe(ready.join(", "))}`:""}${waiting.length?`<br>Waiting: ${safe(waiting.join(", "))}`:""}`}
    function renderInviteTarget(id){const box=$(id);if(!box)return;const inv=(managerInvites||[]).find(x=>x.needsMe)||(managerInvites||[])[0];if(!inv){box.classList.add("hide");box.classList.remove("invite-alert");box.innerHTML="";return}box.classList.remove("hide");box.classList.toggle("invite-alert",!!inv.needsMe);if(inv.needsMe){box.innerHTML=`<b>MANAGER INVITE</b><br>${safe(inv.from).toUpperCase()} invited you to ${safe(inv.divisionName||"a league")}<button class="green" data-accept-invite="${safe(inv.id)}" type="button">LET'S GO</button>`;box.querySelector("[data-accept-invite]").onclick=()=>acceptManagerInvite(inv.id);return}box.innerHTML=`<b>MANAGER INVITE SENT</b><br>${inviteStatusHtml(inv)}`}
    function renderMenuInvite(){renderInviteTarget("inviteBox")}
    function acceptedInviteIds(invites,user){return new Set((invites||[]).filter(inv=>inv&&!inv.needsMe&&(inv.users||[]).includes(user)&&inv.accepted?.[user]).map(inv=>String(inv.id||"")).filter(Boolean))}
    async function openManagerFromStartedInvite(){setRoute("manager");clearManagerView("Loading manager mode");show("manager");await loadManager(false);replaceAppHistory()}
    async function refreshMenuInvite(){if(menuInvitePollBusy||visibleScreen()!=="menu"||!me)return;menuInvitePollBusy=true;try{const acceptedBefore=acceptedInviteIds(managerInvites,me?.name),r=await api("state",{}, {quiet:true});if(!r.ok)return;if(checkLoadedVersion(r.appVersion))return;if(visibleScreen()!=="menu"||!me)return;if(!r.user)return;const nextInvites=r.managerInvites||[],acceptedStillWaiting=nextInvites.some(inv=>acceptedBefore.has(String(inv.id||""))),acceptedInviteStarted=acceptedBefore.size>0&&!acceptedStillWaiting&&!!(r.managerCanContinue??r.managerHasSeason);fillTeams(r.teams||[]);me=r.user;setManagerStateFlags(r);managerInvites=nextInvites;syncAdminControls();syncMenuControls();renderUserTop();renderMenuInvite();if(r.match){setMatch(r.match);show("match");renderMatch();syncHistoryFromState();startPoll();return}if(acceptedInviteStarted){await openManagerFromStartedInvite()}}finally{menuInvitePollBusy=false}}
    function startMenuInvitePoll(){if(menuInvitePoll||!me)return;refreshMenuInvite();menuInvitePoll=setInterval(refreshMenuInvite,MENU_INVITE_POLL_MS)}
    function stopMenuInvitePoll(){if(menuInvitePoll)clearInterval(menuInvitePoll);menuInvitePoll=null;menuInvitePollBusy=false}
    function closeManagerSaveMenu(){const l=$("managerSaveList");if(l)l.classList.add("hide")}
    function managerSaveRows(){return managerSaves.length?managerSaves.map(s=>{const mode=s.modeLabel||String(s.mode||((s.users||[]).length>1?"ONLINE":"SOLO")).toUpperCase(),title=`${mode} | ${s.divisionName||"Manager"} | ${s.seasonYear||""}`.trim();return `<div class="manager-save-row"><button class="manager-save-load" data-manager-save="${safe(s.id)}" type="button"><b>${safe(title)}</b><small>${safe((s.users||[]).join(" + "))} | saved by ${safe(s.savedBy||"-")}<br>${safe(s.match||"Autosave")} ${safe(s.score||"")} | ${safe(s.savedAtText||"")}</small></button>${s.canDelete?`<button class="manager-save-delete orange" data-delete-save="${safe(s.id)}" type="button" aria-label="DELETE SAVE ${safe(s.label||s.file)}">X</button>`:`<span></span>`}</div>`}).join(""):`<div class="manager-save-empty">No manager saves found</div>`}
    function renderManagerSaves(){["managerSaveList","managerSavesList"].forEach(id=>{const list=$(id);if(!list)return;list.innerHTML=managerSaveRows();list.querySelectorAll("[data-manager-save]").forEach(b=>b.onclick=()=>loadManagerSave(b.dataset.managerSave));list.querySelectorAll("[data-delete-save]").forEach(b=>b.onclick=e=>{e.stopPropagation();deleteManagerSave(b.dataset.deleteSave)})})}
    async function refreshManagerSaves(messageId="menuMsg"){const r=await api("managerSaveList");if(!r.ok){managerSaves=[];managerHasAnySave=false;syncMenuControls();renderManagerSaves();return msg(messageId,"Could not load saves")}managerSaves=r.managerSaves||[];managerHasAnySave=!!(r.managerHasAnySave??managerSaves.length);syncMenuControls();renderManagerSaves();msg(messageId,managerSaves.length?"":"No manager saves found")}
    async function openManagerSaves(record=true){stopPoll();stopCrowd();stopManagerPoll();setRoute("");setMatch(null);renderUserTop();managerSaves=[];renderManagerSaves();msg("managerSavesMsg","Loading saves...");show("managerSavesPage");if(record)pushAppHistory();await refreshManagerSaves("managerSavesMsg")}
    async function loadManagerSave(id){const save=managerSaves.find(s=>s.id===id)||{};if(!id)return;if(!await askConfirm("LOAD SAVED GAME",`Load ${save.label||id}?\nYour manager progress in this save will be restored.`,`LOAD`,`CANCEL`,false))return;const r=await api("loadManagerSave",{saveId:id});if(!r.ok)return msg(visibleScreen()==="managerSavesPage"?"managerSavesMsg":"menuMsg",r.error==="missing_users"?`Missing users: ${(r.missingUsers||[]).join(", ")}`:"Could not load save");closeManagerSaveMenu();me=r.user||me;teamChoices=r.teams||teamChoices;managerInvites=r.managerInvites||[];managerSeason=r.managerSeason||null;setManagerStateFlags({managerHasSeason:!!managerSeason,managerCanContinue:r.managerCanContinue??!!managerSeason,managerHasAnySave:r.managerHasAnySave});managerFinance=r.managerFinance||null;managerSquad=r.managerSquad||null;managerMarket=r.managerMarket||[];setManagerTransferOffers(r.managerTransferOffers);setRoute("manager");show("manager");await loadManager(false);replaceAppHistory();msg("managerMsg","Manager save loaded")}
    async function deleteManagerSave(id){const save=managerSaves.find(s=>s.id===id)||{};if(!id)return;if(!await askConfirm("ARE YOU SURE?",`Delete ${save.label||id}?`,`DELETE`,`CANCEL`))return;const r=await api("deleteManagerSave",{saveId:id});if(!r.ok)return msg(visibleScreen()==="managerSavesPage"?"managerSavesMsg":"menuMsg",r.error==="forbidden"?"You cannot delete this save":"Could not delete save");managerSaves=r.managerSaves||[];managerSeason=r.managerSeason||null;setManagerStateFlags({managerHasSeason:!!(r.managerHasSeason??managerSeason),managerCanContinue:r.managerCanContinue??!!managerSeason,managerHasAnySave:r.managerHasAnySave??managerSaves.length});syncMenuControls();renderManagerSaves();msg(visibleScreen()==="managerSavesPage"?"managerSavesMsg":"menuMsg","Save deleted")}
    async function acceptManagerInvite(id){const r=await api("acceptManagerInvite",{inviteId:id});if(!r.ok)return msg("menuMsg",r.error==="duplicate_human_team"?"Two invited users have the same team. Pick different teams for this league.":"Could not accept invite");managerInvites=r.managerInvites||[];managerSeason=r.managerSeason||managerSeason;setManagerStateFlags({managerHasSeason:!!managerSeason,managerCanContinue:r.managerCanContinue??!!managerSeason,managerHasAnySave:r.managerHasAnySave});renderMenuInvite();renderInviteTarget("managerInviteBox");if(managerSeason){setRoute("manager");show("manager");await loadManager();setTimeout(refreshManager,400)}else{if(visibleScreen()==="menu")startMenuInvitePoll();msg("menuMsg","Invite accepted")}}
    function managerScrollSnapshot(){return{season:$("managerSeason")?.scrollTop||0,homeMain:document.querySelector("#managerSeason .manager-home-main")?.scrollTop||0,table:document.querySelector("#managerSeason .manager-table")?.scrollTop||0,division:$("managerDivisionList")?.scrollTop||0}}
    function restoreManagerScroll(s){if(!s)return;const apply=()=>{const season=$("managerSeason"),homeMain=document.querySelector("#managerSeason .manager-home-main"),table=document.querySelector("#managerSeason .manager-table"),division=$("managerDivisionList");if(season)season.scrollTop=s.season||0;if(homeMain)homeMain.scrollTop=s.homeMain||0;if(table)table.scrollTop=s.table||0;if(division)division.scrollTop=s.division||0};apply();requestAnimationFrame(apply)}
    function injuryBlockMessage(r={}){const blocks=r.injuryBlocks||[],players=blocks.flatMap(b=>(b.players||[]).map(p=>`${b.team?.name||b.user}: ${p.playerName}`));if(players.length)return `Move injured starter to bench: ${players.join(", ")}`;const own=r.injuryBlock||managerSeason?.injuryBlock,ownPlayers=own?.blockedStarters||[];return ownPlayers.length?`Move injured starter to bench: ${ownPlayers.map(p=>p.playerName).join(", ")}`:"Move injured starter to bench before match"}
    function handleManagerInjuryBlock(r,messageId="managerMsg"){if(!r?.injuryBlocked)return false;managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;syncManagerActions();renderManagerSeason();msg(messageId,injuryBlockMessage(r));return true}
    function managerInjuryHtml(){const info=managerSeason?.injuryBlock;if(!info)return "";const blocked=info.blockedStarters||[],active=info.active||[],rows=(blocked.length?blocked:active);if(!rows.length)return "";const lines=rows.map(p=>`${safe(p.playerName)} - ${safe(p.reason||"Injured")} (${safe(p.remaining||0)} match${Number(p.remaining||0)===1?"":"es"})`).join("<br>");return `<div class="manager-playoff manager-injury-alert"><b>${blocked.length?"INJURY BLOCK":"INJURIES"}</b><br>${lines}${blocked.length?`<br>Move injured player out of STARTING before match.`:""}</div>`}
    function managerFormArrowHtml(row){const trend=String(row?.formTrend||"");if(trend==="up")return `<span class="manager-form-arrow up" title="Form top +${safe(row.formBonusPct||3)}%" aria-label="Form top +${safe(row.formBonusPct||3)}%"></span>`;if(trend==="down")return `<span class="manager-form-arrow down" title="Form dip ${safe(row.formBonusPct||-3)}%" aria-label="Form dip ${safe(row.formBonusPct||-3)}%"></span>`;return `<span class="manager-form-empty" aria-hidden="true"></span>`}
    function managerWaitingText(names=[]){const mine=String(me?.name||"").toLowerCase(),raw=(names||[]).map(u=>String(u||"").trim()).filter(Boolean),waitingForMe=!!mine&&raw.some(u=>u.toLowerCase()===mine),others=raw.filter(u=>!mine||u.toLowerCase()!==mine).map(u=>u.toUpperCase());if(waitingForMe&&!others.length)return "For you";const otherText=others.length?`${others.length===1?"user":"users"} ${others.join(", ")}`:"";if(waitingForMe&&otherText)return `For you and ${otherText}`;return otherText?`For ${otherText}`:""}
    function renderManagerSeason(){
      const box=$("managerSeason");if(!box)return;
      const scroll=managerScrollSnapshot();
      if(!managerSeason){box.classList.remove("hide");box.innerHTML=`<div class="manager-meta">No active manager league. Start a new league from Manager Menu.</div>`;return}
      if(managerSeason.status==="finished")managerBusy=false;
      const rules=managerSeason.promotionRules||{},direct=Number(rules.direct||0),ps=Number(rules.playoffStart||0),pe=Number(rules.playoffEnd||0),relegation=Number(rules.relegation||0),tableRows=managerSeason.table||[],relegationStart=tableRows.length-relegation+1;
      const humans=new Set(managerSeason.humanManagers||[]),table=tableRows.map((r,i)=>{let rank=i+1,isHuman=humans.has(r.user),isDirect=rank<=direct&&direct>0,isPlayoff=ps&&rank>=ps&&rank<=pe,isRelegation=relegation>0&&rank>=relegationStart,nationalLeagueBye=direct===1&&ps===2&&pe===7&&(rank===2||rank===3),cls=isDirect?" direct":(isPlayoff?` playoff${nationalLeagueBye?" playoff-bye league-orange":""}`:"");if(isRelegation)cls+=" relegation";return `<div class="manager-row${cls}"><span>${rank}</span><span><button class="manager-team-link" data-manager-team="${safe(r.user)}" type="button">${teamFlag(r.team)} ${isHuman?`<span class="human-dot" title="Human manager">H</span>`:""}<span class="manager-team-name">${safe(r.team?.name||r.user)}</span></button></span><span>${Math.round(Number(r.strength||0))}</span><span>${r.played||0}</span><span>${r.won||0}</span><span>${r.drawn||0}</span><span>${r.lost||0}</span><span>${r.gf||0}</span><span>${r.ga||0}</span><span>${r.points||0}</span><span class="manager-form-cell">${managerFormArrowHtml(r)}</span></div>`}).join("");
      const upcoming=nextManagerFixture(),previewNext=upcoming||nextManagerFixturePreview(),pendingRound=managerPendingRound(),roundVote=effectiveRoundVote(),hasRoundWait=roundVote&&!upcoming&&(roundVote.waiting||[]).length,nextMatch=previewNext?`<div class="manager-last"><b><span class="manager-inline-label">NEXT:</span> ${safe(managerMatchTypeLine(previewNext,previewNext.fixture))}</b>${managerFixtureHtml(previewNext.fixture)}</div>`:pendingRound&&!hasRoundWait?`<div class="manager-last"><b>${safe(managerCpuOnlyActionLabel())}</b><div class="manager-match-type">${safe(managerMatchTypeLine(pendingRound,pendingRound.fixture))}</div>${managerFixtureHtml(pendingRound.fixture)}</div>`:"";
      const ownLastKey=managerMatchCardKey(managerSeason.lastMatch),otherLastMatches=Object.values(managerSeason.humanLastMatches||{}).filter(m=>m?.fixture&&String(m.user||"")!==String(me?.name||"")&&managerMatchCardKey(m)!==ownLastKey);
      const last=`${managerLastMatchCard("LAST:",managerSeason.lastMatch)}${otherLastMatches.map(m=>managerLastMatchCard(`${String(m.user||"USER").toUpperCase()} LAST:`,m)).join("")}`;
      const liveSimRows=managerSeason.liveSimulations||[],liveSims=liveSimRows.map(m=>m?.fixture?`<div class="manager-last"><b><span class="manager-inline-label">LIVE:</span> ${safe(managerMatchTypeLine({round:m.round,phase:m.fixture?.phase||"league",playoff:m.fixture?.phase==="playoff"},m.fixture))}</b>${managerFixtureHtml(m.fixture)}</div>`:"").join("");
      const playoff=(managerSeason.playoffActive||managerSeason.status==="playoff")?`<div class="manager-playoff"><b>PLAYOFF NOW</b><br>${safe(rules.text||"")}</div>`:"";
      const vote=roundVote,voteMode=vote?.mode==="watch"?"WATCH":"ROUND",readyWord="READY",showRoundWait=vote&&!vote.allReady&&Number(vote.readyCount||0)>0,readyLines=(vote?.ready||[]).map(u=>`<div class="manager-waiting-ready">USER ${safe(String(u).toUpperCase())} ${safe(readyWord)}</div>`).join(""),waitingText=managerWaitingText(vote?.waiting||[]),simVote=showRoundWait?`<div class="manager-playoff manager-waiting-card"><div class="manager-waiting-title">${safe(voteMode)} WAITING</div><div class="manager-waiting-round">Round ${safe(vote.round||"")} <span>|</span> ${safe(vote.readyCount||0)}/${safe(vote.neededCount||0)} ${safe(readyWord.toLowerCase())}</div>${readyLines}${waitingText?`<div class="manager-waiting-for"><span>Waiting: <strong>${safe(waitingText)}</strong></span></div>`:""}</div>`:"";
      const injuries=managerInjuryHtml();
      const seasonYear=managerSeason.seasonYear||2026,startBalance=managerSeason.startBalance,startBalanceText=startBalance===null||startBalance===undefined?"":` (Start ${money(startBalance)})`;
      const finance=managerFinance?`<div class="manager-meta">Season ${safe(seasonYear)} | Balance ${money(managerFinance.balance)}${startBalanceText}</div>`:"";
      const humanOutcomes=Object.entries(managerSeason.outcome||{}).filter(([u])=>(managerSeason.humanManagers||[]).includes(u));
      const outcome=managerSeason.status==="finished"?`<div class="manager-meta manager-outcome">${humanOutcomes.map(([u,o])=>`${safe(u)} ${o==="promoted"?"promoted":o==="relegated"?"relegated":"stayed"}`).join(" | ")}</div>`:"";
      const movement=seasonReportHtml();
      const continueWait=(managerSeason.continueWait||[]).filter(Boolean),continueWaiting=managerSeason.status==="finished"&&continueWait.length>0,continueText=continueWaiting?`WAITING FOR ${continueWait.map(u=>String(u).toUpperCase()).join(", ")}`:"NEXT SEASON";
      const waitForSeasons=continueWaiting?`<div class="manager-playoff"><b>WAITING FOR SEASON FINISH</b><br>${safe(continueWait.join(", "))} must finish this season before next season can start.</div>`:"";
      const needsRoundContinue=!!vote?.mineContinuePending,roundWaiting=hasRoundWait&&!needsRoundContinue;
      const leagueSquad=`<button id="managerLeagueSquad" type="button">SQUAD</button>`;
      const simMatch=roundWaiting?`<button id="managerNextRound" type="button" data-waiting="1" disabled>SIM MATCH</button>`:previewNext?`<button id="managerNextRound" type="button" aria-label="SIM MATCH" title="SIM MATCH">SIM MATCH</button>`:needsRoundContinue?`<button id="managerNextRound" class="green" type="button" data-manager-sim-action="continue" aria-label="SIM MATCH" title="SIM MATCH">SIM MATCH</button>`:pendingRound?`<button id="managerNextRound" class="green" type="button" data-manager-sim-action="cpu" aria-label="SIM MATCH" title="SIM MATCH">SIM MATCH</button>`:"";
      const next=managerSeason.status==="finished"?`<button id="continueManagerSeason" class="green" type="button" data-waiting="${continueWaiting?1:0}" ${continueWaiting?"disabled":""}>${safe(continueText)}</button>`:`${simMatch}${leagueSquad}`;
      const abort="";
      const abortConfirm=managerAbortConfirm?`<div class="manager-confirm"><b>ARE YOU SURE?</b><div class="row"><button id="abortManagerYes" class="orange" type="button">YES</button><button id="abortManagerNo" type="button">NO</button></div></div>`:"";
      box.classList.remove("hide");
      const tableBlock=liveSimRows.length?`<div class="manager-table-panel"><div class="manager-table-title">LEAGUE TABLE</div><div class="manager-meta">League table updates when live sim ends</div></div>`:`<div class="manager-table-panel"><div class="manager-table-title">LEAGUE TABLE</div><div class="manager-table"><div class="manager-row head"><span>#</span><span>TEAM</span><span>$</span><span>P</span><span>W</span><span>D</span><span>L</span><span>GF</span><span>GA</span><span>PTS</span><span>F</span></div>${table}</div></div>`;
      const homeMain=`${waitForSeasons}${simVote}${liveSims}${injuries}${nextMatch}${finance}<div class="manager-button-row manager-main-actions">${next}${abort}</div>${last}${playoff}${outcome}${movement}${abortConfirm}`;
      box.innerHTML=`<div class="manager-home-layout"><div class="manager-home-main">${homeMain}</div>${tableBlock}</div>`;
      box.querySelectorAll("[data-manager-team]").forEach(b=>b.onclick=()=>openManagerTeam(b.dataset.managerTeam));
      $("managerNextRound")?.addEventListener("click",managerMainSimClick);
      $("managerWatchMatch")?.addEventListener("click",watchManagerMatch);
      $("managerRoundContinue")?.addEventListener("click",continueManagerRound);
      $("managerLeagueSquad")?.addEventListener("click",()=>openManagerTeam(me?.name));
      $("managerCpuRound")?.addEventListener("click",playManagerCpuRound);
      $("continueManagerSeason")?.addEventListener("click",continueManagerSeason);
      $("abortManagerLeague")?.addEventListener("click",showAbortManagerConfirm);
      $("abortManagerYes")?.addEventListener("click",abortManagerLeague);
      $("abortManagerNo")?.addEventListener("click",()=>{managerAbortConfirm=false;renderManagerSeason()});
      updateManagerReadyIndicators();
      setManagerBusy(managerBusy);
      restoreManagerScroll(scroll);
    }
    function managerSections(){return["managerHome","managerCreate","managerSquadPage","managerFullSquadPage","managerMarketPage","managerEconomyPage","managerTransfersPage","managerBrowsePage","managerBrowseTeamPage","managerDivisionPage","managerScorersPage","managerAssistsPage","managerReportsPage","managerCupsPage","managerSimPage","managerFixturesPage","managerViewTeamsPage","managerStatsInfoPage","managerTrophiesPage","managerTeamPage"]}
    function syncManagerActionVisibility(){const action=$("managerActionMenu"),visible=visibleScreen()==="manager"&&!!managerSeason;if(action)action.classList.toggle("hide",!visible);if(!visible)closeManagerActionMenu()}
    function showManagerSection(id,record=true){if(id!=="managerSimPage"&&typeof stopManagerSimAnimation==="function")stopManagerSimAnimation();managerSections().forEach(x=>$(x).classList.toggle("hide",x!==id));syncManagerActionVisibility();const back=$("managerBack");if(back)back.classList.add("hide");closeManagerMenus();syncManagerMenuMusic();if(record&&visibleScreen()==="manager")pushAppHistory()}
    function currentManagerSection(){return managerSections().find(id=>$(id)&&!$(id).classList.contains("hide"))||""}
    function clearManagerView(text=""){managerSections().forEach(x=>$(x).classList.add("hide"));$("managerActionMenu")?.classList.add("hide");$("managerBack")?.classList.add("hide");closeManagerMenus();if(String(text||"").toLowerCase().includes("loading manager"))setManagerMessage("",false);else managerStatus(text,true)}
    function setManagerActionMenuOpen(open){const m=$("managerActionMenu"),l=$("managerActionList"),b=$("managerActionButton"),screen=$("manager");if(!m||!l)return;m.classList.toggle("open",!!open);l.classList.toggle("hide",!open);if(screen)screen.classList.toggle("manager-menu-open",!!open);if(b)b.setAttribute("aria-expanded",open?"true":"false")}
    function closeManagerActionMenu(){setManagerActionMenuOpen(false)}
    function closeManagerMenus(){closeManagerActionMenu()}
    function managerVoteNames(vote){return (vote?.ready||[]).map(u=>String(u||"").toUpperCase()).filter(Boolean)}
    function updateReadyButton(id,vote,base){const b=$(id);if(!b)return;const names=managerVoteNames(vote),waiting=(vote?.waiting||[]).map(u=>String(u||"").toUpperCase()).filter(Boolean),on=!!(vote&&!vote.allReady&&names.length),mineWaiting=on&&b.dataset.waiting==="1"&&vote.mineReady&&waiting.length;b.classList.toggle("manager-ready-waiting",on);b.textContent=mineWaiting?`WAITING FOR ${waiting.join(", ")}`:(on?`${base} ${safe(vote.readyCount||names.length)}/${safe(vote.neededCount||names.length)} READY`:base);b.title=mineWaiting?`Waiting for ${waiting.join(", ")}`:(on?`${base}: ${names.join(", ")} READY`:base)}
    function updateManagerReadyIndicators(){const simVote=effectiveSimRoundVote();updateReadyButton("managerNextRound",simVote,"SIM MATCH");updateReadyButton("managerTeamNextRound",simVote,"SIM ROUND");updateReadyButton("managerSimSeasonButton",managerSeason?.simSeasonVote,"SIM SEASON");updateReadyButton("adminSimSeason",managerSeason?.simSeasonVote,"SIMULATE WHOLE SEASON")}
    function setManagerTransferOffers(offers){managerTransferOffers={incoming:[...(offers?.incoming||[])],outgoing:[...(offers?.outgoing||[])],notices:[...(offers?.notices||[])]};const latest=(managerTransferOffers.notices||[])[0];if(latest&&currentManagerSection()==="managerBrowseTeamPage"){managerBrowseNotice=dealNoticeHtml(latest);const n=$("managerBrowseNotice");if(n){n.innerHTML=managerBrowseNotice;n.classList.remove("hide")}}syncManagerActions();if(currentManagerSection()==="managerTransfersPage")renderManagerDealOffers()}
    function hasDealAlerts(){return (managerTransferOffers?.incoming||[]).length>0||(managerTransferOffers?.notices||[]).length>0}
    function managerInteractionBusy(){return managerBusy&&managerSeason?.status!=="finished"}
    function setManagerBusy(on){managerBusy=!!on;const busy=managerInteractionBusy();["managerNextRound","managerTeamNextRound","managerCpuRound","managerTeamCpuRound","managerRoundContinue","managerTeamRoundContinue","managerTeamBestLineup","managerTeamFullSquad","managerSimSeasonButton","adminSimSeason","managerWatchMatch","managerTeamWatchMatch","continueManagerSeason","abortManagerLeague","startManagerLeague","cancelManagerCreate","managerActionButton","arenaBuildButton"].forEach(id=>{const b=$(id);if(b)b.disabled=busy||b.dataset.waiting==="1"});document.querySelectorAll("[data-manager-action],[data-manager-view-team]").forEach(b=>b.disabled=busy||b.hidden||b.dataset.waiting==="1");syncAdminControls();syncManagerMenuMusic()}
	    function syncManagerActions(){
	      const active=!!managerSeason&&managerSeason.status!=="finished",finished=!!managerSeason&&managerSeason.status==="finished";
	      syncManagerActionVisibility();
	      [["managerLeagueOption",!!managerSeason],["managerContinueOption",finished],["managerCreateOption",!managerSeason],["managerTopScorersOption",!!managerSeason],["managerDivisionsOption",!!managerSeason],["managerCupsOption",!!managerSeason],["managerReportsOption",!!managerSeason],["managerFixturesOption",!!managerSeason],["managerEconomyOption",!!managerSeason],["managerTransfersOption",!!managerSeason],["managerLineupOption",!!managerSeason],["managerTrophiesOption",!!managerSeason],["managerStatsInfoOption",!!managerSeason],["managerOptionsOption",!!managerSeason],["managerAbortOption",active]].forEach(([id,on])=>{const o=$(id);if(o){o.hidden=!on;o.disabled=!on}});
      const wait=(managerSeason?.continueWait||[]).filter(Boolean),continueOption=$("managerContinueOption");
      if(continueOption){continueOption.dataset.waiting=wait.length?"1":"0";continueOption.disabled=managerInteractionBusy()||continueOption.hidden||wait.length>0;continueOption.textContent=wait.length?`WAITING FOR ${wait.map(u=>String(u).toUpperCase()).join(", ")}`:"CONTINUE TO NEXT SEASON"}
      const dealAlert=hasDealAlerts();
      $("managerActionButton")?.classList.toggle("manager-deal-alert",dealAlert);
      $("managerTransfersOption")?.classList.toggle("manager-deal-alert",dealAlert);
	      $("transfersDeals")?.classList.toggle("manager-deal-alert",dealAlert);
	      updateManagerReadyIndicators();
	    }
	    async function refreshManagerSectionAfterUpdate(section=currentManagerSection(),teamUser=currentTeamDetailsUser){
	      if(section==="managerTeamPage"&&teamUser){await openManagerTeam(teamUser,{record:false});return true}
	      return false
	    }
	    function renderManagerSquadPage(){const list=$("managerSquadList"),starters=managerSquad?.starters||[],bench=managerSquad?.bench||[],team=me?.team||{},role=String(managerSquadSellRoleFilter||"").toUpperCase();renderManagerSquadSellFilters();if(role){const players=sortedSquadSellPlayers(starters,bench);list.innerHTML=players.length?`<div class="team-section-title">${safe(role)} PLAYERS</div>${players.map(p=>managerTransferPlayerHtml(p,"sell",p.group||"PLAYER",team)).join("")}`:`<div class="manager-meta">No ${safe(role)} players in your squad</div>`}else{list.innerHTML=(starters.length||bench.length)?`${starters.length?`<div class="team-section-title">STARTING</div>${starters.map(p=>managerTransferPlayerHtml(p,"sell","START",team)).join("")}`:""}${bench.length?`<div class="team-section-title">BENCH</div>${bench.map(p=>managerTransferPlayerHtml(p,"sell","BENCH",team)).join("")}`:""}`:`<div class="manager-meta">No players</div>`}bindTransferCards(list,"data-sell",openSquadSellOffer)}
    function marketRoles(){return["ALL","GK","CB","LB","RB","CM","LM","RM","ST"]}
    function playerStatNum(p,k){return Number(p?.stats?.[k]??0)}
    function marketRoleScore(p,role){const r=String(role||"").toUpperCase(),rating=playerRating(p),s=k=>playerStatNum(p,k);if(!r)return rating+s("composure")*.08+s("positioning")*.08+s("workrate")*.05;const score={GK:s("keeping")*2.6+s("passcontrol")*.7+s("shotcontrol")*.45+s("positioning")*.55+s("composure")*.45+s("speed")*.2,CB:s("tackling")*1.45+s("heading")*1.1+s("strength")*.9+s("positioning")*.9+s("discipline")*.55+s("passing")*.25,LB:s("speed")*1.05+s("stamina")*.75+s("crossing")*.9+s("tackling")*.8+s("passing")*.45+s("workrate")*.65,RB:s("speed")*1.05+s("stamina")*.75+s("crossing")*.9+s("tackling")*.8+s("passing")*.45+s("workrate")*.65,CM:s("passing")*1.2+s("vision")*1.05+s("positioning")*.8+s("passcontrol")*.75+s("tackling")*.55+s("workrate")*.55+s("composure")*.65,LM:s("speed")*1.0+s("crossing")*1.1+s("dribbling")*.9+s("passing")*.65+s("stamina")*.55+s("workrate")*.45,RM:s("speed")*1.0+s("crossing")*1.1+s("dribbling")*.9+s("passing")*.65+s("stamina")*.55+s("workrate")*.45,ST:s("shooting")*1.55+s("shotcontrol")*1.1+s("speed")*.75+s("heading")*.65+s("composure")*.85+s("positioning")*.8+s("crit")*.35}[r];return (score??rating)+rating*.55}
    function playerMatchesRole(p,role){role=String(role||"").toUpperCase();return !role||playerRoleTokens(p).includes(role)}
    function sortedSquadSellPlayers(starters,bench){let players=[...(starters||[]).map(p=>({...p,group:"START"})),...(bench||[]).map(p=>({...p,group:"BENCH"}))],role=String(managerSquadSellRoleFilter||"").toUpperCase();if(role)players=players.filter(p=>playerMatchesRole(p,role));players.sort((a,b)=>marketRoleScore(b,role)-marketRoleScore(a,role)||playerRating(b)-playerRating(a)||Number(b.value||0)-Number(a.value||0));return players}
    function renderManagerSquadSellFilters(){const box=$("managerSquadFilters");if(!box)return;box.innerHTML=marketRoles().map(r=>`<button class="${(managerSquadSellRoleFilter||"")===(r==="ALL"?"":r)?"active":""}" data-squad-sell-role="${r==="ALL"?"":r}" type="button">${safe(r)}</button>`).join("");box.querySelectorAll("[data-squad-sell-role]").forEach(b=>b.onclick=()=>{managerSquadSellRoleFilter=b.dataset.squadSellRole||"";renderManagerSquadPage()})}
    function sortedMarketPlayers(){let players=[...(managerMarket||[])],role=String(managerMarketRoleFilter||"").toUpperCase();if(role)players=players.filter(p=>playerMatchesRole(p,role));players.sort((a,b)=>marketRoleScore(b,role)-marketRoleScore(a,role)||playerRating(b)-playerRating(a)||Number(b.price||b.value||0)-Number(a.price||a.value||0));return players}
    function renderManagerMarketFilters(){const box=$("managerMarketFilters");if(!box)return;box.innerHTML=marketRoles().map(r=>`<button class="${(managerMarketRoleFilter||"")===(r==="ALL"?"":r)?"active":""}" data-market-role="${r==="ALL"?"":r}" type="button">${safe(r)}</button>`).join("");box.querySelectorAll("[data-market-role]").forEach(b=>b.onclick=()=>{managerMarketRoleFilter=b.dataset.marketRole||"";renderManagerMarketPage()})}
    function renderManagerMarketPage(){renderTransferEconomy("managerMarketEconomy");const notice=$("managerMarketNotice");if(notice){notice.innerHTML=managerMarketNotice;notice.classList.toggle("hide",!managerMarketNotice);bindSigningNoticeAction(notice)}renderManagerMarketFilters();const list=$("managerMarketList"),team={shirt:"#1b5fd0",trim:"#ffdd45"},players=sortedMarketPlayers(),role=managerMarketRoleFilter?`${managerMarketRoleFilter} `:"";list.innerHTML=players.length?players.map(p=>managerTransferPlayerHtml(p,"buy","MARKET",team)).join(""):`<div class="manager-meta">No ${safe(role)}players on market</div>`;bindTransferCards(list,"data-buy",openMarketTransferOffer);bindTransferCards(list,"data-remove-market",removeMarketListing)}
    function statInfoRows(){return[["HOME/AWAY","Advantage","Home team gets a small match boost, about +4.5%. Away team has no boost, so the same two teams are slightly less likely to win away."],["FOOT","Preferred foot","L/R/B means left, right or both feet. Wide players get +1 OVR on their correct side and -1 on the wrong side."],["SHT","Shooting","Finishing, shot choice and goal threat."],["PAS","Passing","Pass accuracy, tempo and safer buildup."],["TAC","Tackling","Ball winning, defensive duels and pressure."],["HEA","Heading","Aerial duels, crosses, corners and clearances."],["PCL","Passcontrol","First touch when receiving passes and keeping possession."],["SCL","Shotcontrol","Shot contact, low shots, accuracy under pressure."],["SPD","Speed","Sprint pace, recovery runs and breaks behind defense."],["CRT","Crit","Chance for special high-impact moments in shots and duels."],["STA","Stamina","How long the player keeps level late in matches."],["STR","Strength","Physical duels, shielding and contact balance."],["POS","Positioning","Being in the right zone for attacks, defense and rebounds."],["VIS","Vision","Finding better passes, switches and through balls."],["CMP","Composure","Calm decisions when pressed or near goal."],["AGR","Aggression","How hard the player presses and challenges."],["DIS","Discipline","Lower foul/card risk and cleaner defending."],["CRS","Crossing","Wide delivery from flanks and set-up for headers."],["DRB","Dribbling","Carrying the ball, beating pressure and wide attacks."],["WRK","Workrate","Off-ball movement, tracking back and support runs."],["KEEP","Keeping","Goalkeeper saves, catching and keeper control."]]}
    function renderManagerStatsInfoPage(){const stats=statInfoRows().map(([code,name,text])=>`<div class="manager-info-row"><b>${safe(code)} ${safe(name)}</b><span>${safe(text)}</span></div>`).join(""),game=gameInfoRows().map(([code,text])=>`<div class="manager-info-row"><b>${safe(code)}</b><span>${safe(text)}</span></div>`).join("");$("managerStatsInfoList").innerHTML=`${stats}<div class="manager-info-title">GAME INFO</div>${game}`;showManagerSection("managerStatsInfoPage")}
    function gameInfoRows(){return[["FOOT SIDE","LB/LM works best with left foot, RB/RM works best with right foot. Correct side gives +1 OVR, wrong side gives -1 OVR, both feet gives +1 on either side."],["POSITION","A player in the wrong role gets -10% OVR. LB/RB count as the same side-pair and LM/RM count as the same side-pair, so swapping left/right only uses the foot bonus/penalty."],["RED MARK","In Squad Setup a red player box means the player has the -10% wrong-position penalty in that slot."],["OVR","OVR starts from the player's stats, then foot side is added, then wrong-position penalty is applied."],["PLAYER IDS","Each player has a permanent ID stored on the player. Shirt number is separate, so transfers and match reports can track the same player after he moves club."],["TEAM SKILL","The league S number shows squad depth. It uses raw skill points from the squad's best 16 players, so it is useful when judging the whole club."],["MATCH POWER","When a match is simulated, the engine uses only the starting XI on the pitch. Bench players do not improve match power until they are selected into the team."],["MATCH FLOW","The starting XI is split into attack, midfield, defense and keeper power. Player roles and stats decide which part of the team they affect most."],["DAILY FORM","Before each fixture, daily form normally gives one starter +10% and one different starter -10%. If the club is in formtop, there is no -10% player and two different players get +10% instead. Dots are locked to players until the fixture is played."],["INJURIES","Each human manager can get 0-5 random injuries per season. They trigger before a round, last 1-3 matches, and show with a red injury badge in Squad Setup."],["INJURY BLOCK","If an injured player is still in STARTING, Watch, Sim Round and Sim Season stop. Press BEST to move injured starters to the bench automatically when you have 11 fit players."],["MATCH REPORTS","Watched manager matches store compact player summaries: distance, ball time, touches, passes, shots, tackles, fouls, saves and complaints."],["REPORT LABELS","Workhorse means most running. Slacker means lowest running outfield player. Underperformer means bad impact. Inefficient means he ran but did not help enough."],["PLAYER TALK NOTE","A terrible report should later unlock rest, bonus, gift or threat actions. Threat should show a warning first because the player can quit."],["CONDITION NOTE","Future injuries, fitness and daily form should adjust a player's effective stats before match power is calculated. That means the same squad S can stay high while today's starting XI plays weaker or stronger."],["ODDS","1 / X / 2 comes from both teams' match power, recent form and home advantage. It is chance, not a fixed result."],["HOME BOOST","The home team gets a small match profile boost, currently about +4.5%, and the goal model starts slightly higher for the home side."],["FORM CURVE","Three straight league wins give a green up arrow, +3% match power and formtop daily form: two +10% players and no -10% player. Three straight league losses give a red down arrow and -3%. Draws reset the streak; cup matches do not count."],["GOALS","Scorers are picked from the match engine when you watch, or weighted by role and attacking stats when simmed."],["PENS","Penalties exist. Penalty goals are counted in Top Scorers with the pen number in parentheses."],["ASSISTS","Assists are tracked separately. Midfielders and wide players are more likely to assist, with passing and vision helping."],["TRANSFERS","Browse transfers can be rejected or countered. Better players and higher-division clubs are harder and more expensive to buy."]]}
    async function renderManagerTrophiesPage(){const list=$("managerTrophiesList");if(!list||!me?.name)return;list.innerHTML="";showManagerSection("managerTrophiesPage");const r=await api("managerTeamDetails",{teamUser:me.name});if(!r.ok)return msg("managerMsg","Could not load trophies");const d=r.teamDetails||{};list.innerHTML=trophiesHtml(d.trophies||[])}
    function renderManagerEconomyPage(){const players=allSquadPlayers(),seasonAwards=Number(managerFinance?.seasonAwards||0),transferIncome=Number(managerFinance?.transferIncome||0),arenaSpent=Number(managerFinance?.arenaSpent||0),income=Number(managerFinance?.income||0)+seasonAwards+transferIncome,out=Number(managerFinance?.wages||0)+Number(managerFinance?.transferSpent||0)+arenaSpent,squadValue=Number(managerFinance?.squadValue||0)||sumPlayers(players,"value"),weekly=Number(managerFinance?.weeklyWages||0)||sumPlayers(players,"wage"),net=income-out;$("managerEconomySummary").innerHTML=[economyCard("BALANCE",money(managerFinance?.balance||0),"good"),economyCard("MONEY IN",money(income),"good"),economyCard("MONEY OUT",signedMoney(-out),"bad"),economyCard("NET",signedMoney(net),net>=0?"good":"bad"),economyCard("WAGES/WEEK",signedMoney(-weekly),"bad"),economyCard("SQUAD VALUE",money(squadValue)),economyCard("ARENA VISITORS",money(managerFinance?.gateIncome||0),"good"),economyCard("SHIRT SALES",money(managerFinance?.merchIncome||0),"good"),economyCard("MATCH BONUS",money(managerFinance?.bonusIncome||0),"good"),economyCard("CUP MONEY",money(managerFinance?.cupIncome||0),"good"),economyCard("SEASON MONEY",money(seasonAwards),"good"),economyCard("TRANSFER SALES",money(transferIncome),"good"),economyCard("TRANSFER SPENT",signedMoney(-Number(managerFinance?.transferSpent||0)),"bad"),economyCard("ARENA BUILD",signedMoney(-arenaSpent),"bad"),economyCard("WAGES PAID",signedMoney(-Number(managerFinance?.wages||0)),"bad"),economyCard("START XI VALUE",money(managerFinance?.startersValue||sumPlayers(managerSquad?.starters||[],"value"))),economyCard("BENCH VALUE",money(managerFinance?.benchValue||sumPlayers(managerSquad?.bench||[],"value")))].join("");$("managerEconomyExpand").innerHTML=arenaExpansionHtml();bindArenaExpansion();showManagerSection("managerEconomyPage")}
    async function startArenaExpansion(){const next=managerFinance?.arenaNext;if(!next||managerBusy)return;if(!await askConfirm("BUILD ARENA",`Expand arena by ${Number(next.seats||0).toLocaleString("en-GB")} seats?\nCost ${money(next.cost||0)}. Build time ${next.rounds||0} rounds.`,`BUILD`,`CANCEL`,false))return;setManagerBusy(true);try{const r=await api("managerStartArenaExpansion");if(!r.ok)return msg("managerMsg",r.error==="no_money"?"Not enough money":r.error==="arena_building"?"Arena build already running":r.error==="arena_max"?"Arena is fully expanded":"Could not start arena build");managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;renderManagerSeason();renderManagerEconomyPage();msg("managerMsg","Arena build started")}finally{setManagerBusy(false)}}
    function dealOfferText(o){const p=o?.player||{},cash=Number(o?.cashOffer||0),offerPlayer=o?.offerPlayer,offerValue=Number(o?.offerValue||0),total=Number(o?.offerTotal||cash+offerValue);return `${safe(p.name||"Player")} | ${o?.countered?"counter":"cash"} ${money(cash)}${offerPlayer?` + ${safe(offerPlayer.name||"player")} ${money(offerValue)}`:""} | total ${money(total)}`}
    function dealNoticeTitle(n){const type=String(n?.type||"");return type==="accepted"?"DEAL ACCEPTED":type==="counter_accepted"?"COUNTER ACCEPTED":type==="counter_rejected"?"COUNTER DECLINED":"DEAL DECLINED"}
    function dealNoticeHtml(n){const type=String(n?.type||""),seller=n?.sellerTeam?.name||n?.seller||"Manager",buyer=n?.buyerTeam?.name||n?.buyer||"Manager",player=n?.player?.name||"Player";if(type==="counter_accepted")return `<b>COUNTER ACCEPTED</b>${safe(buyer)} accepted your counter for ${safe(player)}.`;if(type==="counter_rejected")return `<b>COUNTER DECLINED</b>${safe(buyer)} declined your counter for ${safe(player)}.`;return `<b>${type==="accepted"?"DEAL ACCEPTED":"DEAL DECLINED"}</b>${safe(seller)} ${type==="accepted"?"accepted":"declined"} your bid for ${safe(player)}.`}
    function managerDealNoticeCard(n){return `<div class="manager-deal-card incoming"><div><b>${dealNoticeTitle(n)}</b><small>${dealOfferText(n)}<br>${dealNoticeHtml(n).replace(/<b>.*?<\/b>/,"")}</small></div><div class="manager-deal-actions"><button class="green" data-deal-notice="${safe(n.id)}" type="button">OK</button></div></div>`}
    function managerDealCard(o,type){const incoming=type==="incoming",viewer=me?.name||"",sellerAction=incoming&&o.seller===viewer,buyerAction=incoming&&o.buyer===viewer,sellerName=o.sellerTeam?.name||o.seller,buyerName=o.buyerTeam?.name||o.buyer,other=sellerAction?buyerName:sellerName,title=sellerAction?`${safe(other)} WANTS A DEAL`:buyerAction?`COUNTER OFFER`:o.seller===viewer&&o.countered?`COUNTER SENT`:`WAITING FOR ${safe(other)}`,note=sellerAction?"They want your player":buyerAction?`${safe(sellerName)} wants ${money(o.cashOffer||0)}`:o.seller===viewer&&o.countered?`Waiting for ${safe(buyerName)}`:"You sent this bid",actions=sellerAction?`<div class="manager-deal-actions"><button class="green" data-deal-accept="${safe(o.id)}" type="button">ACCEPT</button><button data-deal-counter="${safe(o.id)}" type="button">COUNTER</button><button class="orange" data-deal-reject="${safe(o.id)}" type="button">REJECT</button></div>`:buyerAction?`<div class="manager-deal-actions"><button class="green" data-deal-accept="${safe(o.id)}" type="button">ACCEPT</button><button class="orange" data-deal-reject="${safe(o.id)}" type="button">DECLINE</button></div>`:"";return `<div class="manager-deal-card ${incoming?"incoming":"outgoing"}"><div><b>${title}</b><small>${dealOfferText(o)}<br>${note}</small></div>${actions}</div>`}
    function renderManagerDealOffers(){const box=$("managerDealOffers");if(!box)return;const incoming=managerTransferOffers?.incoming||[],outgoing=managerTransferOffers?.outgoing||[],notices=managerTransferOffers?.notices||[];box.innerHTML=`${notices.map(managerDealNoticeCard).join("")}${incoming.map(o=>managerDealCard(o,"incoming")).join("")}${outgoing.map(o=>managerDealCard(o,"outgoing")).join("")}`;box.querySelectorAll("[data-deal-accept]").forEach(b=>b.onclick=()=>decideManagerDeal(b.dataset.dealAccept,"accept"));box.querySelectorAll("[data-deal-reject]").forEach(b=>b.onclick=()=>decideManagerDeal(b.dataset.dealReject,"reject"));box.querySelectorAll("[data-deal-counter]").forEach(b=>b.onclick=()=>openDealCounterOffer(b.dataset.dealCounter));box.querySelectorAll("[data-deal-notice]").forEach(b=>b.onclick=()=>dismissManagerDealNotice(b.dataset.dealNotice))}
    async function decideManagerDeal(offerId,decision){if(!offerId||managerBusy)return;setManagerBusy(true);try{const r=await api("managerTransferOfferDecision",{offerId,decision});if(!r.ok)return msg("managerMsg",r.error==="bench_full"?benchFullText(r):r.error==="no_money"?"Buyer does not have enough money":r.error==="no_replacement"?"A starter needs a bench replacement":r.error==="offer_expired"?"Offer expired":"Could not update deal");managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;managerMarket=r.managerMarket||managerMarket;setManagerTransferOffers(r.managerTransferOffers||managerTransferOffers);renderManagerDealOffers();renderManagerSeason();msg("managerMsg",decision==="accept"?"Deal accepted":"Deal declined")}finally{setManagerBusy(false)}}
    async function counterManagerDeal(offerId,offer){if(!offerId||managerBusy)return;setManagerBusy(true);try{const r=await api("managerTransferOfferCounter",{offerId,...offer});if(!r.ok)return msg("managerMsg",r.error==="counter_too_low"?"Raise the counter":r.error==="offer_expired"?"Offer expired":"Could not send counter");managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;managerMarket=r.managerMarket||managerMarket;setManagerTransferOffers(r.managerTransferOffers||managerTransferOffers);renderManagerDealOffers();renderManagerSeason();msg("managerMsg",r.status==="accepted"?"Counter accepted":r.status==="counter_rejected"?"Counter declined":"Counter sent")}finally{setManagerBusy(false)}}
    async function dismissManagerDealNotice(noticeId){if(!noticeId)return;const r=await api("managerTransferNoticeSeen",{noticeId});if(r.ok)setManagerTransferOffers(r.managerTransferOffers||{incoming:[],outgoing:[],notices:[]})}
    function renderManagerTransfersPage(){renderTransferEconomy("managerTransfersEconomy");renderManagerDealOffers();showManagerSection("managerTransfersPage")}
    function renderManagerBrowsePage(level=managerBrowseLevel||5){renderTransferEconomy("managerBrowseEconomy");managerBrowseLevel=Number(level)||5;const div=(managerDivisions||[]).find(d=>Number(d.level)===managerBrowseLevel)||(managerDivisions||[])[0];if(!div)return msg("managerMsg","Could not load divisions");managerBrowseLevel=Number(div.level||managerBrowseLevel);$("managerBrowseMeta").textContent=`${div.name} | ${(div.teams||[]).length} clubs | choose a club to make bids`;$("managerBrowseLevels").innerHTML=(managerDivisions||[]).map(d=>`<button class="manager-level-button ${Number(d.level)===managerBrowseLevel?"active":""}" data-browse-level="${safe(d.level)}" type="button">${safe(d.name)}</button>`).join("");$("managerBrowseTeams").innerHTML=(div.teams||[]).map(t=>`<button class="manager-user" data-browse-team="${safe(t.id)}" type="button">${teamFlag(t.team)} <b>${safe(t.team?.name||t.id)}</b><small>Skill ${Math.round(Number(t.strength||0))} | squad browse</small></button>`).join("");$("managerBrowseLevels").querySelectorAll("[data-browse-level]").forEach(b=>b.onclick=()=>renderManagerBrowsePage(b.dataset.browseLevel));$("managerBrowseTeams").querySelectorAll("[data-browse-team]").forEach(b=>b.onclick=()=>{managerBrowseNotice="";managerBrowseRoleFilter="";openManagerBrowseTeam(b.dataset.browseTeam)});showManagerSection("managerBrowsePage")}
    function renderManagerBrowseFilters(){const box=$("managerBrowseFilters");if(!box)return;box.innerHTML=marketRoles().map(r=>`<button class="${(managerBrowseRoleFilter||"")===(r==="ALL"?"":r)?"active":""}" data-browse-role="${r==="ALL"?"":r}" type="button">${safe(r)}</button>`).join("");box.querySelectorAll("[data-browse-role]").forEach(b=>b.onclick=()=>{managerBrowseRoleFilter=b.dataset.browseRole||"";renderManagerBrowseTeamDetails()})}
    function sortedBrowsePlayers(starters,bench){let all=[...(starters||[]).map(p=>({...p,_browseGroup:"START"})),...(bench||[]).map(p=>({...p,_browseGroup:"BENCH"}))],role=String(managerBrowseRoleFilter||"").toUpperCase();if(role)all=all.filter(p=>playerMatchesRole(p,role));all.sort((a,b)=>marketRoleScore(b,role)-marketRoleScore(a,role)||playerRating(b)-playerRating(a)||Number(b.price||b.value||0)-Number(a.price||a.value||0));return all}
    function renderManagerBrowseTeamDetails(){renderTransferEconomy("managerBrowseTeamEconomy");const d=managerBrowseTeamDetails||{},userId=managerBrowseTeam;if(!userId)return;const team=d.team||{},sq=d.squad||{},starters=sq.starters||[],bench=sq.bench||[],players=starters.length+bench.length,notice=$("managerBrowseNotice"),role=managerBrowseRoleFilter?`${managerBrowseRoleFilter} `:"";$("managerBrowseTeamTitle").innerHTML=`<div class="team-profile-hero">${teamFlag(team)}<div><div class="team-profile-name">${safe(team.name||userId)}</div><div class="team-profile-sub">TRANSFER BROWSE${d.divisionName?` | ${safe(d.divisionName)}`:""}</div></div></div>`;$("managerBrowseTeamMeta").textContent=`${players} available players | skill ${Math.round(Number(d.strength||0))} | bids can be rejected or countered`;notice.innerHTML=managerBrowseNotice;notice.classList.toggle("hide",!managerBrowseNotice);bindSigningNoticeAction(notice);renderManagerBrowseFilters();const list=$("managerBrowsePlayerList");if(managerBrowseRoleFilter){const filtered=sortedBrowsePlayers(starters,bench);list.innerHTML=filtered.length?`<div class="team-section-title">${safe(managerBrowseRoleFilter)} PLAYERS</div>${filtered.map(p=>managerBrowsePlayerHtml(p,p._browseGroup||"PLAYER",team)).join("")}`:`<div class="manager-meta">No ${safe(role)}players in this squad</div>`}else{list.innerHTML=players?`${starters.length?`<div class="team-section-title">STARTING</div>${starters.map(p=>managerBrowsePlayerHtml(p,"START",team)).join("")}`:""}${bench.length?`<div class="team-section-title">BENCH</div>${bench.map(p=>managerBrowsePlayerHtml(p,"BENCH",team)).join("")}`:""}`:`<div class="manager-meta">No available players</div>`}bindTransferCards(list,"data-browse-buy",id=>openBrowseTransferOffer(userId,id));showManagerSection("managerBrowseTeamPage")}
    async function openManagerBrowseTeam(userId){if(!userId)return;managerBrowseTeam=userId;const r=await api("browseTeamDetails",{teamUser:userId});if(!r.ok)return msg("managerMsg",r.error==="human_transfer_locked"?"That manager is not available for deals":"Could not load browse team");managerBrowseTeamDetails=r.teamDetails||{};renderManagerBrowseTeamDetails()}
    function renderManagerDivision(level){const scroll=managerScrollSnapshot(),divisions=managerDivisions||[],div=divisions.find(d=>Number(d.level)===Number(level));if(!div)return msg("managerMsg","Could not load division");managerViewDivision=div;$("managerDivisionTitle").textContent=div.name;const levelBox=$("managerDivisionLevels");if(levelBox){levelBox.innerHTML=divisions.map(d=>`<button class="${Number(d.level)===Number(div.level)?"active":""}" data-manager-division-level="${safe(d.level)}" type="button">${safe(d.name||"DIVISION")}</button>`).join("");levelBox.querySelectorAll("[data-manager-division-level]").forEach(b=>b.onclick=()=>renderManagerDivision(b.dataset.managerDivisionLevel))}$("managerDivisionMeta").textContent=`${(div.teams||[]).length} clubs | click a team to view squad`;const rows=(div.teams||[]).map((r,i)=>`<div class="manager-row"><span>${i+1}</span><span><button class="manager-team-link" data-manager-team="${safe(r.id)}" type="button">${teamFlag(r.team)} <span class="manager-team-name">${safe(r.team?.name||r.id)}</span></button></span><span>${Math.round(Number(r.strength||0))}</span></div>`).join("");$("managerDivisionList").innerHTML=`<div class="manager-row head"><span>#</span><span>TEAM</span><span>SKILL</span></div>${rows}`;$("managerDivisionList").querySelectorAll("[data-manager-team]").forEach(b=>b.onclick=()=>openManagerTeam(b.dataset.managerTeam));showManagerSection("managerDivisionPage");restoreManagerScroll(scroll)}
    function renderManagerViewTeamsPage(){const divisions=managerDivisions||[];$("managerViewTeamsPageList").innerHTML=divisions.length?divisions.map(d=>`<button data-manager-view-team="${safe(d.level)}" type="button">${safe(d.name||"DIVISION")}</button>`).join(""):`<div class="manager-meta">No divisions</div>`;showManagerSection("managerViewTeamsPage")}
    function managerStatContextTitle(){return managerSeason?.statContext?.label||"League"}
    function managerScorerRows(rows){return (rows||[]).length?(rows||[]).map(s=>{const pens=Number(s.penalties||0),goals=`${safe(s.goals||0)}${pens?` (${pens} pen)`:""}`;return `<div class="manager-fixture"><span>${goals}</span><span>${safe(s.player)}</span><b></b><span>${safe(s.team?.name||s.user)}</span></div>`}).join(""):`<div class="manager-meta">No goals yet</div>`}
    function managerAssistRows(rows){return (rows||[]).length?(rows||[]).map(s=>`<div class="manager-fixture"><span>${safe(s.assists||0)}</span><span>${safe(s.player)}</span><b></b><span>${safe(s.team?.name||s.user)}</span></div>`).join(""):`<div class="manager-meta">No assists yet</div>`}
    function managerScorersHtml(){const cup=managerScorersScope==="cup",rows=cup?(managerSeason?.topCupScorers||[]):(managerSeason?.topScorers||[]),title=cup?"Cup goals":"League goals",assists=managerAssistRows(managerSeason?.topAssists||[]);return `<div class="manager-filter-row"><button class="${!cup?"active":""}" data-scorer-scope="league" type="button">LEAGUE</button><button class="${cup?"active":""}" data-scorer-scope="cup" type="button">CUP</button></div><div class="manager-meta">${safe(title)}</div>${managerScorerRows(rows)}<div class="manager-stat-divider"><b>TOP ASSISTS</b><span>LEAGUE ASSISTS</span></div>${assists}`}
    function managerAssistsHtml(){return `<div class="manager-meta">${safe(managerStatContextTitle())}</div>${managerAssistRows(managerSeason?.topAssists||[])}`}
    function reportBehavior(p){const b=String(p?.behavior||"OK");if(b==="SLACKED"&&String(p?.role||"").toUpperCase()!=="GK"&&Number(p?.distanceMeters||0)>=220)return "INEFFICIENT";return b}
    function reportPlayerText(p){return p?`${safe(p.name||"Player")} (${safe(p.teamName||"")}) ${safe(reportBehavior(p))} | impact ${safe(p.impact??0)} | dist ${safe(p.distanceMeters||0)}m | touch ${safe(p.touches||0)} | pass ${safe(p.passes||0)} | shot ${safe(p.shots||0)} | tackle ${safe(p.tackles||0)}${Number(p.complaints||0)?` | moan ${safe(p.complaints)}`:""}`:"-"}
    function reportBelongsToViewer(r){const user=String(me?.name||"");return !user||String(r?.home||"")===user||String(r?.away||"")===user}
    function viewerMatchReports(){return (managerSeason?.matchReports||[]).filter(reportBelongsToViewer)}
    function reportHumanPlayers(r){const user=String(me?.name||""),humans=new Set(managerSeason?.humanManagers||[]),sideUser={home:String(r?.home||""),away:String(r?.away||"")};if(user&&(sideUser.home===user||sideUser.away===user))return (r?.players||[]).filter(p=>sideUser[p.side]===user);return (r?.players||[]).filter(p=>humans.has(sideUser[p.side]))}
    function reportTop(players,key,lowest=false){const list=[...(players||[])].filter(p=>Number.isFinite(Number(p?.[key])));if(!list.length)return null;list.sort((a,b)=>lowest?Number(a[key])-Number(b[key]):Number(b[key])-Number(a[key]));return list[0]}
    function reportSlacker(players){let list=[...(players||[])].filter(p=>String(p?.role||"").toUpperCase()!=="GK"&&Number.isFinite(Number(p?.distanceMeters)));if(!list.length)list=[...(players||[])].filter(p=>Number.isFinite(Number(p?.distanceMeters)));if(!list.length)return null;list.sort((a,b)=>Number(a.distanceMeters||0)-Number(b.distanceMeters||0)||Number(a.impact||0)-Number(b.impact||0));return list[0]}
    function reportHumanTitle(r,kind){const humans=new Set(managerSeason?.humanManagers||[]),h=r.homeTeam||{},a=r.awayTeam||{},sc=r.score||[0,0],homeHuman=humans.has(String(r.home||"")),awayHuman=humans.has(String(r.away||""));if(homeHuman&&awayHuman)return `${safe(kind)} | ${teamFlag(h)} ${safe(h.name||r.home)} ${safe(sc[0]??0)}-${safe(sc[1]??0)} ${teamFlag(a)} ${safe(a.name||r.away)}`;if(homeHuman)return `${safe(kind)} | ${teamFlag(h)} ${safe(h.name||r.home)} vs ${teamFlag(a)} ${safe(a.name||r.away)} | result ${safe(sc[0]??0)}-${safe(sc[1]??0)}`;if(awayHuman)return `${safe(kind)} | ${teamFlag(a)} ${safe(a.name||r.away)} vs ${teamFlag(h)} ${safe(h.name||r.home)} | result ${safe(sc[1]??0)}-${safe(sc[0]??0)}`;return safe(kind)}
    function managerReportCard(r){const kind=r.simulated?"SIM REPORT":"WATCH REPORT",humanPlayers=reportHumanPlayers(r),mostBall=reportTop(humanPlayers,"possessionSeconds"),workhorse=reportTop(humanPlayers,"distanceMeters"),slacker=reportSlacker(humanPlayers),moaner=reportTop(humanPlayers,"complaints"),worst=humanPlayers.find(p=>p.terrible)||reportTop(humanPlayers,"impact",true),players=humanPlayers.slice(0,8).map(reportPlayerText).map(x=>`<div class="season-report-row"><span class="season-report-label">PLAYER</span><div>${x}</div></div>`).join(""),bad=worst?`<div class="season-report-row"><span class="season-report-label">UNDERPERFORMER</span><div>${reportPlayerText(worst)}<br><span class="manager-meta">Resting him next match should help. Bonus/gift/threat can be added later as a player talk action with warning before threat risk.</span></div></div>`:"";return `<div class="manager-playoff season-report"><div class="season-report-title">${reportHumanTitle(r,kind)}</div><div class="season-report-section"><div class="season-report-row"><span class="season-report-label">MOST BALL</span><div>${reportPlayerText(mostBall)}</div></div><div class="season-report-row"><span class="season-report-label">WORKHORSE</span><div>${reportPlayerText(workhorse)}</div></div><div class="season-report-row"><span class="season-report-label">SLACKER</span><div>${slacker?reportPlayerText(slacker):"-"}</div></div><div class="season-report-row"><span class="season-report-label">MOANER</span><div>${Number(moaner?.complaints||0)>0?reportPlayerText(moaner):"-"}</div></div>${bad}</div><div class="season-report-section"><b>PLAYER SUMMARY</b>${players||`<div class="manager-meta">No human player data</div>`}</div></div>`}
    function managerReportsHtml(){const reports=viewerMatchReports();return reports.length?reports.map(managerReportCard).join(""):`<div class="manager-meta">No watched match reports yet</div>`}
    function managerCupName(cupKey,fixture={},round={}){const cup=(managerSeason?.cups||[]).find(c=>String(c.key||"")===String(cupKey||""));return fixture.cupName||round.cupName||cup?.name||"Cup"}
    function managerCupStageText(fixture={},round={}){const stage=String(fixture.cupStage||round.cupStage||"").toLowerCase(),label=String(round.round||fixture.round||"").trim();if(stage==="group")return "GROUP STAGE";if(stage==="league")return "LEAGUE PHASE";return label||"KNOCKOUT"}
    function managerCupResultForUser(f,user){if(f.status!=="played"||!Array.isArray(f.score))return "PENDING";const home=String(f.home||"")===user,gf=Number(f.score[home?0:1]||0),ga=Number(f.score[home?1:0]||0),winner=String(f.winner||"");if(winner)return winner===user?"W":"L";return gf>ga?"W":gf<ga?"L":"D"}
    function managerCupStatus(cup,stat){if(stat.winner)return "WINNER";if(stat.runnerUp)return "RUNNER-UP";if(stat.active)return `ACTIVE - NEXT ${stat.pendingRound||stat.bestRound||""}`.trim();if(stat.knockoutExit)return `OUT - ${stat.knockoutExit}`;if(stat.tableExit)return `OUT - ${stat.tableExit}`;if(String(cup?.status||"")==="finished")return `OUT - ${stat.bestRound||"CUP"}`;if(stat.tableOnly)return stat.tableWaiting||"GROUP TABLE";return stat.bestRound||"NO MATCHES"}
    function managerCupMatchRow(m){const score=m.status==="played"&&Array.isArray(m.score)?`${safe(m.score[0])}-${safe(m.score[1])}`:"v",decision=m.decision?` | ${safe(m.decision)}`:"",cls=String(m.result||"").toLowerCase();return `<div class="manager-cup-match ${safe(cls)}"><span>${safe(m.round)}</span>${seasonTeamLine(m.home,null,"season-home")}<b>${safe(score)}</b>${seasonTeamLine(m.away,null,"season-away")}<em>${safe(m.result)}${decision}</em></div>`}
    function managerCupCard(cup,stat){const team=managerSeason?.teamsByUser?.[stat.user]?.team,gd=Number(stat.gf||0)-Number(stat.ga||0),matches=stat.matches.map(managerCupMatchRow).join("");return `<div class="manager-cup-card"><div class="manager-cup-head">${seasonTeamLine(stat.user,team)}<strong>${safe(managerCupStatus(cup,stat))}</strong></div><div class="manager-cup-metrics"><span><b>${safe(stat.played)}</b> P</span><span><b>${safe(stat.wins)}</b> W</span><span><b>${safe(stat.draws)}</b> D</span><span><b>${safe(stat.losses)}</b> L</span><span><b>${safe(stat.gf)}-${safe(stat.ga)}</b> GF-GA</span><span><b>${gd>0?"+":""}${safe(gd)}</b> GD</span></div><div class="manager-cup-matches">${matches||`<div class="manager-meta">No cup matches</div>`}</div></div>`}
    function managerCupsHtml(){const humans=(managerSeason?.humanManagers||[]).map(String),cups=new Map(),cupDefs=new Map((managerSeason?.cups||[]).map((c,i)=>[String(c.key||c.name||i),{...c,_order:i}]));function ensureCup(key,name){if(!cups.has(key)){const def=cupDefs.get(key)||{};cups.set(key,{key,name:name||def.name||"Cup",def,order:Number(def._order??cups.size),stats:new Map()})}return cups.get(key)}function ensureStat(cup,user){if(!cup.stats.has(user))cup.stats.set(user,{user,played:0,wins:0,draws:0,losses:0,gf:0,ga:0,bestRound:"",pendingRound:"",knockoutExit:"",tableExit:"",tableOnly:false,tableWaiting:"",active:false,winner:false,runnerUp:false,matches:[]});return cup.stats.get(user)}(managerSeason?.rounds||[]).forEach((round,roundIndex)=>{(round.fixtures||[]).forEach((f,fixtureIndex)=>{const phase=String(f.phase||round.phase||"league").toLowerCase();if(phase!=="cup")return;const key=String(f.cupKey||round.cupKey||"cup"),cup=ensureCup(key,managerCupName(key,f,round)),stage=String(f.cupStage||round.cupStage||"").toLowerCase(),roundText=String(round.round||f.round||cup.name).trim();humans.forEach(user=>{if(String(f.home||"")!==user&&String(f.away||"")!==user)return;const stat=ensureStat(cup,user),home=String(f.home||"")===user,score=Array.isArray(f.score)?f.score:null,gf=score?Number(score[home?0:1]||0):0,ga=score?Number(score[home?1:0]||0):0,result=managerCupResultForUser(f,user),stageText=managerCupStageText(f,round);stat.bestRound=stage==="group"||stage==="league"?stageText:roundText;if(stage==="group"||stage==="league")stat.tableOnly=true;if(f.status==="pending"||f.status==="watching"){stat.active=true;stat.pendingRound=roundText}if(f.status==="played"&&score){stat.played++;stat.gf+=gf;stat.ga+=ga;if(result==="W")stat.wins++;else if(result==="L")stat.losses++;else stat.draws++;if(stage==="knockout"&&String(f.winner||"")&&String(f.winner||"")!==user)stat.knockoutExit=roundText;stat.matches.push({round:roundText,status:f.status,home:f.home,away:f.away,score:f.score,result,decision:f.decidedBy||"",roundIndex,fixtureIndex})}})})});cups.forEach(cup=>{humans.forEach(user=>{const stat=cup.stats.get(user);if(!stat)return;if(String(cup.def?.winner||"")===user)stat.winner=true;if(String(cup.def?.runnerUp||"")===user)stat.runnerUp=true;if(!stat.active&&!stat.knockoutExit&&stat.tableOnly){const cupStatus=String(cup.def?.status||"");stat.tableExit=cupStatus==="knockout"||cupStatus==="finished"?stat.bestRound||"GROUP STAGE":"";stat.tableWaiting=stat.tableExit?"":stat.bestRound||"GROUP TABLE"}})});const rows=[...cups.values()].sort((a,b)=>a.order-b.order||a.name.localeCompare(b.name)).map(cup=>{const cards=humans.map(user=>cup.stats.get(user)).filter(Boolean).map(stat=>managerCupCard(cup.def,stat)).join("");return cards?`<div class="manager-cup-section"><div class="manager-info-title">${safe(cup.name)}</div>${cards}</div>`:""}).filter(Boolean);return rows.length?rows.join(""):`<div class="manager-meta">No human cup matches yet</div>`}
    function managerFixturesHtml(){return (managerSeason?.rounds||[]).length?(managerSeason.rounds||[]).map(r=>`<div class="manager-round ${r.playoff||r.phase==="cup"?"playoff":""}"><div class="manager-meta">${safe(managerRoundTitle(r))}</div>${(r.fixtures||[]).map(managerFixtureHtml).join("")}</div>`).join(""):`<div class="manager-meta">No fixtures</div>`}
    function renderManagerScorersPage(resetScope=false){if(resetScope)managerScorersScope="league";$("managerScorersList").innerHTML=managerScorersHtml();$("managerScorersList").querySelectorAll("[data-scorer-scope]").forEach(b=>b.onclick=()=>{managerScorersScope=b.dataset.scorerScope||"league";renderManagerScorersPage()});showManagerSection("managerScorersPage")}
    function renderManagerAssistsPage(){$("managerAssistsList").innerHTML=managerAssistsHtml();showManagerSection("managerAssistsPage")}
    function renderManagerReportsPage(){$("managerReportsList").innerHTML=managerReportsHtml();showManagerSection("managerReportsPage")}
    function renderManagerCupsPage(){$("managerCupsList").innerHTML=managerCupsHtml();showManagerSection("managerCupsPage")}
    function renderManagerFixturesPage(){$("managerFixturesList").innerHTML=managerFixturesHtml();showManagerSection("managerFixturesPage")}
    function renderManagerCreate(){
      const users=normalizedManagerUsers(),ready=readyManagerUsers(),own=ready.find(u=>u.name===me?.name),count=$("managerCount"),list=$("managerUsers"),picked=$("managerPicked"),start=$("startManagerLeague");
      count.textContent=users.length>1?"SELECT USERS TO INVITE":"ONLY YOU AVAILABLE";
      if(!own){list.innerHTML=`<div class="manager-meta">Choose a team first</div>`;picked.textContent="0 human managers selected";start.disabled=true;return}
      const pendingInvite=(managerInvites||[]).find(inv=>inv.from===me?.name&&!inv.needsMe);
      if(pendingInvite){list.innerHTML=`<div class="invite-box"><b>WAITING FOR PLAYERS</b><br>${inviteStatusHtml(pendingInvite)}</div>`;picked.textContent="Manager mode starts automatically when everyone has pressed LET'S GO.";start.disabled=true;start.textContent="WAITING FOR PLAYERS";return}
      start.textContent="START MANAGER MODE";
      const readyNames=new Set(ready.map(u=>u.name));
      managerPicked=Array.from(new Set(managerPicked.filter(name=>readyNames.has(name))));
      if(me?.name&&!managerPicked.includes(me.name))managerPicked.unshift(me.name);
      const set=new Set(managerPicked);
      list.innerHTML=users.map(u=>`<button class="manager-user ${!u.ready?"off":set.has(u.name)?"green":""}" data-user="${safe(u.name)}" type="button" ${!u.ready?"disabled":""}><b>${safe(u.name.toUpperCase())}${u.name===me?.name?" YOU":""}</b><small>${u.ready?safe(managerTeamLine(u)):"NO TEAM - LOGIN AND CHOOSE TEAM FIRST"}</small></button>`).join("");
      picked.textContent=`${managerPicked.length} human manager${managerPicked.length===1?"":"s"} selected. Other clubs become CPU.`;
      start.disabled=managerPicked.length<1;
    }
    async function loadManager(record=true,startFreshWhenNoContinue=false){
      const r=await api("managerSetup");if(!r.ok)return msg("managerMsg","Could not load manager mode");
      managerUsers=r.managerUsers||[];managerSeason=r.managerSeason||null;managerInvites=r.managerInvites||[];managerFinance=r.managerFinance||null;managerSquad=r.managerSquad||null;managerMarket=r.managerMarket||[];managerDivisions=r.managerDivisions||[];setManagerTransferOffers(r.managerTransferOffers);
      const canContinue=!!(r.managerCanContinue??!!managerSeason);
      if(startFreshWhenNoContinue&&!canContinue)managerSeason=null;
      setManagerStateFlags({managerHasSeason:!!managerSeason,managerCanContinue:canContinue,managerHasAnySave:r.managerHasAnySave});
      managerAbortConfirm=false;
      renderUserTop();
      syncMenuControls();
      syncManagerActions();
      renderInviteTarget("managerInviteBox");renderManagerSeason();msg("managerMsg","");
      if(managerSeason)showManagerSection("managerHome",record);else openManagerCreate(record);
      startManagerPoll();
    }
    async function refreshManager(){if(managerPollBusy||managerBusy||$("manager").classList.contains("hide"))return;managerPollBusy=true;try{const section=currentManagerSection(),hadSeason=!!managerSeason,r=await api("managerSetup",{}, {quiet:true});if(!r.ok)return;if(checkLoadedVersion(r.appVersion))return;managerUsers=r.managerUsers||[];managerSeason=r.managerSeason||null;managerInvites=r.managerInvites||[];managerFinance=r.managerFinance||null;managerSquad=r.managerSquad||null;managerMarket=r.managerMarket||[];managerDivisions=r.managerDivisions||[];setManagerTransferOffers(r.managerTransferOffers);setManagerStateFlags({managerHasSeason:!!managerSeason,managerCanContinue:r.managerCanContinue??!!managerSeason,managerHasAnySave:r.managerHasAnySave});renderUserTop();syncManagerActions();renderInviteTarget("managerInviteBox");renderManagerSeason();if(managerSeason&&(!hadSeason||section==="managerCreate"||!section)){showManagerSection("managerHome",false);replaceAppHistory()}else if(!managerSeason&&section==="managerCreate")renderManagerCreate()}finally{managerPollBusy=false}}
    function startManagerPoll(){if(managerPoll)return;refreshManager();managerPoll=setInterval(refreshManager,MANAGER_POLL_MS)}
    function stopManagerPoll(){if(managerPoll)clearInterval(managerPoll);managerPoll=null;managerPollBusy=false}
    async function openManager(){stopPoll();stopCrowd();stopManagerPoll();setMatch(null);trainingMode=false;setRoute("manager");managerSeason=null;managerFinance=null;managerSquad=null;managerMarket=[];setManagerTransferOffers({incoming:[],outgoing:[]});managerInvites=[];renderUserTop();clearManagerView();show("manager");await loadManager()}
    async function openManagerStart(){stopPoll();stopCrowd();stopManagerPoll();setMatch(null);trainingMode=false;setRoute("manager");managerSeason=null;managerFinance=null;managerSquad=null;managerMarket=[];setManagerTransferOffers({incoming:[],outgoing:[]});managerInvites=[];renderUserTop();clearManagerView();show("manager");await loadManager(true,true)}
    async function startNewSoloManagerMode(){
      if(!me?.name)return;
      if(managerHasSeason&&!await askConfirm("NEW GAME","Start a clean solo game?\nActive manager progress resets, but saved games stay untouched.","START","CANCEL",false))return;
      stopPoll();stopCrowd();stopManagerPoll();setMatch(null);trainingMode=false;setRoute("manager");clearManagerView("Starting manager mode");show("manager");
      const r=await api("createManagerSeason",{count:1,users:[me.name],freshStart:true});
      if(!r.ok){setRoute("");goMainMenu(true);return msg("menuMsg",r.error==="user_without_team"?"Choose team first":"Could not start manager mode")}
      managerSeason=r.managerSeason||null;setManagerStateFlags({managerHasSeason:!!managerSeason,managerCanContinue:r.managerCanContinue??!!managerSeason,managerHasAnySave:r.managerHasAnySave});managerInvites=r.managerInvites||[];await loadManager(false);replaceAppHistory();msg("managerMsg","Solo manager mode started");
    }
    function openManagerCreate(record=true){
      managerPicked=me?.name&&readyManagerUsers().some(u=>u.name===me.name)?[me.name]:[];
      showManagerSection("managerCreate",record);renderManagerCreate();
    }
    function toggleManagerUser(name){name=String(name||"").toLowerCase();if(!readyManagerUsers().some(u=>u.name===name))return;if(name===me?.name){if(!managerPicked.includes(name))managerPicked.unshift(name);renderManagerCreate();return}const i=managerPicked.indexOf(name);if(i>=0)managerPicked.splice(i,1);else managerPicked.push(name);if(me?.name&&!managerPicked.includes(me.name))managerPicked.unshift(me.name);renderManagerCreate()}
    async function startManagerLeague(){
      if(managerBusy)return;
      const users=managerPicked.includes(me?.name)?managerPicked:[me.name,...managerPicked];
      setManagerBusy(true);clearManagerView("Starting manager mode");
      try{
        const r=await api("createManagerSeason",{count:users.length,users,freshStart:users.length===1});
        if(!r.ok){openManagerCreate();return msg("managerMsg",r.error==="user_without_team"?"A selected user needs a team":r.error==="duplicate_human_team"?"Two selected users have the same team. Pick different teams for this league.":"Could not start league")}
        managerSeason=r.managerSeason||null;setManagerStateFlags({managerHasSeason:!!managerSeason,managerCanContinue:r.managerCanContinue??!!managerSeason,managerHasAnySave:r.managerHasAnySave});managerInvites=r.managerInvites||managerInvites;
        await loadManager(false);
        replaceAppHistory();
        if(r.managerInvite)setTimeout(refreshManager,500);
        msg("managerMsg",r.managerInvite?"Invite sent. Waiting for players.":"League started");
      }finally{setManagerBusy(false)}
    }
    function stopManagerSimAnimation(){if(managerSimTimer){clearInterval(managerSimTimer);managerSimTimer=null}managerSimRunning=false;managerSimWhistledGoals.clear();syncManagerMenuMusic()}
    function managerSimTeam(user){return managerSeason?.teamsByUser?.[user]?.team||{}}
    function managerSimFixture(){const n=nextManagerFixture()||nextManagerFixturePreview();return n?{...n.fixture,round:n.round,roundLabel:managerRoundTitle(n),phase:n.phase,playoff:n.playoff,roundIndex:n.roundIndex,fixtureIndex:n.fixtureIndex}:null}
    function managerSimMatchType(fixture=null,report=null){
      const round=Number.isFinite(Number(report?.roundIndex))?(managerSeason?.rounds||[])[Number(report.roundIndex)]:null;
      const phase=String(round?.phase||fixture?.phase||report?.phase||"league").toLowerCase();
      const roundName=String(round?.round||fixture?.roundLabel||fixture?.round||report?.round||"");
      if(phase==="cup")return round?.cupName||fixture?.cupName||roundName||"Cup match";
      if(phase==="playoff"||round?.playoff||fixture?.playoff)return roundName?`Playoff - ${roundName}`:"Playoff match";
      if(phase==="friendly")return "Friendly match";
      if(phase==="training")return "Training match";
      return `${managerSeason?.divisionName||"League"} match`;
    }
    function managerSimLatestReport(fixture=null){const reports=viewerMatchReports();if(!reports.length)return null;if(fixture){const h=String(fixture.home||""),a=String(fixture.away||"");const found=reports.find(r=>String(r.home||"")===h&&String(r.away||"")===a);if(found)return found}return reports[0]}
    function managerSimPressure(report){const s=report?.teamStats||{},val=side=>Number(s.possession?.[side]||0)*0.75+Number(s.touches?.[side]||0)*0.35+Number(s.shots?.[side]||0)*8+Number(s.tackles?.[side]||0)*2.5+Number(report?.score?.[side==="home"?0:1]||0)*5,home=val("home"),away=val("away"),total=Math.max(1,home+away);return Math.max(5,Math.min(95,Math.round(home/total*100)))}
    function managerSimPressureText(homeTeam,awayTeam,pressure){const home=homeTeam?.name||"Home",away=awayTeam?.name||"Away",dist=Math.abs(Number(pressure||50)-50);if(dist<18)return "ZzZz mode";return pressure>50?`${home} pressure ->`:`<- ${away} pressure`}
    function managerSimPressureStyle(pressure){const p=clamp(Number(pressure)||50,5,95),dist=Math.abs(p-50),left=Math.min(p,50),hot=dist>=42?"#b91024":dist>=27?"#f28a18":dist>=18?"#ffdd45":"#26da48",from=p>=50?"#26da48":hot,to=p>=50?hot:"#26da48";return `--pressure-left:${left}%;--pressure-width:${dist}%;--pressure-from:${from};--pressure-to:${to}`}
    function managerSimPlaybackPressure(report,progress=0){
      if(!report)return 50+Math.round(Math.sin(Date.now()/260)*6);
      const minute=clamp(Number(progress||0)*90,0,90),events=managerSimTimeline(report),baseTarget=managerSimPressure(report),baseBias=clamp((baseTarget-50)*.22,-8,8);
      const goalNow=events.find(e=>e.kind==="goal"&&minute>=Number(e.minute||0)&&minute-Number(e.minute||0)<.55);
      if(goalNow){
        const side=goalNow.side==="home"?1:goalNow.side==="away"?-1:0;
        if(side)return side>0?95:5;
      }
      const lastGoal=[...events].filter(e=>e.kind==="goal"&&Number(e.minute||0)<=minute).pop();
      if(lastGoal){
        const sinceGoal=minute-Number(lastGoal.minute||0);
        if(sinceGoal>0&&sinceGoal<4.5)return 50;
        if(sinceGoal>=4.5&&sinceGoal<9){
          const restart=(sinceGoal-4.5)/4.5;
          return clamp(Math.round(50+baseBias*restart+Math.sin(Date.now()/620+minute*.12)*2*restart),42,58);
        }
      }
      const seed=managerSimSeed(report);
      let force=baseBias
        + Math.sin(minute*.29+(seed%31)) * 9
        + Math.sin(minute*.73+(seed%17)) * 4;
      events.forEach(e=>{
        const side=e.side==="home"?1:e.side==="away"?-1:0;
        if(!side)return;
        const eventMinute=Number(e.minute||0);
        if(eventMinute>minute)return;
        const dist=minute-eventMinute,window=e.kind==="goal"?13:e.kind==="pen"?11:7;
        if(dist>window)return;
        if(e.kind==="goal")return;
        const weight=(1-dist/window)*(e.kind==="goal"?31:e.kind==="pen"?25:e.kind==="red"?20:e.kind==="yellow"?13:9);
        force+=side*weight;
      });
      const wobble=Math.sin(Date.now()/520+minute*.18)*3;
      return clamp(Math.round(50+force+wobble),5,95);
    }
    function managerSimSeed(report){return String(report?.id||report?.seasonId||"match").split("").reduce((a,c)=>a+c.charCodeAt(0),0)}
    function managerSimSideName(report,side){return side==="home"?(report?.homeTeam?.name||report?.home||"Home"):(report?.awayTeam?.name||report?.away||"Away")}
    function managerSimSidePlayer(report,side,index=0){const all=(report?.players||[]).filter(p=>p.side===side),shots=all.filter(p=>Number(p.shots||0)>0),pool=shots.length?shots:all;return pool.length?(pool[index%pool.length]?.name||"Player"):"Player"}
    function managerSimSideAssist(report,side,scorer,index=0){const all=(report?.players||[]).filter(p=>p.side===side&&p.name&&p.name!==scorer);if(!all.length)return"";const score=p=>Number(p.passes||0)*2+Number(p.longPasses||0)*2.5+Number(p.widePasses||0)*2.2+Number(p.touches||0)*.5+Number(p.impact||0)*.2;const passers=all.filter(p=>Number(p.passes||0)>0||Number(p.longPasses||0)>0||Number(p.widePasses||0)>0),pool=(passers.length?passers:all).slice().sort((a,b)=>score(b)-score(a)||String(a.name||"").localeCompare(String(b.name||"")));return pool.length?(pool[index%pool.length]?.name||""):""}
    function managerSimTimeline(report){if(!report)return[];const seed=managerSimSeed(report),events=[],score=report.score||[0,0],disc=report.discipline||{},addGoal=(side,count,off)=>{for(let i=0;i<count;i++){let minute=clamp(Math.round(8+((i+1)/(count+1))*74+(((seed+off+i*11)%9)-4)),2,89),player=managerSimSidePlayer(report,side,i),assist=managerSimSideAssist(report,side,player,i);events.push({kind:"goal",side,minute,sort:minute+i*.07,player,assist,text:`${minute}' ${managerSimSideName(report,side)} goal - ${player}${assist?` | assist ${assist}`:""}`})}},addCount=(kind,side,count,label,base)=>{for(let i=0;i<Math.min(4,Number(count)||0);i++){let minute=clamp(base+i*17+((seed+i*5)%9)-4,6,88),player=managerSimSidePlayer(report,side,minute+i);events.push({kind,side,minute,sort:minute+i*.03,player,label,text:`${minute}' ${label}: ${managerSimSideName(report,side)}`})}};addGoal("home",Number(score[0]||0),3);addGoal("away",Number(score[1]||0),17);addCount("yellow","home",disc.yellows?.home,"Yellow card",24);addCount("yellow","away",disc.yellows?.away,"Yellow card",27);addCount("red","home",disc.reds?.home,"Red card",53);addCount("red","away",disc.reds?.away,"Red card",57);addCount("pen","home",disc.penalties?.home,"Penalty",35);addCount("pen","away",disc.penalties?.away,"Penalty",39);const fkHome=Number(disc.freeKicks?.home||0),fkAway=Number(disc.freeKicks?.away||0);if(fkHome+fkAway>0)events.push({kind:"free",side:fkHome>=fkAway?"home":"away",minute:31,sort:31.5,label:"Free kick",text:`31' Free kicks are building`});const moaner=report.highlights?.moaner;if(moaner)events.push({kind:"arguing",side:moaner.side||"",minute:66,sort:66.2,player:moaner.name||"Player",label:"Dissent",text:`66' Arguing: ${moaner.name||"Player"}`});return events.sort((a,b)=>a.sort-b.sort)}
    function managerSimLiveScore(report,progress=1){if(!report)return[0,0];if(progress>=.995)return[Number(report.score?.[0]||0),Number(report.score?.[1]||0)];let home=0,away=0;managerSimTimeline(report).filter(e=>e.kind==="goal"&&e.minute/90<=progress).forEach(e=>{if(e.side==="home")home++;else away++});return[home,away]}
    function managerSimGoalWhistles(report,prevProgress,progress){if(!report)return;managerSimTimeline(report).filter(e=>e.kind==="goal").forEach(e=>{const at=clamp(Number(e.minute||0)/90,0,1),key=`${report.id||"match"}:${e.side}:${e.minute}:${e.sort}`;if(at>prevProgress&&at<=progress&&!managerSimWhistledGoals.has(key)){managerSimWhistledGoals.add(key);ensureAudio();goalCheerSound();whistleSound()}})}
    function managerSimLog(report,progress=1,pressure=50){if(!report)return `<div class="sim-log-row">Match engine running...</div>`;const rows=managerSimTimeline(report).filter(e=>e.minute/90<=progress).slice(-6).map(e=>e.text);if(!rows.length)rows.push(Math.abs(Number(pressure||50)-50)<18?"ZzZz mode - midfield passing.":"Pressure is building.");if(progress>=.995){const hi=report.highlights||{};if(hi.worst)rows.push(`Underperformer: ${hi.worst.name||"Player"} (${hi.worst.behavior||""})`);if(hi.moaner&&!rows.some(x=>String(x).includes("Arguing")))rows.push(`Arguing: ${hi.moaner.name||"Player"} (${hi.moaner.teamName||""})`)}return rows.map(x=>`<div class="sim-log-row">${safe(x)}</div>`).join("")}
    function managerSimStatusText(state,progress=1,message=""){if(state==="waiting")return message||"Waiting";if(state==="running")return `${Math.max(1,Math.min(90,Math.round(clamp(progress,0,.995)*90)))}'`;return "Full Time"}
    function managerSimEventName(report,e,index=0){const player=e.player||managerSimSidePlayer(report,e.side,index);if(e.kind==="goal"){const assist=e.assist||managerSimSideAssist(report,e.side,player,index);return `<span class="sim-goal-stack"><span class="sim-goal-main"><b>${safe(player)}</b> <span class="sim-ball-icon"></span></span>${assist?`<small class="sim-assist-line">A: ${safe(assist)}</small>`:""}</span>`}if(e.kind==="yellow")return `<span class="sim-card-icon yellow"></span><b>${safe(player)}</b>`;if(e.kind==="red")return `<span class="sim-card-icon red"></span><b>${safe(player)}</b>`;if(e.kind==="pen")return `<span class="sim-event-tag">PEN</span><b>${safe(managerSimSideName(report,e.side))}</b>`;if(e.kind==="free")return `<b>Free kick</b>`;if(e.kind==="arguing")return `<span class="sim-event-tag">DISSENT</span><b>${safe(player)}</b>`;return safe(e.text||"Event")}
    function managerSimSummaryRows(report,progress=1,state="done"){
      if(!report)return `<div class="sim-summary-row"><span class="sim-summary-minute">--</span><div class="sim-summary-side">Match engine running...</div><b></b><div></div></div>`;
      const cutoff=state==="running"?clamp(Number(progress||0)*90,0,89.5):90,items=managerSimTimeline(report).filter(e=>Number(e.minute||0)<=cutoff);
      if(cutoff>=45)items.push({kind:"marker",minute:45,sort:45.5,label:"HT"});
      if(state!=="running"||progress>=.995)items.push({kind:"marker",minute:90,sort:90.5,label:"FT"});
      items.sort((a,b)=>(a.sort??a.minute)-(b.sort??b.minute));
      let home=0,away=0,rows=[];
      items.forEach((e,i)=>{
        if(e.kind==="goal"){if(e.side==="home")home++;else away++}
        if(e.kind==="marker"){rows.push(`<div class="sim-summary-row marker"><span class="sim-summary-minute">${safe(e.label)}</span><div></div><b>${home} - ${away}</b><div></div></div>`);return}
        const body=managerSimEventName(report,e,i),score=e.kind==="goal"?`${home} - ${away}`:"";
        rows.push(`<div class="sim-summary-row ${safe(e.side||"")} ${safe(e.kind||"")}"><span class="sim-summary-minute">${safe(e.minute)}'</span><div class="sim-summary-side sim-home-event">${e.side==="home"?body:""}</div><b>${safe(score)}</b><div class="sim-summary-side sim-away-event">${e.side==="away"?body:""}</div></div>`);
      });
      if(!rows.length)rows.push(`<div class="sim-summary-row"><span class="sim-summary-minute">${Math.max(1,Math.round(cutoff||1))}'</span><div class="sim-summary-side">Match settling in midfield.</div><b></b><div></div></div>`);
      return rows.join("");
    }
    function managerSimHtml(state="running",fixture=null,report=null,message="Simulating round...",progress=1,pressure=null){
      fixture=fixture||managerSimFixture()||report||{};
      const h=report?.homeTeam||managerSimTeam(fixture.home),a=report?.awayTeam||managerSimTeam(fixture.away),p=state==="running"?clamp(Number(progress)||0,0,.995):1,liveScore=report?managerSimLiveScore(report,p):null,score=liveScore?`${safe(liveScore[0])} - ${safe(liveScore[1])}`:(state==="running"?"0 - 0":"v"),press=pressure??(state==="running"?managerSimPlaybackPressure(report,p):(report?managerSimPressure(report):50)),status=managerSimStatusText(state,p,message),matchType=managerSimMatchType(fixture,report);
      return `<div class="sim-summary-page"><div class="sim-scoreboard"><div class="sim-board-team">${teamFlag(h)}<span>${safe(h?.name||fixture.home||"HOME")}</span></div><div class="sim-board-score"><b>${score}</b><span>${safe(status)}</span><small>${safe(matchType)}</small></div><div class="sim-board-team away">${teamFlag(a)}<span>${safe(a?.name||fixture.away||"AWAY")}</span></div></div><div class="sim-tabs"><span>Info</span><span class="active">Summary</span><span>Stats</span><span>Line-ups</span><span>H2H</span></div><div class="sim-momentum-card"><div><b>${safe(managerSimPressureText(h,a,press))}</b><span>${safe(message)}</span></div><div class="sim-pressure-track"><div id="managerSimPressureFill" class="sim-pressure-fill" style="${managerSimPressureStyle(press)}"></div></div></div><div class="sim-summary-table">${managerSimSummaryRows(report,p,state)}</div></div>`;
    }
    function managerSimScrollSnapshot(view){const table=view?.querySelector(".sim-summary-table");return table?{top:table.scrollTop,bottom:table.scrollTop+table.clientHeight>=table.scrollHeight-8}:null}
    function managerSimRestoreScroll(view,keep){const table=view?.querySelector(".sim-summary-table");if(!table||!keep)return;table.scrollTop=keep.bottom?table.scrollHeight:Math.min(keep.top,table.scrollHeight)}
    function renderManagerSimPage(state="running",fixture=null,report=null,message="Simulating round..."){
      fixture=fixture||managerSimFixture()||report||{};
      managerSimRunning=state==="running";
      $("managerSimView").innerHTML=managerSimHtml(state,fixture,report,message,state==="running"?0:1);
      const back=$("managerSimBack");
      if(back){back.classList.toggle("hide",state==="running");back.dataset.roundContinue="0";back.dataset.roundIndex="";back.dataset.roundPhase="";back.textContent=state==="done"?"CONTINUE":"BACK"}
      showManagerSection("managerSimPage",state==="running");
      syncManagerMenuMusic();
      if(state==="running"&&!managerSimTimer){const started=performance.now();let prev=0;managerSimTimer=setInterval(()=>{const p=clamp((performance.now()-started)/MANAGER_SIM_PLAYBACK_MS,0,.985),press=managerSimPlaybackPressure(report,p),view=$("managerSimView");managerSimGoalWhistles(report,prev,p);prev=p;if(view){const keep=managerSimScrollSnapshot(view);view.innerHTML=managerSimHtml("running",fixture,report,message,p,press);managerSimRestoreScroll(view,keep)}},260)}
      if(state!=="running")stopManagerSimAnimation();
    }
    function managerWaitNames(r={}){return (r.humanMatchWaiting||r.roundWaiting||effectiveRoundVote()?.waiting||[]).filter(Boolean)}
    function managerWaitText(r={},fallback="Waiting for other managers"){const names=managerWaitNames(r);return names.length?`Waiting for ${names.join(", ")}`:fallback}
    function openManagerLiveMatch(m){stopManagerPoll();setMatch(m);if(m?.status==="ready"){crowdMatchId=m.id;playCrowd()}show("match");pushAppHistory();renderMatch();startPoll()}
    function managerMainSimClick(){const action=String($("managerNextRound")?.dataset.managerSimAction||"");if(action==="continue")return continueManagerRound(managerRoundContinuePayload());if(action==="cpu")return playManagerCpuRound();return playManagerRound()}
	    async function playManagerRound(){
	      if(managerBusy)return;
	      const section=currentManagerSection(),teamUser=currentTeamDetailsUser;
	      const fixture=managerSimFixture();
	      if(!fixture)return playManagerCpuRound();
	      stopManagerSimAnimation();
	      setManagerBusy(true);msg("managerMsg","Loading match...");
	      try{
	        const r=await api("managerNextRound",managerNextRoundPayload(fixture));
	        await delay(MANAGER_SIM_LOAD_SETTLE_MS);
	        if(handleManagerInjuryBlock(r,"managerMsg")){stopManagerSimAnimation();await refreshManagerSectionAfterUpdate(section,teamUser);return}
	        if(!r.ok){stopCrowd();renderManagerSimPage("done",fixture,null,r.error==="match_in_progress"?"Finish watched match first":"Could not simulate round");return msg("managerMsg",r.error==="match_in_progress"?"Finish watched match first":"Could not simulate round")}
	        if(r.match){openManagerLiveMatch(r.match);return}
	        managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;syncManagerActions();renderManagerSeason();
	        if(r.staleRound){stopCrowd();renderManagerSeason();msg("managerMsg","");return}
	        if(r.simVotePending&&!r.managerFixtureSimulated){stopCrowd();renderManagerSeason();showManagerSection("managerHome",false);msg("managerMsg","");return}
	        const report=managerSimLatestReport(fixture);
	        renderManagerSimPage("running",fixture,report,"Match running");
	        playCrowd();
	        msg("managerMsg","");
	        await delay(MANAGER_SIM_PLAYBACK_MS);
	        stopCrowd();
	        const doneText="Match done";
	        renderManagerSimPage("done",fixture,report,doneText);
	        msg("managerMsg","")
      }finally{setManagerBusy(false)}
    }
    async function playManagerCpuRound(){
      if(managerBusy)return;
      const label=managerCpuOnlyActionLabel(),section=currentManagerSection(),teamUser=currentTeamDetailsUser;
      stopManagerSimAnimation();
      setManagerBusy(true);msg("managerMsg",`${label}...`);
      try{
        const r=await api("managerNextRound",managerNextRoundPayload());
        if(handleManagerInjuryBlock(r,"managerMsg")){await refreshManagerSectionAfterUpdate(section,teamUser);return}
        if(!r.ok)return msg("managerMsg",r.error==="match_in_progress"?"Finish watched match first":"Could not update season");
        managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;managerMarket=r.managerMarket||managerMarket;if(r.managerTransferOffers)setManagerTransferOffers(r.managerTransferOffers);syncManagerActions();renderManagerSeason();
        if(r.staleRound)return msg("managerMsg","");
        if(r.simVotePending){msg("managerMsg","");return}
        msg("managerMsg",managerSeason?.status==="finished"?"Season finished":`${label} done`);
      }finally{setManagerBusy(false)}
    }
    async function playManagerSeason(messageId="managerMsg",adminOverride=false){
      if(managerBusy)return;
      if(!managerSeason)return msg(messageId,"No active manager league");
      if(!await askConfirm("SIM SEASON","Simulate the whole season until everything is finished?\nNext season will not start until you press NEXT SEASON.","SIM SEASON","CANCEL",false))return;
      setManagerBusy(true);msg(messageId,"Marking season ready...");
      try{
        const r=await api("managerSimSeason",{simSeasonKey:managerSeason?.simSeasonVote?.key||"",adminOverride:!!adminOverride});
        if(handleManagerInjuryBlock(r,messageId))return;
        if(!r.ok)return msg(messageId,r.error==="match_in_progress"?"Finish watched match first":r.error==="no_season"?"No active manager league":"Could not simulate season");
        managerSeason=r.managerSeason||managerSeason;
        managerFinance=r.managerFinance||managerFinance;
        managerSquad=r.managerSquad||managerSquad;
        managerMarket=r.managerMarket||managerMarket;
        if(r.managerTransferOffers)setManagerTransferOffers(r.managerTransferOffers);
        syncManagerActions();renderManagerSeason();syncAdminControls();
        if(r.staleSeason)return msg(messageId,"League updated. Check the table.");
        if(r.simSeasonVotePending){const v=managerSeason?.simSeasonVote;return msg(messageId,messageId==="managerMsg"?"":(v?.waiting?.length?`Waiting for ${v.waiting.join(", ")}`:"Waiting for other managers"))}
        msg(messageId,r.seasonSimulated?"Season simulated. Press NEXT SEASON when you are ready.":"Season sim stopped before finish")
      }finally{setManagerBusy(false)}
    }
    async function watchManagerMatch(){ensureAudio();const r=await api("managerWatchMatch");if(handleManagerInjuryBlock(r,"managerMsg"))return;if(!r.ok)return msg("managerMsg",r.error==="current_match"?"Leave current match first":r.error==="no_fixture"?"No match for your user in this league":"Could not open match");if(r.simVotePending&&!r.match){managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;syncManagerActions();renderManagerSeason();return msg("managerMsg","")}openManagerLiveMatch(r.match)}
    async function continueManagerRound(payload=null){
      if(managerBusy)return;
      stopManagerSimAnimation();
      let autoSimNext=false;
      setManagerBusy(true);msg("managerMsg","");
      try{
        const requestPayload=payload&&!("currentTarget" in payload)&&!("target" in payload)?payload:managerRoundContinuePayload();
        const r=await api("managerRoundContinue",requestPayload);
        if(r.managerSeason)managerSeason=r.managerSeason;
        if(r.managerFinance)managerFinance=r.managerFinance;
        if(r.managerSquad)managerSquad=r.managerSquad;
        if(r.managerMarket)managerMarket=r.managerMarket;
        if(r.managerTransferOffers)setManagerTransferOffers(r.managerTransferOffers);
        syncManagerActions();renderManagerSeason();showManagerSection("managerHome",false);
        if(!r.ok)return msg("managerMsg",r.error==="no_season"?"No active manager league":r.error==="not_your_season"?"Not your manager league":"Match is not ready to continue");
        if(r.staleRound)return msg("managerMsg","");
        const wait=managerWaitNames(r);
        autoSimNext=!wait.length&&!!r.managerRoundAdvanced&&!!nextManagerFixturePreview();
        msg("managerMsg",wait.length||autoSimNext?"":"Round ready");
      }finally{setManagerBusy(false)}
      if(autoSimNext)return playManagerRound();
    }
    async function continueManagerSeason(){const r=await api("continueManagerSeason");if(!r.ok){if(r.managerSeason)managerSeason=r.managerSeason;if(r.managerFinance)managerFinance=r.managerFinance;if(r.managerSquad)managerSquad=r.managerSquad;if(r.managerMarket)managerMarket=r.managerMarket;if(r.managerTransferOffers)setManagerTransferOffers(r.managerTransferOffers);setManagerStateFlags({managerHasSeason:!!managerSeason,managerCanContinue:r.managerCanContinue??!!managerSeason,managerHasAnySave:r.managerHasAnySave});syncManagerActions();renderManagerSeason();return msg("managerMsg",r.error==="season_year_wait"?`Waiting for ${(r.waitingUsers||[]).join(", ")}`:r.error==="season_not_finished"?"Season is not finished yet":"Could not continue season")}managerSeason=r.managerSeason||null;setManagerStateFlags({managerHasSeason:!!managerSeason,managerCanContinue:r.managerCanContinue??!!managerSeason,managerHasAnySave:r.managerHasAnySave});managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;managerMarket=r.managerMarket||managerMarket;if(r.managerTransferOffers)setManagerTransferOffers(r.managerTransferOffers);syncManagerActions();renderManagerSeason();msg("managerMsg","Next season started")}
    function showAbortManagerConfirm(){managerAbortConfirm=true;renderManagerSeason()}
    async function abortManagerLeague(){const r=await api("abortManagerSeason");if(!r.ok)return msg("managerMsg","Could not abort league");managerAbortConfirm=false;managerSeason=null;managerHasSeason=false;managerCanContinue=false;managerFinance=r.managerFinance||managerFinance;syncManagerActions();goMainMenu(true);msg("menuMsg","League aborted")}
    async function buyManagerPlayer(id,offer={}){const wanted=(managerMarket||[]).find(p=>p.id===id);const r=await api("buyPlayer",{playerId:id,...offer});if(!r.ok)return msg("managerMsg",r.error==="bench_full"?benchFullText(r):r.error==="no_money"?"Not enough money":r.error==="human_transfer_locked"?"Use Browse and send a deal offer to that manager":r.error==="low_offer"?"Bid is too low":r.error==="no_replacement"?"Need a bench player before offering a starter":"Could not buy player");managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;managerMarket=r.managerMarket||managerMarket;if(r.managerTransferOffers)setManagerTransferOffers(r.managerTransferOffers);if(r.status==="human_offer"){managerMarketNotice=`<b>OFFER SENT</b>${safe(r.message||"Waiting for manager approval")}`;renderManagerMarketPage();return msg("managerMsg","Offer sent")}managerMarketNotice=signingNotice(r.boughtPlayer||wanted,r.skillChange);renderManagerSeason();renderManagerMarketPage();msg("managerMsg",`${(r.boughtPlayer||wanted)?.name||"Player"} signed for ${money(r.cashPrice??r.price??r.boughtPlayer?.purchasePrice??0)}${r.offerValue?` + player value ${money(r.offerValue)}`:""}${r.skillChange?.delta?` | skill ${r.skillChange.before}->${r.skillChange.after}`:""}`)}
    async function sellManagerPlayer(id,offer={}){const r=await api("sellPlayer",{playerId:id,...offer});if(!r.ok)return msg("managerMsg",r.error==="no_buyer"?`No buyer at that price${r.chance?` (${r.chance}% chance)`:""}. Lower price and try again.`:r.error==="no_replacement"?"Need a bench player first":"Could not sell player");managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance;managerSquad=r.managerSquad;managerMarket=r.managerMarket||managerMarket;if(r.managerTransferOffers)setManagerTransferOffers(r.managerTransferOffers);renderManagerSeason();renderManagerSquadPage();msg("managerMsg",r.salePending?`Player listed for ${money(r.salePrice||offer.cashOffer||0)}. CPU bids can take 1-10 matches.`:`Player sold for ${money(r.salePrice||offer.cashOffer||0)}`)}
    async function finishBrowseTransfer(r,wanted){managerSeason=r.managerSeason||managerSeason;managerFinance=r.managerFinance||managerFinance;managerSquad=r.managerSquad||managerSquad;managerBrowseNotice=signingNotice(r.boughtPlayer||wanted,r.skillChange);renderManagerSeason();await openManagerBrowseTeam(managerBrowseTeam);const cash=Number(r.cashPrice??r.price??r.boughtPlayer?.purchasePrice??0),count=Number(r.offerPlayerCount||0),playerText=r.offerValue?` + ${count>1?"players":"player"} value ${money(r.offerValue)}`:"";msg("managerMsg",`${(r.boughtPlayer||wanted)?.name||"Player"} signed for ${money(cash)}${playerText}`)}
    async function browseTransferBid(teamUser,id,offer={}){const r=await api("browseTransferBid",{teamUser,playerId:id,...offer});if(!r.ok)return msg("managerMsg",r.error==="too_many_offer_players"?`Max ${r.maxPlayers||3} players in one bid`:r.error==="bench_full"?benchFullText(r):r.error==="no_money"?"Not enough money":r.error==="human_transfer_locked"?"That manager is not available for deals":r.error==="no_replacement"?"Need enough bench cover before offering those players":"Could not make bid");if(r.managerTransferOffers)setManagerTransferOffers(r.managerTransferOffers);if(r.status==="human_offer"){managerBrowseNotice=`<b>OFFER SENT</b>${safe(r.message||"Waiting for manager approval")}`;await openManagerBrowseTeam(teamUser);return msg("managerMsg","Offer sent")}if(r.status==="rejected"){managerBrowseNotice=`<b>BID REJECTED</b>${safe(r.message||"Club rejected the bid")}`;await openManagerBrowseTeam(teamUser);return msg("managerMsg","Bid rejected")}if(r.status==="counter"){const player=r.player?.name||"Player",cash=Number(r.price||0),total=Number(r.counterTotal||r.offerTotal||cash),count=Number(r.offerPlayerCount||0),extra=r.offerValue?` including ${count>1?`${count} players`:"player"} value ${money(r.offerValue)}`:"",ok=await askConfirm("TRANSFER COUNTER",`${player}: club wants total ${money(total)}. Cash needed ${money(cash)}${extra}. Accept?`,"ACCEPT","DECLINE",false);if(!ok){managerBrowseNotice=`<b>COUNTER DECLINED</b>${safe(player)} stays at ${safe(r.team?.name||"the club")}`;await openManagerBrowseTeam(teamUser);return msg("managerMsg","Counter declined")}const a=await api("acceptBrowseTransfer",{offerId:r.offerId});if(!a.ok)return msg("managerMsg",a.error==="bench_full"?benchFullText(a):a.error==="no_money"?"Not enough money for counter offer":a.error==="no_replacement"?"Need enough bench cover before offering those players":"Could not accept counter offer");return finishBrowseTransfer(a,r.player)}if(r.status==="bought")return finishBrowseTransfer(r,r.boughtPlayer||r.player)}
    async function submitTransferOffer(){if(!transferOffer)return;const offer=transferOfferPayload(),kind=transferOffer.kind,id=transferOffer.player?.id,teamUser=transferOffer.teamUser,offerId=transferOffer.offerId;closeTransferOffer();if(kind==="counter")return counterManagerDeal(offerId,offer);if(kind==="market")return buyManagerPlayer(id,offer);if(kind==="sell")return sellManagerPlayer(id,offer);return browseTransferBid(teamUser,id,offer)}
    function openOptions(){setRoute("");renderUserTop();updateSoundButton();msg("optionsMsg","");show("optionsPage");pushAppHistory()}
    $("openLogin").onclick=()=>show("auth");
    $("showLogin").onclick=()=>{$("loginForm").classList.remove("hide");$("createForm").classList.add("hide");$("showCreate").classList.remove("hide");$("showLogin").classList.add("hide");msg("authMsg","")};
    $("showCreate").onclick=()=>{$("createForm").classList.remove("hide");$("loginForm").classList.add("hide");$("showCreate").classList.add("hide");$("showLogin").classList.remove("hide");msg("authMsg","")};
    $("transferBidMinus").onclick=()=>changeTransferBid(-1);
    $("transferBidPlus").onclick=()=>changeTransferBid(1);
    $("transferOfferPlayerButton").onclick=e=>{e.stopPropagation();$("transferOfferPlayerList").classList.toggle("hide")};
    $("transferOfferPlayerList").onclick=e=>{const b=e.target.closest("[data-transfer-offer-player]");if(!b)return;e.stopPropagation();chooseTransferOfferPlayer(b.dataset.transferOfferPlayer||"")};
    $("transferOfferCancel").onclick=closeTransferOffer;
    $("transferOfferSubmit").onclick=submitTransferOffer;
    $("transferOfferModal").onclick=e=>{if(e.target===$("transferOfferModal"))closeTransferOffer()};
    $("loginForm").onsubmit=async e=>{e.preventDefault();const d=Object.fromEntries(new FormData(e.target));const r=await api("login",d);if(!r.ok)return msg("authMsg","Wrong login");msg("authMsg","");await state()};
    $("createForm").onsubmit=async e=>{e.preventDefault();const d=Object.fromEntries(new FormData(e.target));const r=await api("register",d);if(!r.ok)return msg("authMsg",r.error==="user_exists"?"Login name exists":r.error==="bad_username"?"Login name must be 3-24 letters/numbers":r.error==="short_password"?"Password must be at least 3 characters":"Could not create account");msg("authMsg","");await state()};
    $("teamDropdown").onclick=e=>{e.stopPropagation();$("teamDropdownList").classList.toggle("hide")};
    document.addEventListener("click",e=>{if(!$("teamPicker").contains(e.target))$("teamDropdownList").classList.add("hide");const p=$("transferOfferPlayerPicker");if(p&&!p.contains(e.target))closeTransferOfferPlayerMenu()});
    function runManagerAction(v){if(!v||managerInteractionBusy())return;closeManagerMenus();if(v==="home"){renderManagerSeason();showManagerSection("managerHome");return}if(v==="play")watchManagerMatch();if(v==="simulate")playManagerRound();if(v==="simSeason")playManagerSeason();if(v==="continue")continueManagerSeason();if(v==="create")openManagerCreate();if(v==="scorers")renderManagerScorersPage(true);if(v==="assists")renderManagerAssistsPage();if(v==="cups")renderManagerCupsPage();if(v==="reports")renderManagerReportsPage();if(v==="fixtures")renderManagerFixturesPage();if(v==="viewTeams")renderManagerViewTeamsPage();if(v==="economy")renderManagerEconomyPage();if(v==="transfers")renderManagerTransfersPage();if(v==="lineup")openManagerTeam(me?.name);if(v==="trophies")renderManagerTrophiesPage();if(v==="statsInfo")renderManagerStatsInfoPage();if(v==="options")openOptions();if(v==="abort")showAbortManagerConfirm();if(v==="squad"){renderManagerSquadPage();showManagerSection("managerSquadPage")}if(v==="market"){managerMarketNotice="";renderManagerMarketPage();showManagerSection("managerMarketPage")}if(v==="browse"){managerBrowseNotice="";renderManagerBrowsePage(managerBrowseLevel||managerFinance?.divisionLevel||5)}}
    $("managerActionButton").onclick=e=>{e.stopPropagation();if(managerInteractionBusy())return;setManagerActionMenuOpen($("managerActionList").classList.contains("hide"))};
    $("managerActionList").onclick=e=>{const b=e.target.closest("[data-manager-action]");if(!b||b.disabled)return;runManagerAction(b.dataset.managerAction)};
    $("managerViewTeamsPageList").onclick=e=>{const b=e.target.closest("[data-manager-view-team]");if(!b||b.disabled)return;renderManagerDivision(b.dataset.managerViewTeam)};
    $("managerUsers").onclick=e=>{const b=e.target.closest("[data-user]");if(!b||b.disabled)return;toggleManagerUser(b.dataset.user)};
    document.addEventListener("click",e=>{const m=$("managerActionMenu");if(m&&!m.contains(e.target))closeManagerActionMenu()});
    $("teamForm").onsubmit=async e=>{e.preventDefault();const d=Object.fromEntries(new FormData(e.target));if(!d.teamId)return msg("teamMsg","Choose a team");const r=await api("selectTeam",d);if(!r.ok)return msg("teamMsg",r.error==="team_locked"?"This account already has a team":"Choose another team");msg("teamMsg","");await state()};
    async function logout(){stopManagerPoll();await api("logout");setRoute("");me=null;setMatch(null);renderUserTop();appHistoryDepth=0;show("auth");replaceAppHistory()}
    async function resetGameData(){if(me?.name!=="jdc")return;if(!await askConfirm("RESET GAME DATA","Accounts and selected teams stay.\nMatches, manager leagues, squads, economy and progress are reset.","RESET","CANCEL"))return;const r=await api("resetGameData");if(!r.ok)return msg("menuMsg","Could not reset game data");setMatch(null);managerSeason=null;managerHasSeason=false;managerCanContinue=false;managerHasAnySave=false;managerInvites=[];managerFinance=null;managerSquad=null;managerMarket=[];setManagerTransferOffers({incoming:[],outgoing:[]});await state();msg("menuMsg","Game data reset")}
    async function loadAdminManager(){const r=await api("managerSetup");if(!r.ok){msg("adminMsg","Could not load manager league");return false}managerUsers=r.managerUsers||[];managerSeason=r.managerSeason||null;managerInvites=r.managerInvites||[];managerFinance=r.managerFinance||null;managerSquad=r.managerSquad||null;managerMarket=r.managerMarket||[];managerDivisions=r.managerDivisions||[];setManagerTransferOffers(r.managerTransferOffers);setManagerStateFlags({managerHasSeason:!!managerSeason,managerCanContinue:r.managerCanContinue??!!managerSeason,managerHasAnySave:r.managerHasAnySave});renderUserTop();syncMenuControls();syncManagerActions();syncAdminControls();return !!managerSeason}
    async function adminSimSeason(){if(me?.name!=="jdc")return;if(!managerSeason&&!await loadAdminManager())return msg("adminMsg","No active manager league");if(managerSeason?.status==="finished")return msg("adminMsg","Season already finished");await playManagerSeason("adminMsg",true)}
    async function openAdmin(){if(me?.name!=="jdc")return;stopPoll();stopManagerPoll();stopCrowd();setMatch(null);setRoute("");renderUserTop();msg("adminMsg","");show("admin");syncAdminControls();pushAppHistory();if(managerHasSeason&&!managerSeason)await loadAdminManager()}
    async function resetManagerLeague(){if(me?.name!=="jdc")return;if(!await askConfirm("ARE YOU SURE?","Reset manager league?\nSelected teams stay.\nLeagues, matches, squads, economy, transfers and progress reset.","YES","NO"))return;const r=await api("resetManagerLeague");if(!r.ok)return msg("adminMsg","Could not reset manager league");stopManagerPoll();setRoute("");setMatch(null);managerSeason=null;managerHasSeason=false;managerCanContinue=false;managerHasAnySave=false;managerInvites=[];managerFinance=null;managerSquad=null;managerMarket=[];setManagerTransferOffers({incoming:[],outgoing:[]});managerDivisions=[];teamChoices=r.teams||teamChoices;me=r.user||me;renderUserTop();goMainMenu(true);msg("menuMsg","Manager league reset")}
    async function resetEverything(){if(me?.name!=="jdc")return;if(!await askConfirm("ARE YOU SURE?","Reset everything?\nThis clears selected teams, squads, player stats, manager seasons, matches, transfers, economy and manager saves.\nLogin accounts stay. Everyone must choose team again after this.","YES","NO"))return;const r=await api("resetEverything");if(!r.ok)return msg("adminMsg","Could not reset everything");setRoute("");setMatch(null);managerSeason=null;managerHasSeason=false;managerCanContinue=false;managerHasAnySave=false;managerSaves=[];managerInvites=[];managerFinance=null;managerSquad=null;managerMarket=[];managerDivisions=[];setManagerTransferOffers({incoming:[],outgoing:[]});teamChoices=r.teams||[];me=r.user||me;msg("adminMsg","Everything reset");await state()}
    $("logout").onclick=$("logoutTeam").onclick=$("appTopLogout").onclick=logout;
    $("adminMenu").onclick=openAdmin;
    $("adminTraining").onclick=openTrainingMenu;
    $("adminSimSeason").onclick=adminSimSeason;
    $("adminBack").onclick=()=>{msg("adminMsg","");goBackOrMenu()};
    $("resetManagerLeague").onclick=resetManagerLeague;
    $("resetEverything").onclick=resetEverything;
    $("resetGameData").onclick=resetGameData;
    $("options").onclick=openOptions;
    $("optionsBack").onclick=()=>{msg("optionsMsg","");goBackOrMenu()};
    $("soundToggle").onclick=()=>setSoundEnabled(!soundAllEnabled());
    $("optionsForm").onsubmit=async e=>{e.preventDefault();const d=Object.fromEntries(new FormData(e.target));const r=await api("changePassword",d);msg("optionsMsg",r.ok?"Password saved":r.error==="short_password"?"Password too short":"Wrong current password");if(r.ok)e.target.reset()};
    $("trainingMenuBack").onclick=()=>{msg("trainingMenuMsg","");goBackOrMenu()};
    $("managerSaveButton").onclick=()=>openManagerSaves(true);
    $("managerSavesBack").onclick=()=>{msg("managerSavesMsg","");goBackOrMenu()};
    $("continueManagerMode").onclick=openManager;
    $("managerMode").onclick=openManagerStart;
    $("startNewManagerMode").onclick=startNewSoloManagerMode;
    $("managerPowerOff").onclick=()=>{msg("managerMsg","");goMainMenu(true)};
    $("managerBack").onclick=()=>{msg("managerMsg","");if(currentManagerSection()==="managerHome"){stopManagerPoll();setRoute("");goMainMenu(true);return}if(currentManagerSection()==="managerTeamPage"){backFromManagerTeam();return}goBackOrMenu()};
    $("cancelManagerCreate").onclick=()=>{if(managerSeason)goBackOrMenu();else goBackOrMenu()};
    $("managerSquadBack").onclick=goBackOrMenu;
    $("managerFullSquadBack").onclick=goBackOrMenu;
    $("managerMarketBack").onclick=goBackOrMenu;
    $("managerEconomyBack").onclick=goBackOrMenu;
    $("managerTransfersBack").onclick=goBackOrMenu;
    $("managerBrowseBack").onclick=goBackOrMenu;
    $("managerBrowseTeamBack").onclick=goBackOrMenu;
    $("transfersSquadSell").onclick=()=>{renderManagerSquadPage();showManagerSection("managerSquadPage")};
    $("transfersMarket").onclick=()=>{managerMarketNotice="";renderManagerMarketPage();showManagerSection("managerMarketPage")};
    $("transfersBrowse").onclick=()=>{managerBrowseNotice="";renderManagerBrowsePage(managerBrowseLevel||managerFinance?.divisionLevel||5)};
    $("transfersDeals").onclick=()=>{renderManagerDealOffers();$("managerDealOffers")?.scrollIntoView({block:"nearest"})};
    $("managerDivisionBack").onclick=goBackOrMenu;
    $("managerScorersBack").onclick=goBackOrMenu;
    $("managerAssistsBack").onclick=goBackOrMenu;
    $("managerReportsBack").onclick=goBackOrMenu;
    $("managerCupsBack").onclick=goBackOrMenu;
    $("managerSimBack").onclick=()=>{stopManagerSimAnimation();renderManagerSeason();showManagerSection("managerHome");refreshManager()};
    $("managerFixturesBack").onclick=goBackOrMenu;
    $("managerViewTeamsPageBack").onclick=goBackOrMenu;
    $("managerStatsInfoBack").onclick=goBackOrMenu;
    $("managerTrophiesBack").onclick=goBackOrMenu;
    $("managerTeamBack").onclick=backFromManagerTeam;
    $("startManagerLeague").onclick=startManagerLeague;
    $("trainingDivisionButton").onclick=e=>{e.stopPropagation();closeTrainingDropdowns("division");$("trainingDivisionList").classList.toggle("hide")};
    $("trainingTeamButton").onclick=e=>{e.stopPropagation();closeTrainingDropdowns("team");$("trainingTeamList").classList.toggle("hide")};
    document.addEventListener("click",e=>{if(!$("trainingDivisionPicker").contains(e.target)&&!$("trainingTeamPicker").contains(e.target))closeTrainingDropdowns()});
    $("speedScenario").onclick=()=>openTraining("speed");
    $("shootScenario").onclick=()=>openTraining("shoot");
    $("startSpeedTest").onclick=startSpeedRace;
    $("startShootTest").onclick=startShootTest;
    $("clearSpeedTest").onclick=clearTrainingSelection;
    $("trainingBack").onclick=closeTraining;
    $("friendly").onclick=async()=>{ensureAudio();msg("menuMsg","");msg("trainingMenuMsg","");const r=await api("find");if(!r.ok)return msg("trainingMenuMsg",r.error==="no_team"?"Choose team first":"Login required");trainingMode=false;trainingRace=null;trainingShot=null;setMatch(r.match);if(r.match?.status==="ready"){crowdMatchId=r.match.id;playCrowd()}show("match");pushAppHistory();renderMatch();startPoll()};
    $("cpuMatch").onclick=async()=>{ensureAudio();msg("menuMsg","");msg("trainingMenuMsg","");const r=await api("cpu");if(!r.ok)return msg("trainingMenuMsg",r.error==="no_team"?"Choose team first":"Leave current match first");trainingMode=false;trainingRace=null;trainingShot=null;setMatch(r.match);if(r.match?.status==="ready"){crowdMatchId=r.match.id;playCrowd()}show("match");pushAppHistory();renderMatch();startPoll()};
    $("startMatch").onclick=async()=>{ensureAudio();const r=await api("start");if(!r.ok)return;setMatch(r.match);playCrowd();renderMatch();startPoll()};
    $("skipLineup").onclick=()=>{lineupUntil=performance.now();renderMatch()};
    $("subMatch").onclick=async()=>{ensureAudio();const r=await api("substitute");if(!r.ok){$("subMatch").textContent=r.error==="subs_used"?`BYTEN ${MAX_MATCH_SUBS}/${MAX_MATCH_SUBS}`:"NO SUB";setTimeout(renderMatch,800);return}setMatch(r.match);renderMatch()};
    async function leaveMatchToMenu(){const managerMatch=isManagerMatch();await api("leave");setMatch(null);stopPoll();if(appHistoryDepth>0){history.back();return}await returnFromMatch(managerMatch)}
    $("cancelWait").onclick=leaveMatchToMenu;
    $("toMenu").onclick=async()=>{if(trainingMode){toggleTrainingMenu();return}await leaveMatchToMenu()};
    $("matchMenu").onclick=leaveMatchToMenu;
    $("leaveMatch").onclick=leaveMatchToMenu;
    $("refreshMatch").onclick=state;
    document.querySelectorAll("[data-mode]").forEach(b=>b.onclick=async()=>{ensureAudio();document.querySelectorAll("[data-mode]").forEach(x=>x.classList.remove("green"));b.classList.add("green");await api("tactic",{mode:b.dataset.mode});await state()});
    async function pollState(){if(pollBusy)return;pollBusy=true;try{await state(true)}finally{pollBusy=false}}
    function startPoll(){stopPoll();poll=setInterval(pollState,POLL_MS)}
    function stopPoll(){if(poll)clearInterval(poll);poll=null;pollBusy=false}
    function layout(){const c=$("field"),r=c.getBoundingClientRect(),d=Math.min(2,devicePixelRatio||1);W=Math.max(320,r.width|0);H=Math.max(320,r.height|0);let cw=Math.round(W*d),ch=Math.round(H*d);if(c.width!==cw||c.height!==ch){c.width=cw;c.height=ch}const g=c.getContext("2d");g.setTransform(d,0,0,d,0,0);let port=W/H<.8,ratio=68/105;if(port){let mm=96/25.4,edge=6,sideOut=mm,goalOut=mm,top=Math.max(72,Math.min(92,H*.11)),controlsTop=H-78,hLimit=Math.max(240,(controlsTop-top-goalOut-12)/(1+2.44/105)),w=Math.min(W-2*(edge+sideOut),hLimit*ratio),h=w/ratio;p={x:(W-w)/2,y:top,w,h};return g}let top=Math.max(70,H*.09),bot=Math.max(150,H*.18),side=Math.max(40,W*.04),aw=W-2*side,ah=Math.max(240,H-top-bot),w=Math.min(aw*.55,ah*ratio),h=Math.min(ah,w/ratio),down=Math.min(76,Math.max(54,H*.085));p={x:(W-w)/2,y:top+(ah-h)/2+down,w,h};return g}
    function showIntroNow(ready){if(!ready||!match)return false;const now=performance.now();if(introMatchId!==match.id){introMatchId=match.id;introUntil=now+INTRO_MS;if(crowdMatchId!==match.id){crowdMatchId=match.id;playCrowd()}}return now<introUntil}
    function showLineupNow(ready){if(!ready||!match)return false;const now=performance.now();if(lineupMatchId!==match.id){lineupMatchId=match.id;lineupUntil=now+LINEUP_MS}return now<lineupUntil}
    function introFrame(frame){if(!match||introMatchId!==match.id||performance.now()>=introUntil)return frame;let base=clamp(1-(introUntil-performance.now())/INTRO_MS,0,1),out={ball:frame.ball,units:{home:{},away:{}}},ease=t=>t*t*(3-2*t);["home","away"].forEach(side=>{Object.keys(frame.units[side]||{}).forEach((id,i)=>{let v=frame.units[side][id],targetY=side==="home"?Math.max(v.y,.535):Math.min(v.y,.465),st=ease(clamp((base-i*.016)/.86,0,1)),lane=side==="away"?.055:0,x0=1.18+lane+i*.01,y0=.5,mid={x:.62+lane-(i%4)*.018,y:.5};if(st<.46){let q=ease(st/.46);out.units[side][id]={x:lerp(x0,mid.x,q),y:y0}}else{let q=ease((st-.46)/.54);out.units[side][id]={x:lerp(mid.x,v.x,q),y:lerp(mid.y,targetY,q)}}})});return out}
    function lineupPosition(i,u){const hx=Number(u.homeX??u.x),hy=Number(u.homeY??u.y);if(Number.isFinite(hx)&&Number.isFinite(hy))return{x:clamp(hx,.08,.92),y:clamp(hy,.10,.86)};const base=[[.50,.82],[.18,.66],[.38,.68],[.62,.68],[.82,.66],[.16,.47],[.38,.50],[.62,.50],[.84,.47],[.38,.25],[.62,.25]],roles=["GK","LB","CB","CB","RB","LM","CM","CM","RM","ST","ST"],role=u.slotRole||u.role||"",p=base.find((_,n)=>roles[n]===role)||base[i]||[.5,.5];return{x:p[0],y:p[1]}}
    function renderLineup(){if(!match)return;const home=match.teams.home,away=match.teams.away;$("lineHome").textContent=home.name;$("lineAway").textContent=away.name;const draw=(id,team,units)=>{let flag=`<span class="line-flag" style="--shirt:${safe(team.shirt||"#fff")};--trim:${safe(team.trim||"#111")}"></span>`,name=`<div class="line-team-name">${safe(team.name||"TEAM")}</div>`,players=(units||[]).map((u,i)=>{let pos=lineupPosition(i,u),x=pos.x*100,y=pos.y*100;return `<div class="line-player" style="left:${x}%;top:${y}%;--shirt:${safe(team.shirt||"#fff")};--trim:${safe(team.trim||"#111")}"><span class="line-head"></span><span>${safe(u.name||u.id||u.role||"Player")}</span></div>`}).join("");$(id).innerHTML=flag+players+name};draw("lineHomeGrid",home,match.units?.home);draw("lineAwayGrid",away,match.units?.away)}
    function renderMatch(){const waiting=match&&match.status==="waiting",ready=match&&match.status==="ready",active=match&&["ready","active","finished"].includes(match.status),intro=showIntroNow(ready),lineup=!intro&&showLineupNow(ready);syncManagerMenuMusic();maintainCrowd();renderGoalBanner();$("wait").classList.toggle("hide",!waiting);$("hud").classList.toggle("hide",!active);$("controls").classList.toggle("hide",!active||intro||lineup);$("toMenu").classList.add("hide");$("lineup").classList.toggle("hide",!lineup);$("startMatch").classList.toggle("hide",!ready||intro||lineup);if(lineup)renderLineup();if(waiting){$("waitText").textContent="WAITING FOR OPPONENT";drawEmpty();return}if(!active){drawEmpty();return}const home=match.teams.home,away=match.teams.away,rt=match.ratings||{},frame=introFrame(sampleFrame()),side=match.side||"home",subs=match.engine?.subsUsed?.[side]||0;$("homeName").innerHTML=teamLabel(home,rt.home,false);$("awayName").innerHTML=teamLabel(away,rt.away,true);$("scoreScorers").innerHTML=goalScorerHtml(match.engine?.goalScorers||[]);$("subMatch").textContent=`BYTEN ${subs}/${MAX_MATCH_SUBS}`;$("subMatch").disabled=subs>=MAX_MATCH_SUBS;$("hs").textContent=match.score[0];$("as").textContent=match.score[1];let total=Math.max(0,Math.min(90*60,Math.floor((match.elapsed||0)*60)));$("time").textContent=String(total/60|0).padStart(2,"0")+":"+String(total%60).padStart(2,"0");$("events").innerHTML=(match.events||[]).slice(-5).map(eventLineHtml).join("");drawField(home,away,frame)}
    function drawEmpty(){const g=layout();g.fillStyle="#050407";g.fillRect(0,0,W,H)}
    function drawGoalFlutter(g,gx,gd,gw){if(!goalFx)return;const age=(performance.now()-goalFx.at)/1000;if(age>2.3){goalFx=null;return}const top=goalFx.net==="top",baseY=top?-gd:p.h,fade=1-age/2.3,amp=gd*(.12+.30*fade),phase=age*20;g.save();g.strokeStyle=`rgba(255,255,255,${.22+.62*fade})`;g.lineWidth=Math.max(1,p.w*.004);for(let i=0;i<=7;i++){let x=gx+gw*i/7,wig=Math.sin(phase+i*.8)*amp;g.beginPath();g.moveTo(x,top?-gd:p.h);g.quadraticCurveTo(x+wig*.42,baseY+(top?gd*.5:-gd*.5),x+wig,top?0:p.h);g.stroke()}for(let j=1;j<=4;j++){let y=top?-gd+gd*j/5:p.h+gd*j/5,wig=Math.sin(phase+j)*amp*.62;g.beginPath();g.moveTo(gx,y);g.quadraticCurveTo(gx+gw*.5,y+wig,gx+gw,y-wig*.35);g.stroke()}g.restore()}
    function visualBallOwner(frame){if(trainingMode)return null;const engineOwner=match?.engine?.owner;if(engineOwner?.side&&engineOwner?.id)return engineOwner;const ball=frame?.ball||match?.ball;if(!match||!ball||(ball.z||0)>.035||match?.engine?.shot)return null;let best=null,bestD=.0009;["home","away"].forEach(side=>{(match.units?.[side]||[]).forEach(u=>{const v=frame?.units?.[side]?.[u.id]||u,dx=(v.x??u.x)-ball.x,dy=(v.y??u.y)-ball.y,d=dx*dx+dy*dy;if(d<bestD){bestD=d;best={side,id:u.id}}})});return best}
    function drawField(home,away,frame){const g=layout(),ball=frame.ball||match.ball||{x:.5,y:.5,z:0},owner=visualBallOwner(frame);g.fillStyle="#050407";g.fillRect(0,0,W,H);g.fillStyle="#1b111f";g.fillRect(0,p.y-42,W,28);g.fillRect(0,p.y+p.h+14,W,28);g.save();g.translate(p.x,p.y);for(let i=0;i<12;i++){g.fillStyle=i%2?"#238143":"#176d35";g.fillRect(0,p.h/12*i,p.w,p.h/12+1)}g.strokeStyle="#f3f0ff";g.lineWidth=Math.max(2,p.w*.006);g.strokeRect(0,0,p.w,p.h);g.beginPath();g.moveTo(0,p.h/2);g.lineTo(p.w,p.h/2);g.stroke();let L=105,FW=68,goal=7.32,goalH=2.44,pen=16.5,box=5.5,spot=11,arc=9.15,cr=p.h*(arc/L);g.beginPath();g.arc(p.w/2,p.h/2,cr,0,7);g.stroke();let bw=p.w*((goal+pen*2)/FW),bh=p.h*(pen/L),sw=p.w*((goal+box*2)/FW),sh=p.h*(box/L),gw=p.w*(goal/FW),gd=Math.max(6,p.h*(goalH/L)),gx=(p.w-gw)/2,pt=p.h*(spot/L),pb=p.h*(1-spot/L),pr=Math.max(1.8,p.w*.006),aa=Math.asin((pen-spot)/arc),post=Math.max(2,p.w*.008);g.strokeRect((p.w-bw)/2,0,bw,bh);g.strokeRect((p.w-bw)/2,p.h-bh,bw,bh);g.strokeRect((p.w-sw)/2,0,sw,sh);g.strokeRect((p.w-sw)/2,p.h-sh,sw,sh);g.beginPath();g.arc(p.w/2,pt,cr,aa,Math.PI-aa);g.stroke();g.beginPath();g.arc(p.w/2,pb,cr,Math.PI+aa,Math.PI*2-aa);g.stroke();g.fillStyle="#f3f0ff";g.beginPath();g.arc(p.w/2,pt,pr,0,7);g.fill();g.beginPath();g.arc(p.w/2,pb,pr,0,7);g.fill();g.fillStyle="rgba(235,235,255,.1)";g.fillRect(gx,-gd,gw,gd);g.fillRect(gx,p.h,gw,gd);g.strokeRect(gx,-gd,gw,gd);g.strokeRect(gx,p.h,gw,gd);g.fillStyle="#f3f0ff";g.fillRect(gx-post/2,-gd,post,gd+post);g.fillRect(gx+gw-post/2,-gd,post,gd+post);g.fillRect(gx-post/2,p.h-post,post,gd+post);g.fillRect(gx+gw-post/2,p.h-post,post,gd+post);g.lineWidth=1;for(let i=1;i<5;i++){let x=gx+gw*i/5;g.beginPath();g.moveTo(x,-gd);g.lineTo(x,0);g.moveTo(x,p.h);g.lineTo(x,p.h+gd);g.stroke()}drawGoalFlutter(g,gx,gd,gw);g.restore();drawPlayers(home,0,match?.units?.home,frame.units.home,owner);drawPlayers(away,1,match?.units?.away,frame.units.away,owner);if(!trainingMode)drawReferee(g,match?.referee,ball);let bx=p.x+p.w*ball.x,by=p.y+p.h*ball.y,bz=(ball.z||0)*p.h*.45,br=Math.max(4,p.w*.014);g.fillStyle="rgba(0,0,0,.32)";g.beginPath();g.ellipse(bx,by,br*(1.1+Math.min(.8,ball.z||0)),br*.42,0,0,7);g.fill();g.fillStyle="#fff";g.strokeStyle="#151515";g.lineWidth=1.5;g.beginPath();g.arc(bx,by-bz,br,0,7);g.fill();g.stroke()}
    const baseDrawField=drawField;
    function drawGoalFrameOverlay(){const g=$("field").getContext("2d");if(!p.w||!p.h)return;const L=105,FW=68,goal=7.32,goalH=2.44,gw=p.w*(goal/FW),gd=Math.max(7,p.h*(goalH/L)),gx=p.x+(p.w-gw)/2,post=Math.max(2.4,p.w*.009),now=performance.now();function one(top){const y=top?p.y:p.y+p.h,dir=top?-1:1,active=goalFx&&((top&&goalFx.net==="top")||(!top&&goalFx.net==="bottom")),age=active?(now-goalFx.at)/1000:9,fade=active?clamp(1-age/2.3,0,1):0,phase=age*22,amp=gd*(.06+.22*fade);g.save();g.lineCap="round";g.lineJoin="round";g.strokeStyle="rgba(235,235,255,.28)";g.lineWidth=Math.max(1,p.w*.003);for(let i=0;i<=8;i++){let x=gx+gw*i/8,wig=active?Math.sin(phase+i*.7)*amp:0;g.beginPath();g.moveTo(x,y);g.quadraticCurveTo(x+wig*.45,y+dir*gd*.52,x+wig*.18,y+dir*gd);g.stroke()}for(let j=1;j<=4;j++){let yy=y+dir*gd*j/4,wig=active?Math.sin(phase+j*.9)*amp*.45:0;g.beginPath();g.moveTo(gx,yy);g.quadraticCurveTo(gx+gw*.5,yy+wig,gx+gw,yy-wig*.35);g.stroke()}g.strokeStyle="#f8f7ff";g.lineWidth=post;g.beginPath();g.moveTo(gx,y);g.lineTo(gx+gw,y);g.moveTo(gx,y);g.lineTo(gx,y+dir*gd);g.moveTo(gx+gw,y);g.lineTo(gx+gw,y+dir*gd);g.stroke();g.strokeStyle="rgba(248,247,255,.72)";g.lineWidth=Math.max(1.4,post*.55);g.beginPath();g.moveTo(gx,y+dir*gd);g.lineTo(gx+gw,y+dir*gd);g.moveTo(gx,y+dir*gd);g.lineTo(gx,y);g.moveTo(gx+gw,y+dir*gd);g.lineTo(gx+gw,y);g.stroke();g.restore()}one(true);one(false)}
    function drawBallPattern(ball){const g=$("field").getContext("2d"),bx=p.x+p.w*ball.x,by=p.y+p.h*ball.y,bz=(ball.z||0)*p.h*.45,br=Math.max(4,p.w*.014),cy=by-bz;g.save();g.lineJoin="round";g.lineCap="round";g.strokeStyle="#111";g.fillStyle="#111";g.lineWidth=Math.max(1,br*.13);g.beginPath();for(let i=0;i<5;i++){let a=-Math.PI/2+i*Math.PI*2/5,r=i%2?br*.28:br*.46;g.lineTo(bx+Math.cos(a)*r,cy+Math.sin(a)*r)}g.closePath();g.fill();for(let i=0;i<5;i++){let a=-Math.PI/2+i*Math.PI*2/5,x=bx+Math.cos(a)*br*.82,y=cy+Math.sin(a)*br*.82;g.beginPath();g.moveTo(bx+Math.cos(a)*br*.38,cy+Math.sin(a)*br*.38);g.lineTo(x,y);g.stroke();g.beginPath();g.arc(x,y,br*.17,0,7);g.fill()}g.strokeStyle="rgba(0,0,0,.42)";g.lineWidth=Math.max(1,br*.09);g.beginPath();g.arc(bx,cy,br*.72,.15,Math.PI*.92);g.stroke();g.restore()}
    function drawBallBoyOverlay(){const r=match?.engine?.restart;if(!r?.type||!r.out||!r.spot||!p.w)return;let start=Number(r.rollUntil||r.startedAt||match.elapsed||0),end=Number(r.deliveryUntil||start),span=Math.max(.1,end-start),t=clamp(((match.elapsed||0)-start)/span,0,1);if(t<=0||t>=1)return;const g=$("field").getContext("2d"),ease=x=>x*x*(3-2*x),q=ease(t),ox=Number(r.out.x??r.spot.x),oy=Number(r.out.y??r.spot.y),tx=Number(r.spot.x??.5),ty=Number(r.spot.y??.5),nearX=lerp(ox,tx,.82),nearY=lerp(oy,ty,.82);let bx,by,bz=0,boyX,boyY;if(q<.72){let n=ease(q/.72);boyX=lerp(ox,nearX,n);boyY=lerp(oy,nearY,n);bx=boyX;by=boyY;bz=.035}else{let n=ease((q-.72)/.28);boyX=nearX;boyY=nearY;bx=lerp(nearX,tx,n);by=lerp(nearY,ty,n);bz=Math.sin(n*Math.PI)*.10}const x=p.x+p.w*boyX,y=p.y+p.h*boyY,s=Math.max(5,Math.min(9,p.w*.021));g.save();g.fillStyle="rgba(0,0,0,.25)";g.beginPath();g.ellipse(x,y+s*.95,s*.75,s*.25,0,0,7);g.fill();g.strokeStyle="#1b0d0a";g.lineCap="round";g.lineWidth=Math.max(1.5,s*.18);g.beginPath();g.moveTo(x-s*.18,y+s*.05);g.lineTo(x-s*.40,y+s*.72);g.moveTo(x+s*.18,y+s*.05);g.lineTo(x+s*.42,y+s*.72);g.stroke();g.fillStyle="#1677d2";g.strokeStyle="#101010";g.lineWidth=Math.max(1,s*.12);g.fillRect(x-s*.42,y-s*.46,s*.84,s*.72);g.strokeRect(x-s*.42,y-s*.46,s*.84,s*.72);g.strokeStyle="#d8a06f";g.lineWidth=Math.max(1.5,s*.16);g.beginPath();g.moveTo(x-s*.42,y-s*.2);g.lineTo(x-s*.76,y+s*.1);g.moveTo(x+s*.42,y-s*.2);g.lineTo(x+s*.76,y+s*.1);g.stroke();g.fillStyle="#d8a06f";g.beginPath();g.arc(x,y-s*.85,s*.28,0,7);g.fill();drawSmallBall(g,bx,by,bz,Math.max(3,p.w*.01));g.restore()}
    function drawSmallBall(g,x,y,z,r){let bx=p.x+p.w*x,by=p.y+p.h*y-(z||0)*p.h*.45;g.fillStyle="#fff";g.strokeStyle="#111";g.lineWidth=1.2;g.beginPath();g.arc(bx,by,r,0,7);g.fill();g.stroke();g.fillStyle="#111";g.beginPath();g.arc(bx,by,r*.32,0,7);g.fill()}
    drawField=function(home,away,frame){baseDrawField(home,away,frame);drawBallBoyOverlay();drawGoalFrameOverlay();let b=frame.ball||match.ball||{x:.5,y:.5,z:0};if(b.x>=-.08&&b.x<=1.08&&b.y>=-.16&&b.y<=1.16)drawBallPattern(b)}
    function drawReferee(g,ref,ball){let tx=ref?.x??.975,ty=ref?.y??clamp((ball?.y||.5)+.03,.04,.96);if(!refView)refView={x:tx,y:ty};refView.x=lerp(refView.x,tx,.18);refView.y=lerp(refView.y,ty,.18);let x=p.x+p.w*refView.x,y=p.y+p.h*refView.y,s=Math.max(6,Math.min(11,p.w*.025));g.save();g.fillStyle="rgba(0,0,0,.3)";g.beginPath();g.ellipse(x,y+s*.78,s*.8,s*.28,0,0,7);g.fill();g.lineCap="round";g.strokeStyle="#111";g.lineWidth=Math.max(2,s*.18);g.beginPath();g.moveTo(x-s*.22,y+s*.18);g.lineTo(x-s*.45,y+s*.9);g.moveTo(x+s*.22,y+s*.18);g.lineTo(x+s*.45,y+s*.9);g.stroke();g.fillStyle="#f0d84e";g.strokeStyle="#111";g.lineWidth=Math.max(1,s*.13);g.fillRect(x-s*.46,y-s*.42,s*.92,s*.78);g.strokeRect(x-s*.46,y-s*.42,s*.92,s*.78);g.strokeStyle="#111";g.lineWidth=Math.max(2,s*.14);g.beginPath();g.moveTo(x-s*.5,y-s*.25);g.lineTo(x-s*.86,y+s*.12);g.moveTo(x+s*.5,y-s*.25);g.lineTo(x+s*.86,y+s*.12);g.stroke();g.fillStyle="#d8a06f";g.beginPath();g.arc(x,y-s*.85,s*.32,0,7);g.fill();g.fillStyle="#111";g.beginPath();g.arc(x,y-s*1.05,s*.34,Math.PI,0);g.fill();if(ref?.card==="yellow"){g.fillStyle="#ffd900";g.strokeStyle="#111";g.lineWidth=1;g.fillRect(x+s*.55,y-s*1.8,s*.34,s*.52);g.strokeRect(x+s*.55,y-s*1.8,s*.34,s*.52)}if(ref?.whistle){g.strokeStyle="#fff";g.lineWidth=Math.max(1,s*.12);g.beginPath();g.arc(x+s*.5,y-s*.92,s*.18,0,7);g.stroke();g.beginPath();g.moveTo(x+s*.66,y-s*.9);g.lineTo(x+s*1.02,y-s*.78);g.stroke()}g.restore()}
    function drawOwnerNumber(g,x,y,s,num){g.save();g.font=Math.max(9,s*.9)+"px Arial";g.textAlign="center";g.textBaseline="middle";g.fillStyle="#ffdd45";g.strokeStyle="#000";g.lineWidth=3;g.strokeText(String(num),x,y-s*2.25);g.fillText(String(num),x,y-s*2.25);g.restore()}
    function playerNum(u,i){return u?.shirtNumber||u?.legacyNumber||String(i+1)}
    function weightScale(w){return w==="light"?.9:w==="heavy"?1.1:1}
    function drawPlayers(team,side,units,sampled,visualOwner=null){const g=$("field").getContext("2d"),shirt=team.shirt||(side?"#11bcc7":"#ff6d10"),trim=team.trim||"#111",s=Math.max(6.3,Math.min(11.7,p.w*.027)),sideName=side?"away":"home",fk=match?.engine?.freeKick,owner=visualOwner||match?.engine?.owner;if(units&&units.length){units.forEach((u,i)=>{let v=sampled&&sampled[u.id]||u,ps=u.shirt||shirt,pt=u.trim||trim,raised=!!(fk?.signal&&fk?.side===sideName&&fk?.kicker===u.id),x=p.x+p.w*v.x,y=p.y+p.h*v.y,ss=s*weightScale(u.weightClass||v.weightClass);drawPlayer(g,x,y,ss,u.role==="GK"?(side?"#48d7ff":"#ffe05d"):ps,pt,side,u.rating,u.role==="GK",i,raised);if(owner?.side===sideName&&String(owner?.id)===String(u.id))drawOwnerNumber(g,x,y,ss,playerNum(u,i))});return}if(trainingMode)return;const base=[[.5,.94,1],[.18,.78,0],[.38,.8,0],[.62,.8,0],[.82,.78,0],[.16,.58,0],[.38,.61,0],[.62,.61,0],[.84,.58,0],[.38,.36,0],[.62,.36,0]];base.forEach((a,i)=>{let x=p.x+p.w*a[0],y=p.y+p.h*(side?1-a[1]:a[1]);drawPlayer(g,x,y,s,a[2]? (side?"#48d7ff":"#ffe05d"):shirt,trim,side,"",!!a[2],i,false)})}
    function drawPlayer(g,x,y,s,shirt,trim,side,rating,keeper,idx,raised=false){const face=-1,walk=match&&match.status==="active"?Math.sin(performance.now()/150+idx*.9)*s*.12:0,skin=["#f0c8a8","#d8a06f","#8f563b"][idx%3],hair=["#24140f","#4d2c17","#111"][idx%3],line=Math.max(1,s*.13),headY=y+face*s*.72,shoulderY=y+face*s*.12,hipY=y-face*s*.42,footY=y-face*s*.95,shoeY=footY-face*Math.abs(walk);g.fillStyle="rgba(0,0,0,.34)";g.beginPath();g.ellipse(x,y+s*.82,s*.88,s*.32,0,0,7);g.fill();g.lineCap="round";g.lineJoin="round";g.strokeStyle=trim;g.lineWidth=Math.max(2,s*.2);g.beginPath();g.moveTo(x-s*.22,hipY);g.lineTo(x-s*.45,shoeY);g.moveTo(x+s*.22,hipY);g.lineTo(x+s*.45,footY+face*Math.abs(walk));g.stroke();g.strokeStyle=shirt;g.lineWidth=Math.max(2,s*.28);g.beginPath();g.moveTo(x-s*.18,hipY-face*s*.04);g.lineTo(x-s*.43,shoeY);g.moveTo(x+s*.18,hipY-face*s*.04);g.lineTo(x+s*.43,footY+face*Math.abs(walk));g.stroke();g.fillStyle=trim;g.fillRect(x-s*.56,hipY-face*s*.1,s*1.12,s*.22);g.fillStyle=shirt;g.strokeStyle=trim;g.lineWidth=line;g.beginPath();g.moveTo(x-s*.68,shoulderY);g.lineTo(x+s*.68,shoulderY);g.lineTo(x+s*.48,hipY);g.lineTo(x-s*.48,hipY);g.closePath();g.fill();g.stroke();g.save();g.beginPath();g.moveTo(x-s*.62,shoulderY);g.lineTo(x+s*.62,shoulderY);g.lineTo(x+s*.42,hipY);g.lineTo(x-s*.42,hipY);g.closePath();g.clip();g.fillStyle=keeper?"rgba(255,255,255,.28)":trim;g.fillRect(x-s*.1,Math.min(shoulderY,hipY),s*.2,Math.abs(hipY-shoulderY));if(!keeper){g.globalAlpha=.5;g.fillRect(x-s*.48,Math.min(shoulderY,hipY)+Math.abs(hipY-shoulderY)*.46,s*.96,s*.16)}g.restore();g.strokeStyle=skin;g.lineWidth=Math.max(2,s*.2);g.beginPath();g.moveTo(x-s*.62,shoulderY+face*s*.02);g.lineTo(x-s*.88,y-face*s*.22);g.moveTo(x+s*.62,shoulderY+face*s*.02);if(raised){g.lineTo(x+s*.72,y+face*s*1.22);g.moveTo(x+s*.72,y+face*s*1.22);g.lineTo(x+s*.66,y+face*s*1.52)}else g.lineTo(x+s*.88,y-face*s*.22);g.stroke();g.fillStyle=skin;g.strokeStyle="#22110c";g.lineWidth=line;g.beginPath();g.ellipse(x,headY,s*.38,s*.46,0,0,7);g.fill();g.stroke();g.fillStyle=hair;g.beginPath();g.ellipse(x,headY-face*s*.24,s*.43,s*.22,0,0,7);g.fill();g.fillStyle=side?"rgba(255,255,255,.55)":"rgba(0,0,0,.3)";g.fillRect(x-s*.24,headY-face*s*.02,s*.13,s*.08);g.fillRect(x+s*.11,headY-face*s*.02,s*.13,s*.08);if(rating){g.font=Math.max(6,s*.68)+"px Arial";g.textAlign="center";g.textBaseline="middle";g.fillStyle="#fff";g.strokeStyle="#000";g.lineWidth=2;g.strokeText(String(rating),x,y-face*s*.03);g.fillText(String(rating),x,y-face*s*.03)}g.lineCap="butt"}
    function loop(){if(!$("match").classList.contains("hide")){if(trainingMode)renderTraining();else if(match)renderMatch()}requestAnimationFrame(loop)}
    addEventListener("resize",()=>trainingMode?renderTraining():renderMatch());
    requestAnimationFrame(loop);
    setTimeout(()=>{if(visibleScreen()==="loading")startupError(new Error("startup timeout"))},8000);
    state().catch(startupError);
