<!-- SOLID CITY · part 1924 -->
cueBroke=false;if(ships[1].coveringTarget===e.id){ships[1].coveringTarget=null;ships[1].coverBroke=false}}var d=distObj(e,target);if(d<650)e.threatTargetIndex=e.targetIndex;else e.threatTargetIndex=-1;if(target.alive){if(d>210)steer(e,target.x,target.y,dt,80);else{var dx=delta(e.x,target.x,WORLD.w),dy=delta(e.y,target.y,WORLD.h);e.vx+=-dy/(d||1)*35*dt;e.vy+=dx/(d||1)*35*dt}e.a=Math.atan2(delta(target.y,e.y,WORLD.h),delta(target.x,e.x,WORLD.w));e.fire-=dt;if(e.fire<=0&&d<650){e.fire=Math.max(.85,2.2-wave*.12)+Math.random();var a=e.a;bolts.push({x:e.x,y:e.y,vx:Math.cos(a)*150,vy:Math.sin(a)*150,life:5})}}var l=Math.hypot(e.vx,e.vy);if(l>90){e.vx=e.vx/l*90;e.vy=e.vy/l*90}e.x=wrapX(e.x+e.vx*dt);e.y=wrapY(e.y+e.vy*dt)});
    meteors.forEach(function(m){m.x=wrapX(m.x+m.vx*dt);m.y=wrapY(m.y+m.vy*dt);m.rot+=m.spin*dt});
    pickups.forEach(function(p){p.life-=dt;p.phase+=dt*2.5;var pull=p.component?620:240,s=p.component&&ships[p.target].alive?ships[p.target]:nearest(p,ships.filter(function(q){return q.alive}),pull);if(s){var dx=delta(s.x,p.x,WORLD.w),dy=delta(s.y,p.y,WORLD.h),d=Math.hypot(dx,dy)||1,q=Math.max(0,(pull-d)/pull);p.vx+=dx/d*(100+q*700)*dt;p.vy+=dy/d*(100+q*700)*dt;if(d<25)collectPower(p,s,ships.indexOf(s))}p.vx*=Math.pow(.98,dt*60);p.vy*=Math.pow(.98,dt*60);p.x=wrapX(p.x+p.vx*dt);p.y=wrapY(p.y+p.vy*dt)});
    moveProjectiles(shots,dt);moveProjectiles(bolts,dt);collisions();
    sparks.forEach(function(p){p.x+=p.vx*dt;p.y+=p.vy*dt;p.life-=dt;p.vx*=.97;p.vy*=.97});sparks=sparks.filter(function(p){return p.life>0});muzzles.forEach(function(m){m.life-=dt});
    enemies=enemies.filter(function(e){return e.hull>0});shots=shots.filter(function(s){return s.life>0});bolts=bolts.filter(function(b){return b.life>0});meteors=meteors.filter(function(m){return !m.resolved});pickups=pickups.filter(function(p){return p.life>0});muzzles=muzzles.filter(function(m){return m.life>0});
    updateHUD();
  }
  function moveProjectiles(a,dt){a.forEach(function(p){p.x=wrapX(p.x+p.vx*dt);p.y=wrapY(p.y+p.vy*dt);p.life-=dt})}
  function friendlyHit(source,owner,allyIndex,amount,kind,x,y){var key=String(source&&source.id||source&&source.fid||kind)+'-'+owner+'-'+allyIndex;if(friendlyHitGuard[key])return false;friendlyHitGuard[key]=performance.now();metrics.friendlyFire++;damage(ships[allyIndex],allyIndex,amount,'friendly');notify(allyIndex,'FRIENDLY FIRE'+(kind==='blast'?' · BLAST':''),750);notify(owner,'FRIENDLY FIRE'+(kind==='blast'?' · BLAST':''),750);burst(x,y,'#d38cff',kind==='blast'?16:12);logA('friendly fire · '+ships[owner].label+' '+(kind==='blast'?'blast caught ':'struck ')+ships[allyIndex].label);return true}
  function collisions(){
    shots.forEach(function(s){
      if(level===4){ships.forEach(function(ally,ai){if(ai!==s.owner&&s.life>0&&ally.alive&&distObj(s,ally)<18){s.life=0;friendlyHit(s,s.owner,ai,10,'direct',s.x,s.y)}})}
      enemies.forEach(function(e){if(s.life>0&&e.hull>0&&distObj(s,e)<18){s.life=0;e.hull-=12+ships[s.owner].build.cannon*4+coordinatedHit(e,s.owner);burst(s.x,s.y,'#ffb36a',6);if(e.hull<=0){e.hull=0;metrics.kills++;progression.levelKills++;ships[s.owner].score+=100;awardRescue(e,s.owner);burst(e.x,e.y,'#ff5b66',14);if(level===1)dropComponent(e.x,e.y);dropPower(e.x,e.y)}}});
      meteors.forEach(function(m){if(s.life>0&&!m.resolved&&distObj(s,m)<m.r+4){s.life=0;m.hull-=1+coordinatedHit(m,s.owner)/8;burst(s.x,s.y,'#ffc84a',5);if(m.hull<=0){m.resolved=true;ships[s.owner].score+=25;awardRescue(m,s.owner);burst(m.x,m.y,'#ffc84a',13);if(level===4){ships.forEach(function(ally,ai){if(ai!==s.owner&&ally.alive&&distObj(m,ally)<72){friendlyHit(m,s.owner,ai,15,'blast',ally.x,ally.y)}})}if(m.threat){metrics.avoided++;logA('outcome · meteor destroyed');checkContinuity(m)}}}})
    });
    bolts.forEach(function(b){ships.forEach(function(s,i){if(b.life>0&&s.alive&&distObj(b,s)<17){b.life=0;damage(s,i,15,'bolt')}})});
    meteors.forEach(function(m){ships.forEach(function(s,i){if(!m.resolved&&s.alive&&distObj(m,s)<m.r+19){m.resolved=true;damage(s,i,38,'meteor');burst(m.x,m.y,'#ff8c24',20);if(i===0){metrics.meteorHits++;if(m.threat){logA('outcome · human ship hit');checkContinuity(m)}}}})});
    enemies.forEach(function(e){ships.forEach(function(s,i){if(e.hull>0&&s.alive&&distObj(e,s)<32){e.hull=0;metrics.kills++;progression.levelKills++;if(level===1)dropComponent(e.x,e.y);dropPower(e.x,e.y);damage(s,i,30,'collision');burst(e.x,e.y,'#ff5b66',14)}})})
  }
  function breakShield(s){if(!s.shieldCharges)return;s.shieldCharges=0;s.shieldUntil=0;s.shieldShatterUntil=performance.now()+500;burst(s.x,s.y,'#6ef2ff',34)}
  function damage(s,i,n,why){if(!s||!s.alive||!Number.isFinite(n)||n<=0)return;var now=performance.now();if(s.shieldCharges>0&&now<s.shieldUntil){s.shieldCharges--;s.shieldHitUntil=now+260;metrics.shieldHits++;notify(i,'SHIELD ABSORBED',420);burst(s.x,s.y,'#d8fbff',12);if(s.shieldCharges<=0){s.shieldCharges=1;breakShield(s);notify(i,'SHIELD SHATTERED',700)}return}if(fligh