<!-- SOLID CITY · part 3475 -->
.7;o.vx*=-.55;o.vy+=(Math.random()-.5)*.05;addEvent('IMPACT',d.name,'Hull impact; integrity now '+Math.max(0,Math.round(d.hp))+'%');if(d.hp<=0)disableDrone(d,'debris impact')}})
    });
    particles.forEach(function(p){p.x+=p.vx*dt;p.y+=p.vy*dt;p.life-=dt});particles=particles.filter(function(p){return p.life>0});recoverLogic(dt);
    var active=drones.filter(function(d){return d.status==='ACTIVE'}).length;var pressure=Math.max(0,1-debris.length/(7+wave*2));progress+=dt*(.22+.24*pressure)*(active/12);convoyX=.12+progress/100*.65;
    if(progress>=wave*25&&wave<4){wave++;spawnWave(wave);announce('Wave '+wave+' incoming. '+debris.length+' hazards tracked.')}
    if(elapsed>=nextHazardAt){nextHazardAt=elapsed+Math.max(4.5,8-wave);if(wave>=2)debris.push({id:++uid,x:.98,y:.14+Math.random()*.75,r:.012+Math.random()*.012,hp:40+wave*13,max:40+wave*13,vx:-.006-wave*.001,vy:(Math.random()-.5)*.008,spin:(Math.random()-.5)*2,angle:Math.random()*6.28})}
    if(elapsed>=nextAdvisoryAt){nextAdvisoryAt=elapsed+6;autoAdvisory()}
    if(active===0)finish(false);else if(progress>=100)finish(true);
    updateHUD();
  }
  function announce(t){document.getElementById('advisory').textContent=t}
  function autoAdvisory(){var active=drones.filter(function(d){return d.status==='ACTIVE'}).length,human=drones.filter(function(d){return d.owner==='HUMAN'&&d.status==='ACTIVE'}).length;if(active<9)announce('Integrity falling. REGROUP can recover disabled drones.');else if(debris.length>12)announce('Hazard density high. Recommend SCATTER or SWEEP. Advisory only.');else if(human)announce(human+' human-held drone'+(human===1?' remains':'s remain')+' outside AI group orders.');else announce('AI sub-commanders executing '+activeOrder+'. All control channels stable.')}
  function finish(success){if(!running)return;running=false;paused=false;pauseBtn.hidden=true;pauseScreen.hidden=true;app.inert=false;document.getElementById('debriefTitle').textContent=success?'Lane secured.':'Swarm lost.';document.getElementById('debriefCopy').textContent=success?'The convoy reached the exit vector under coordinated protection.':'No active drones remain to protect the convoy.';document.getElementById('finalTime').textContent=nowStamp();document.getElementById('finalClear').textContent=count('CLEARED');document.getElementById('finalLost').textContent=count('LOST');debrief.hidden=false;addEvent(success?'COMPLETE':'FAILED','MISSION',success?'Convoy reached exit vector':'All drones disabled')}
  function loop(t){if(!running)return;if(paused){last=t;draw();requestAnimationFrame(loop);return}var dt=Math.min(.035,(t-last)/1000||.016);last=t;update(dt);draw();if(running)requestAnimationFrame(loop)}

  function draw(){
    var w=canvasW,h=canvasH;if(!w||!h)return;ctx.clearRect(0,0,w,h);ctx.fillStyle='#03090b';ctx.fillRect(0,0,w,h);
    ctx.strokeStyle='rgba(82,224,208,.055)';ctx.lineWidth=1;for(var x=0;x<w;x+=44){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,h);ctx.stroke()}for(var y=0;y<h;y+=44){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(w,y);ctx.stroke()}
    for(var s=0;s<38;s++){var sx=(s*97%997)/997*w,sy=(s*61%557)/557*h;ctx.fillStyle='rgba(220,246,242,'+(s%5===0?.55:.22)+')';ctx.fillRect(sx,sy,s%5===0?1.6:1,s%5===0?1.6:1)}
    var top=.18*h,bottom=.86*h;ctx.fillStyle='rgba(82,224,208,.035)';ctx.fillRect(0,top,w,bottom-top);ctx.strokeStyle='rgba(82,224,208,.18)';ctx.setLineDash([8,10]);ctx.beginPath();ctx.moveTo(0,top);ctx.lineTo(w,top);ctx.moveTo(0,bottom);ctx.lineTo(w,bottom);ctx.stroke();ctx.setLineDash([]);
    ctx.fillStyle='rgba(82,224,208,.65)';ctx.font='10px ui-monospace,monospace';ctx.fillText('CONVOY LANE K-9',12,top-10);
    var cx=convoyX*w,cy=.5*h;ctx.save();ctx.translate(cx,cy);ctx.fillStyle='#8fb9ba';ctx.beginPath();ctx.moveTo(18,0);ctx.lineTo(-10,-10);ctx.lineTo(-18,0);ctx.lineTo(-10,10);ctx.closePath();ctx.fill();ctx.fillStyle='#52e0d0';ctx.fillRect(-8,-2,20,4);ctx.restore();
    debris.forEach(function(o){var ox=o.x*w,oy=o.y*h,r=o.r*Math.min(w,h)*1.25;ctx.save();ctx.translate(ox,oy);ctx.rotate(o.angle);ctx.fillStyle='#332a27';ctx.strokeStyle=o.hp/o.max<.4?'#ff8b3d':'#b78468';ctx.lineWidth=1.5;ctx.beginPath();for(var k=0;k<7;k++){var a=k/7*Math.PI*2,rr=r*(.72+((k*3)%5)/12);var px=Math.cos(a)*rr,py=Math.sin(a)*rr;k?ctx.lineTo(px,py):ctx.moveTo(px,py)}ctx.closePath();ctx.fill();ctx.stroke();ctx.restore()});
    particles.forEach(function(p){ctx.globalAlpha=Math.max(0,p.life);ctx.fillStyle='#ffc34d';ctx.fillRect(p.x*w,p.y*h,2,2);ctx.globalAlpha=1});
    drones.forEach(function(d){var x=d.x*w,y=d.y*h,sel=d.id===selectedId,human=d.owner==='HUMAN';ctx.save();ctx.translate(x,y);if(d.status==='DISABLED'){ctx.globalAlpha=.38;ctx.strokeStyle='#ff625f';ctx.beginPath();ctx.moveTo(-7,-7);ctx.lineTo(7,7);ctx.moveTo(7,-7);ctx.lineTo(-7,7);ctx.stroke();ctx.restore();return}if(d.pulse>0){ctx.globalAlpha=Math.min(1,d.pulse*2.5);ctx.strokeStyle=human?'#ffc34d':'#52e0d0';ctx.lineWidth=1.5;ctx.beginPath();ctx.arc(0,0,(.12-d.pulse*.06)*M