<!-- SOLID CITY · part 2948 -->
)-(keys.a?1:0),y=(keys.s?1:0)-(keys.w?1:0),m=Math.hypot(x,y)||1;input.move.x=x/m;input.move.y=y/m}
    if(!pointerWeapon){var ax=(keys.arrowright?1:0)-(keys.arrowleft?1:0),ay=(keys.arrowdown?1:0)-(keys.arrowup?1:0);if(ax||ay){var am=Math.hypot(ax,ay);input.weapon.x=ax/am;input.weapon.y=ay/am}input.firing=!!keys[' ']}
  }

  function launch(){reset();state.running=true;last=performance.now();els.startCard.hidden=true;els.debrief.hidden=true;els.pauseBtn.hidden=false;els.stage.classList.add('is-running');log('SYS','Route 01 launched','teal');cancelAnimationFrame(raf);raf=requestAnimationFrame(loop)}
  function pauseGame(){
    if(!state.running||state.paused)return;state.paused=true;pauseStarted=now();cancelAnimationFrame(raf);els.pauseBtn.hidden=true;els.pauseOverlay.hidden=false;document.querySelectorAll('.preset').forEach(function(b){b.disabled=true});els.resumeBtn.focus();
  }
  function resumeGame(){
    if(!state.running||!state.paused)return;var pausedFor=now()-pauseStarted;if(releaseAt.move)releaseAt.move+=pausedFor;if(releaseAt.weapon)releaseAt.weapon+=pausedFor;state.paused=false;pauseStarted=0;
    if(pausedReleases.move&&!pointerMove&&!['w','a','s','d'].some(function(x){return keys[x]})){input.move={x:0,y:0};document.getElementById('moveNub').style.left='50%';document.getElementById('moveNub').style.top='50%';release('move')}
    if(pausedReleases.weapon&&!pointerWeapon&&!['arrowup','arrowdown','arrowleft','arrowright',' '].some(function(x){return keys[x]})){input.weapon={x:1,y:0};input.firing=false;document.getElementById('weaponNub').style.left='50%';document.getElementById('weaponNub').style.top='50%';release('weapon')}
    pausedReleases.move=pausedReleases.weapon=false;document.querySelectorAll('.preset').forEach(function(b){b.disabled=false});els.pauseOverlay.hidden=true;els.pauseBtn.hidden=false;els.resumeBtn.blur();last=performance.now();raf=requestAnimationFrame(loop);
  }
  els.pauseBtn.addEventListener('pointerdown',function(e){e.stopPropagation()});els.pauseBtn.addEventListener('click',pauseGame);els.resumeBtn.addEventListener('click',resumeGame);
  document.getElementById('launchBtn').addEventListener('click',launch);document.getElementById('againBtn').addEventListener('click',launch);

  function nearestThreat(){
    var best=null,bd=1e9;state.enemies.forEach(function(e){var d=Math.hypot(e.x-state.player.x,e.y-state.player.y);if(d<bd){bd=d;best=e}});state.rocks.forEach(function(e){var d=Math.hypot(e.x-state.player.x,e.y-state.player.y);if(d<bd){bd=d;best=e}});return {obj:best,d:bd};
  }
  function aiMove(){
    var y=(H*.5-state.player.y)/Math.max(H*.35,1),x=.82,th=nearestThreat();
    if(th.obj&&th.d<170){var dy=state.player.y-th.obj.y;y+=(dy>=0?1:-1)*(1-th.d/190)*1.8}
    return {x:x,y:Math.max(-1,Math.min(1,y))};
  }
  function aiWeapon(){var best=null,bd=1e9;state.enemies.forEach(function(e){var d=Math.hypot(e.x-state.player.x,e.y-state.player.y);if(d<bd){bd=d;best=e}});if(!best)return {x:1,y:0,fire:false};var dx=best.x-state.player.x,dy=best.y-state.player.y,m=Math.hypot(dx,dy)||1;return {x:dx/m,y:dy/m,fire:bd<520}}
  function blendInput(task,dt){
    if(releaseAt[task]&&now()>=releaseAt[task]){releaseAt[task]=0;setOwner(task,'ai',true)}
    updateOwners();
    if(task==='move')return owners.move==='human'?input.move:aiMove();
    return owners.weapon==='human'?{x:input.weapon.x,y:input.weapon.y,fire:input.firing}:aiWeapon();
  }

  function spawnEnemy(){var y=55+Math.random()*(H-110);state.enemies.push({x:W+35,y:y,vx:-45-Math.random()*25,phase:Math.random()*6.28,hp:2,shot:.7+Math.random()});if(Math.random()<.45)state.rocks.push({x:W+70,y:40+Math.random()*(H-80),vx:-85-Math.random()*50,r:13+Math.random()*13,rot:0})}
  function particle(x,y,c,n){for(var i=0;i<n;i++)state.particles.push({x:x,y:y,vx:(Math.random()-.5)*160,vy:(Math.random()-.5)*160,life:.35+Math.random()*.5,c:c})}
  function hitCircle(a,b,r){return Math.hypot(a.x-b.x,a.y-b.y)<r}
  function damage(n){state.hull=Math.max(0,state.hull-n);particle(state.player.x,state.player.y,colors.red,9);if(state.hull<=0)finish(false)}

  function update(dt){
    if(!state.running)return;state.elapsed+=dt;keyboardInput();var mv=blendInput('move',dt),wp=blendInput('weapon',dt);
    var targetVx=mv.x*130,targetVy=mv.y*190;state.player.vx+=(targetVx-state.player.vx)*Math.min(1,dt*4.2);state.player.vy+=(targetVy-state.player.vy)*Math.min(1,dt*5.5);
    state.player.y=Math.max(42,Math.min(H-42,state.player.y+state.player.vy*dt));state.player.x=Math.max(W*.14,Math.min(W*.50,state.player.x+state.player.vx*dt*.18));
    state.progress+=Math.max(18,90+state.player.vx*.36)*dt;state.wing.x+=(state.player.x-54-state.wing.x)*dt*3.2;state.wing.y+=(state.player.y+42-state.wing.y)*dt*3.2;
    state.spawn-=dt;if(state.spawn<=0){spawnEnemy();state.spawn=Math.max(.55,1.35-state.elapsed*.008)+Math.random()*.5}
    state.shot-=dt;if(wp.fire&&state.shot<=0){state.shot=.15;state.bullets.push({x:state.player.x+wp.x*18,y:stat