<!-- SOLID CITY · part 3743 -->
, and Siege can register
   emplacements. The game only consumes claim/release/command/advisory events. */
class ControlCoordination{
  constructor(names,emit){this.emit=emit;this.tasks={};this.seen=new Set();this.queue=[];names.forEach(n=>this.tasks[n]={owner:'ai',epoch:1,status:'ACTIVE',claimedAt:0});this.connected=true;}
  claim(name){const t=this.tasks[name],start=performance.now();if(paused||ended||!t)return;t.owner='human';t.epoch++;t.status='ACTIVE';t.claimedAt=start;this.emit('ownership',{task:name,owner:'human',epoch:t.epoch});const latency=performance.now()-start;this.emit('handoff',{task:name,to:'human',latency:+latency.toFixed(2),epoch:t.epoch});return t.epoch;}
  release(name){const t=this.tasks[name];if(paused||ended||!t||t.owner!=='human')return;t.status='HANDOFF';const epoch=++t.epoch,start=gameClock;this.emit('handoff_start',{task:name,to:'ai',epoch});const complete=()=>{if(ended)return;const activeElapsed=gameClock-start;if(paused||activeElapsed<72){setTimeout(complete,16);return;}if(t.epoch!==epoch||t.owner!=='human'){this.emit('command_rejected',{task:name,reason:'interrupted handoff',epoch});return;}t.owner='ai';t.status='ACTIVE';this.emit('ownership',{task:name,owner:'ai',epoch});this.emit('handoff',{task:name,to:'ai',latency:+activeElapsed.toFixed(2),epoch});updateStations();};setTimeout(complete,16);}
  command(name,owner,epoch,fn,id){if(paused||ended)return false;const t=this.tasks[name];id=id||name+'-'+owner+'-'+epoch+'-'+Math.random();if(!this.connected){this.emit('command_rejected',{task:name,reason:'operator disconnected',epoch});return false;}if(this.seen.has(id)){this.emit('command_rejected',{task:name,reason:'duplicate command',epoch});return false;}this.seen.add(id);if(this.queue.length>48){this.emit('command_rejected',{task:name,reason:'queue overloaded',epoch});return false;}if(!t||t.owner!==owner||t.epoch!==epoch){this.emit('command_rejected',{task:name,reason:'stale ownership epoch',epoch,current:t?t.epoch:null});return false;}this.queue.push(id);try{fn();this.emit('command_accepted',{task:name,owner,epoch});}finally{this.queue.shift();}return true;}
  advisory(task,text){this.emit('advisory',{task,text});}
}

const canvas=document.getElementById('game'),ctx=canvas.getContext('2d');
const $=id=>document.getElementById(id);let dpr=1,W=800,H=560,last=0,raf=0,running=false,paused=false,ended=false,gameClock=0;
let ship,enemies=[],shells=[],enemyShots=[],particles=[],stars=[],events=[],eventSeq=0,wave=1,nextSpawn=0,aiTick=0,adviceTick=0,missionStart=0;
let rngSeed=92811;
function rand(){rngSeed=(rngSeed*1664525+1013904223)>>>0;return rngSeed/4294967296;}
function emit(type,data){events.push({id:++eventSeq,type,time:performance.now(),mission:missionStart?performance.now()-missionStart:0,...(data||{})});if(events.length>1000)events.shift();if(type==='advisory')$('advisoryText').textContent=data.text;if(type==='command_rejected')showWarning('SIGNAL GUARD · '+data.reason.toUpperCase());renderOwnership();}
let coordination=new ControlCoordination(['nav','batteryA','batteryB','defense'],emit);
function reset(){
  rngSeed=92811;events=[];eventSeq=0;coordination=new ControlCoordination(['nav','batteryA','batteryB','defense'],emit);
  ship={x:.5,y:.74,vx:0,vy:0,hull:100,shield:100,shieldFace:'front',progress:0,targetA:0,targetB:0,navInput:0,boost:false,defCooldown:0};
  enemies=[];shells=[];enemyShots=[];particles=[];wave=1;nextSpawn=.4;aiTick=0;adviceTick=0;ended=false;paused=false;gameClock=0;missionStart=performance.now();
  for(let i=0;i<110;i++)stars[i]={x:rand(),y:rand(),z:.25+rand()*.75};
  ['nav','batteryA','batteryB','defense'].forEach(n=>emit('ownership',{task:n,owner:'ai',epoch:1}));
  updateStations();updateHUD();
}
function resize(){const r=canvas.getBoundingClientRect();dpr=Math.min(devicePixelRatio||1,2);W=Math.max(320,r.width);H=Math.max(250,r.height);canvas.width=Math.round(W*dpr);canvas.height=Math.round(H*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);}
function spawnEnemy(kind){const side=rand()<.5?-1:1;const e={id:'E'+Math.floor(rand()*99999),kind:kind||((wave>2&&rand()<.22)?'cruiser':'fighter'),x:.15+rand()*.7,y:-.08-rand()*.2,vx:(rand()-.5)*.05,vy:.045+rand()*.025,hp:kind==='cruiser'?80:((wave>2&&rand()<.22)?80:28),maxHp:0,fire:.7+rand()*1.2,dead:false,side};e.maxHp=e.hp;enemies.push(e);emit('threat_detected',{threat:e.id,kind:e.kind,wave});}
function claim(station){if(coordination.tasks[station].owner==='human')return;coordination.claim(station);updateStations();showWarning(stationLabel(station)+' · YOU HAVE CONTROL',false);}
function release(station){coordination.release(station);updateStations();$('handoffText').textContent='Safe handoff in progress · interruptible by your touch';}
function stationLabel(n){return {nav:'NAVIGATION',batteryA:'BATTERY A',batteryB:'BATTERY B',defense:'DEFENSE'}[n];}
function updateStations(){document.querySelectorAll('.station').forEach(el=>{const n=el.dataset.station,t=coordination.tasks[n];el.dataset.o