<!-- SOLID CITY · part 2910 -->
h.PI*2);ctx.fill();ctx.stroke();ctx.setLineDash([]);for(let i=0;i<3;i++){ctx.rotate(2.094);ctx.fillStyle='rgba(255,99,117,.28)';ctx.beginPath();ctx.moveTo(0,-8);ctx.lineTo(10,-h.r*.72);ctx.lineTo(-10,-h.r*.72);ctx.closePath();ctx.fill()}ctx.restore()}
  function drawDebris(d){ctx.save();ctx.translate(d.x,d.y);ctx.fillStyle='#59666b';ctx.strokeStyle='#8d9da3';ctx.beginPath();for(let i=0;i<7;i++){const a=i/7*Math.PI*2,r=d.r*(.7+((i*3)%5)/10);const x=Math.cos(a)*r,y=Math.sin(a)*r;i?ctx.lineTo(x,y):ctx.moveTo(x,y)}ctx.closePath();ctx.fill();ctx.stroke();ctx.restore()}
  function drawPod(p){if(p.status==='rescued'||p.status==='lost')return;ctx.save();ctx.translate(p.x,p.y);const cargo=p.status==='cargo';ctx.strokeStyle=p.hp<42?'#ff6375':'#ffc857';ctx.fillStyle='#1e3037';ctx.lineWidth=3;ctx.shadowColor=ctx.strokeStyle;ctx.shadowBlur=10;ctx.beginPath();ctx.roundRect(-14,-9,28,18,8);ctx.fill();ctx.stroke();ctx.shadowBlur=0;if(!cargo){ctx.globalAlpha=.32;ctx.beginPath();ctx.arc(0,0,48,0,Math.PI*2);ctx.stroke();ctx.globalAlpha=1;ctx.fillStyle='#eefcff';ctx.font='600 11px system-ui';ctx.textAlign='center';ctx.fillText(p.id+' · '+Math.ceil(p.hp)+'%',0,-56);if(p.stabilize>0){ctx.fillStyle='rgba(0,0,0,.7)';ctx.fillRect(-28,18,56,5);ctx.fillStyle='#ffc857';ctx.fillRect(-28,18,56*(p.stabilize/100),5)}}ctx.restore()}
  function drawEnemy(e){ctx.save();ctx.translate(e.x,e.y);ctx.rotate(Math.PI);ctx.fillStyle='#b72a42';ctx.strokeStyle='#ff8290';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(18,0);ctx.lineTo(-11,-9);ctx.lineTo(-5,0);ctx.lineTo(-11,9);ctx.closePath();ctx.fill();ctx.stroke();ctx.restore()}
  function drawShot(s){ctx.save();ctx.fillStyle='#72f7ff';ctx.shadowColor='#72f7ff';ctx.shadowBlur=9;ctx.beginPath();ctx.arc(s.x,s.y,3,0,Math.PI*2);ctx.fill();ctx.restore()}
  function drawShip(){const s=state.ship;ctx.save();ctx.translate(s.x,s.y);ctx.rotate(s.angle);ctx.fillStyle='#d4eef2';ctx.strokeStyle='#42dce5';ctx.lineWidth=2;ctx.shadowColor='#42dce5';ctx.shadowBlur=10;ctx.beginPath();ctx.moveTo(22,0);ctx.lineTo(-13,-13);ctx.lineTo(-8,-4);ctx.lineTo(-19,0);ctx.lineTo(-8,4);ctx.lineTo(-13,13);ctx.closePath();ctx.fill();ctx.stroke();ctx.shadowBlur=0;ctx.fillStyle='#ffc857';ctx.fillRect(-18,-3,7,6);if(s.pulse>0){ctx.strokeStyle='rgba(87,231,255,'+(.25+s.pulse/2)+')';ctx.beginPath();ctx.arc(0,0,32+s.pulse*10,0,Math.PI*2);ctx.stroke()}ctx.restore()}

  function updateUI(){
    const s=state.ship,p=s.cargo;$('#shipVal').textContent=Math.ceil(s.hp)+'%';$('#shipBar').style.width=s.hp+'%';$('#shieldVal').textContent=Math.ceil(s.shield)+'%';$('#shieldBar').style.width=s.shield+'%';$('#shipMeter').classList.toggle('danger',s.hp<35);
    $('#podVal').textContent=p?Math.ceil(p.hp)+'%':'None';$('#podBar').style.width=p?p.hp+'%':'0%';$('#rescuedCount').textContent=state.rescued;$('#remainingCount').textContent=state.pods.filter(x=>x.status==='adrift'||x.status==='cargo').length;$('#riskCount').textContent=state.risk;$('#lostCount').textContent=state.lost;
    let obj;if(p)obj='Escort '+p.id+' safely to the blue rescue gate.';else{const n=nearestPod();obj=n?'Approach '+n.id+', stabilize the pod, then extract.':'Return to the rescue gate.'}$('#objective').textContent=obj;
  }
  function derived(){
    const of=t=>events.filter(e=>e.type===t), lat=of('handoff').map(e=>e.data.latency).sort((a,b)=>a-b);return{rescued:of('rescue').length,lost:of('lost').length,human:of('extraction').filter(e=>e.data.owner==='human').length,ai:of('extraction').filter(e=>e.data.owner==='ai').length,intercepts:of('interception').length,handoffs:of('handoff').length,rejected:of('rejected').length,repairs:of('repair').length,events:events.filter(e=>['handoff','rejected','interception','extraction','repair'].includes(e.type)).length,latency:lat.length?lat[Math.floor(lat.length/2)]:null};
  }
  function updateStats(){const d=derived();$('#statRescued').textContent=d.rescued;$('#statLost').textContent=d.lost;$('#statHuman').textContent=d.human;$('#statAI').textContent=d.ai;$('#statIntercept').textContent=d.intercepts;$('#statHandoffs').textContent=d.handoffs;$('#statRejected').textContent=d.rejected;$('#statRepairs').textContent=d.repairs;$('#statEvents').textContent=d.events;$('#statLatency').textContent=d.latency===null?'—':d.latency.toFixed(2)}
  function renderLog(){
    const labels={handoff:'Handoff',rejected:'Rejected',interception:'Defense',extraction:'Extracted',repair:'Repair',rescue:'Rescued',lost:'Lost',mission:'Mission'};
    const relevant=events.filter(e=>labels[e.type]).slice(-30).reverse();$('#eventLog').innerHTML=relevant.length?relevant.map(e=>{
      const d=e.data;let txt='';if(e.type==='handoff')txt=pretty(d.task)+': '+d.from+' → '+d.to+' · epoch '+d.epoch+' · '+d.latency.toFixed(2)+' ms';if(e.type==='rejected')txt=pretty(d.task)+' '+d.reason;if(e.type==='interception')txt='Interceptor stopped by '+d.owner;if(e.type==='extraction')txt=d.pod+' recovered by '+d.owner;if(e.type==='repair')txt='Vessel systems restored 