<!-- SOLID CITY · part 2934 -->
{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:state.player.y+wp.y*18,vx:wp.x*520,vy:wp.y*520,life:1.2});particle(state.player.x+wp.x*18,state.player.y+wp.y*18,colors.gold2,2)}
    state.enemies.forEach(function(e){e.phase+=dt*2;e.x+=e.vx*dt;e.y+=Math.sin(e.phase)*15*dt;e.shot-=dt;if(e.shot<0){e.shot=1.4+Math.random();var dx=state.player.x-e.x,dy=state.player.y-e.y,m=Math.hypot(dx,dy)||1;state.enemyBullets.push({x:e.x,y:e.y,vx:dx/m*210,vy:dy/m*210,life:3})}});
    state.rocks.forEach(function(r){r.x+=r.vx*dt;r.rot+=dt*1.8});
    state.bullets.forEach(function(b){b.x+=b.vx*dt;b.y+=b.vy*dt;b.life-=dt;state.enemies.forEach(function(e){if(e.hp>0&&hitCircle(b,e,18)){e.hp--;b.life=0;if(e.hp<=0){state.kills++;particle(e.x,e.y,colors.teal,15)}}})});
    state.enemyBullets.forEach(function(b){b.x+=b.vx*dt;b.y+=b.vy*dt;b.life-=dt;if(b.life>0&&hitCircle(b,state.player,17)){b.life=0;damage(7)}});
    state.rocks.forEach(function(r){if(r.r>0&&hitCircle(r,state.player,r.r+14)){r.r=0;damage(13)}});
    state.enemies.forEach(function(e){if(e.hp>0&&hitCircle(e,state.player,30)){e.hp=0;damage(16)}});
    state.enemies=state.enemies.filter(function(e){return e.x>-80&&e.hp>0});state.rocks=state.rocks.filter(function(r){return r.x>-80&&r.r>0});state.bullets=state.bullets.filter(function(b){return b.life>0&&b.x<W+30&&b.x>-30&&b.y>-30&&b.y<H+30});state.enemyBullets=state.enemyBullets.filter(function(b){return b.life>0});
    state.particles.forEach(function(p){p.x+=p.vx*dt;p.y+=p.vy*dt;p.vx*=.97;p.vy*=.97;p.life-=dt});state.particles=state.particles.filter(function(p){return p.life>0});
    var th=nearestThreat(),warn=th.obj&&th.d<145;if(warn&&!state.warningOn&&state.warningCooldown<=0){state.warnings++;state.warningCooldown=1.5;log('HAZ','Collision vector · advisory only','');}state.warningOn=!!warn;state.warningCooldown=Math.max(0,state.warningCooldown-dt);
    if(state.progress>=state.target)finish(true);updateHud();
  }
  function updateHud(){if(!state)return;els.hullBar.style.width=state.hull+'%';els.hullText.textContent=Math.round(state.hull)+'%';els.distanceText.textContent=Math.max(0,Math.round(state.target-state.progress)).toLocaleString()+' m';els.killsText.textContent=state.kills+' DEFENDED';els.handoffCount.textContent=state.handoffs;els.conflictCount.textContent=state.conflicts;els.warningCount.textContent=state.warnings;els.warning.classList.toggle('sho