<!-- SOLID CITY · part 1919 -->
ove or dive below.';document.querySelector('#continueLevel').textContent='BEGIN FORWARD FLIGHT';document.querySelector('#levelCard').classList.add('future');levelBeat.dataset.next='5';levelBeat.hidden=false
  }
  function showDepthComplete(){
    document.querySelector('#levelNo').textContent='FIRST DEPTH LESSON COMPLETE';document.querySelector('#levelTitle').textContent='UNDER & OVER';document.querySelector('#levelCopy').textContent='The Twin Lightnings crossed the corridor as one formation — above, alongside, and below. The flat sky is behind you.';document.querySelector('#continueLevel').textContent='VIEW DEBRIEF';document.querySelector('#levelCard').classList.add('future');levelBeat.dataset.next='depthComplete';levelBeat.hidden=false
  }
  function spawnSpeedRing(owner){var s=ships[owner],a=Math.hypot(s.vx,s.vy)>30?Math.atan2(s.vy,s.vx):s.a+(Math.random()-.5)*.8,d=210+Math.random()*120;rings.push({id:nextId++,owner:owner,x:wrapX(s.x+Math.cos(a)*d),y:wrapY(s.y+Math.sin(a)*d),r:30,phase:Math.random()*6.28})}
  function spawnMeteor(targeted){
    var targetIndex=targeted&&Math.random()<.28?1:0,target=ships[targetIndex],ang=Math.random()*Math.PI*2;
    var dist=650+Math.random()*350,x=target.x+Math.cos(ang)*dist,y=target.y+Math.sin(ang)*dist;
    var aim=targeted?Math.atan2(target.y+target.vy*1.1-y,target.x+target.vx*1.1-x):ang+Math.PI+(Math.random()-.5)*1.1;
    var sp=360+wave*28+Math.random()*110;
    meteors.push({id:nextId++,x:wrapX(x),y:wrapY(y),vx:Math.cos(aim)*sp,vy:Math.sin(aim)*sp,r:18+Math.random()*15,hull:2,rot:Math.random()*6,spin:(Math.random()-.5)*2,threat:false,targetIndex:targeted?targetIndex:-1,threatTargetIndex:targeted?targetIndex:-1,rescuePilot:-1,rescueBroke:false,lastHitBy:-1,lastHitAt:0,coordAt:0,resolved:false,action:'none'});
  }
  function wrapX(x){return (x+WORLD.w)%WORLD.w}function wrapY(y){return (y+WORLD.h)%WORLD.h}
  function delta(a,b,size){var d=a-b;if(d>size/2)d-=size;if(d<-size/2)d+=size;return d}
  function distObj(a,b){var dx=delta(a.x,b.x,WORLD.w),dy=delta(a.y,b.y,WORLD.h);return Math.hypot(dx,dy)}
  function collisionCourse(o,s,windowSec,margin){var rx=delta(o.x,s.x,WORLD.w),ry=delta(o.y,s.y,WORLD.h),rvx=o.vx-s.vx,rvy=o.vy-s.vy,rv2=rvx*rvx+rvy*rvy;if(rv2<1)return false;var t=-(rx*rvx+ry*rvy)/rv2,cx=rx+rvx*t,cy=ry+rvy*t;return t>0&&t<(windowSec||1.8)&&Math.hypot(cx,cy)<(o.r||12)+(margin||28)}
  function nearest(from,list,max){var best=null,bd=max||1e9;list.forEach(function(o){var d=distObj(from,o);if(d<bd){bd=d;best=o}});return best}
  function updateFlight(dt){
    var d=distObj(ships[0],ships[1]);flight.lastDistance=d;
    if(flight.mode==='formation'){
      metrics.formationTime+=dt;
      if(d>470){flight.mode='separation';flight.wasSeparated=true;metrics.separations++;if(ships[1].coveringTarget!==null)ships[1].coverBroke=true;enemies.concat(meteors).forEach(function(o){if(o.rescuePilot>=0)o.rescueBroke=true;o.lastHitBy=-1;o.lastHitAt=0});flightStateEl.textContent='SEPARATION';flightStateEl.className='flight-state separation';notify(0,'DIRECTION HOME',850);notify(1,'DIRECTION HOME',850)}
    }else if(flight.mode==='separation'&&d<330){
      flight.mode='formation';flight.reunionUntil=performance.now()+1500;metrics.reunions++;metrics.reunionBonus+=150;ships[0].score+=75;ships[1].score+=75;flightStateEl.textContent='REUNITED +150';flightStateEl.className='flight-state reunion';notify(0,'REUNITED +75',1300);notify(1,'REUNITED +75',1300);burst(ships[0].x,ships[0].y,'#fff2a8',22);burst(ships[1].x,ships[1].y,'#fff2a8',22)
    }
    if(flight.mode==='formation'&&performance.now()>flight.reunionUntil){flightStateEl.textContent='FORMATION';flightStateEl.className='flight-state formation'}
  }
  function awardRescue(target,owner){var threatened=target.threatTargetIndex;if(threatened<0||target.targetIndex!==threatened||owner===threatened||target.rescuePilot!==owner||!target.rescueBroke)return;metrics.rescues++;ships[owner].score+=200;notify(threatened,'RESCUED +200',1200);notify(owner,'RESCUE +200',1200);logA('rescue · '+ships[owner].label+' broke formation to stop threat '+target.id);if(ships[1].coveringTarget===target.id){ships[1].coveringTarget=null;ships[1].coverBroke=false}}
  function coordinatedHit(target,owner){var now=performance.now(),bonus=0,threatened=target.threatTargetIndex;if(threatened>=0&&owner!==threatened){target.rescuePilot=owner;if(flight.mode==='separation')target.rescueBroke=true}if(flight.mode!=='formation'){target.lastHitBy=-1;target.lastHitAt=0;return 0}if(target.lastHitBy>=0&&target.lastHitBy!==owner&&now-target.lastHitAt<720&&now-(target.coordAt||0)>800){bonus=8;target.coordAt=now;metrics.coordinated++;ships[0].score+=25;ships[1].score+=25;notify(0,'COORDINATED STRIKE +50',650);notify(1,'COORDINATED STRIKE +50',650)}target.lastHitBy=owner;target.lastHitAt=now;return bonus}
  function steer(ship,tx,ty,dt,force){var dx=delta(tx,ship.x,WORLD.w),dy=delta(ty,ship.y,WORLD.h),l=Math.hypot(dx,dy)||1;ship.vx+=dx/l*force*dt