<!-- SOLID CITY · part 3171 -->
state.ship.y)*Math.min(1,dt*2);}
    if(state.summit&&state.summitPhase===2&&!state.ended){var heart=firstAlive();if(heart&&heart.type==='heart'){state.heartCharge=Math.min(100,state.heartCharge+dt*(100/22));if(state.heartCharge>=100){nova();return;}}}
    ai(dt);
    for(var i=state.shots.length-1;i>=0;i--){var s=state.shots[i];s.t+=dt;var q=Math.min(1,s.t/s.d);s.x=s.x+(s.tx-s.x)*Math.min(1,dt*8);s.y=s.y+(s.ty-s.y)*Math.min(1,dt*8);if(q>=1){hitTarget(s);state.shots.splice(i,1);}}
    for(var j=state.enemyShots.length-1;j>=0;j--){var e=state.enemyShots[j];e.t+=dt;e.x=e.x+(e.tx-e.x)*Math.min(1,dt*4.5);e.y=e.y+(e.ty-e.y)*Math.min(1,dt*4.5);if(e.t>=e.d){enemyHit(e);state.enemyShots.splice(j,1);}}
    for(var k=state.particles.length-1;k>=0;k--){var p=state.particles[k];p.t+=dt;p.x+=p.vx*dt;p.y+=p.vy*dt;if(p.t>p.d)state.particles.splice(k,1);}
    if(state.time%10<dt&&state.wings<state.maxWings&&state.phase>0&&Math.random()<.23){state.wings++;record('WING_REINFORCED',{phase:state.phase+1});}
    state.frameSamples.push(1/Math.max(dt,.001));if(state.frameSamples.length>40)state.frameSamples.shift();state.fps=state.frameSamples.reduce(function(a,b){return a+b},0)/state.frameSamples.length;
    updateHud();
  }
  function drawShellHalf(side,off,fx,fy,fr,W,H,time){ctx.save();ctx.beginPath();if(side<0)ctx.rect(0,0,fx,H);else ctx.rect(fx,0,W-fx,H);ctx.clip();ctx.translate(fx+side*off,fy);ctx.rotate(time*.025);ctx.globalAlpha=.55;ctx.beginPath();for(var i=0;i<14;i++){var a=i/14*Math.PI*2,r=fr*(.78+((i*17)%7)/24);ctx.lineTo(Math.cos(a)*r,Math.sin(a)*r)}ctx.closePath();var ag=ctx.createRadialGradient(-fr*.3,-fr*.3,2,0,0,fr);ag.addColorStop(0,'#46515a');ag.addColorStop(.55,'#222b31');ag.addColorStop(1,'#0d1317');ctx.fillStyle=ag;ctx.fill();ctx.strokeStyle='#60717a';ctx.lineWidth=1;ctx.stroke();ctx.restore();}
  function draw(){
    ctx.clearRect(0,0,W,H);var g=ctx.createRadialGradient(W*.72,H*.5,0,W*.72,H*.5,W*.7);g.addColorStop(0,'#142029');g.addColorStop(.38,'#080d12');g.addColorStop(1,'#020406');ctx.fillStyle=g;ctx.fillRect(0,0,W,H);
    stars.forEach(function(s){ctx.globalAlpha=s.a;ctx.fillStyle='#d9ffff';ctx.fillRect(s.x*W,s.y*H,s.s,s.s)});ctx.globalAlpha=1;
    // fortress asteroid — the summit splits the shell to reveal the heart
    var fx=.73*W,fy=.5*H,fr=Math.min(W,H)*.13;
    if(state&&state.summit){
      var off=state.reveal*fr*1.7;drawShellHalf(-1,off,fx,fy,fr,W,H,state.time);drawShellHalf(1,off,fx,fy,fr,W,H,state.time);
      ctx.save();ctx.translate(fx,fy);ctx.rotate(-state.time*.05);ctx.beginPath();for(var i=0;i<14;i++){var a=i/14*Math.PI*2,r=fr*.52*(.78+((i*17)%7)/24);ctx.lineTo(Math.cos(a)*r,Math.sin(a)*r)}ctx.closePath();var hg=ctx.createRadialGradient(-fr*.15,-fr*.15,2,0,0,fr*.52);hg.addColorStop(0,'#8a6a1f');hg.addColorStop(.55,'#4a3a12');hg.addColorStop(1,'#1a1408');ctx.fillStyle=hg;ctx.fill();ctx.strokeStyle='#f0ae31';ctx.lineWidth=1.5;ctx.stroke();ctx.restore();
      if(state.summitPhase===2&&state.heartCharge>0){ctx.strokeStyle='rgba(255,120,60,'+(.35+.5*state.heartCharge/100).toFixed(2)+')';ctx.lineWidth=3;ctx.beginPath();ctx.arc(fx,fy,fr*.62,-Math.PI/2,-Math.PI/2+Math.PI*2*state.heartCharge/100);ctx.stroke();}
    }else{
      ctx.save();ctx.translate(fx,fy);ctx.rotate(state?state.time*.025:0);ctx.beginPath();for(var i=0;i<14;i++){var a=i/14*Math.PI*2,r=fr*(.78+((i*17)%7)/24);ctx.lineTo(Math.cos(a)*r,Math.sin(a)*r)}ctx.closePath();var ag=ctx.createRadialGradient(-fr*.3,-fr*.3,2,0,0,fr);ag.addColorStop(0,'#46515a');ag.addColorStop(.55,'#222b31');ag.addColorStop(1,'#0d1317');ctx.fillStyle=ag;ctx.fill();ctx.strokeStyle='#60717a';ctx.lineWidth=1;ctx.stroke();ctx.restore();
    }
    if(!state)return;
    // orbit rings per phase — summit rings are the outer rings at half scale
    var ringR=state.summit?[.115,.075,.04][state.summitPhase]:(state.phase===0?.23:state.phase===1?.15:.08);
    ctx.strokeStyle=state.summit?(state.summitPhase===0?'rgba(255,76,50,.35)':state.summitPhase===1?'rgba(42,214,201,.3)':'rgba(240,174,49,.4)'):(state.phase===0?'rgba(255,76,50,.3)':state.phase===1?'rgba(42,214,201,.28)':'rgba(255,181,52,.35)');ctx.lineWidth=1;ctx.setLineDash([4,7]);ctx.beginPath();ctx.arc(fx,fy,ringR*Math.min(W,H)*1.4,0,Math.PI*2);ctx.stroke();ctx.setLineDash([]);
    state.targets.forEach(drawTarget);
    // wings
    for(var wi=0;wi<state.wings;wi++){var wa=state.time*(state.formation==='SCREEN'?.8:1.2)+wi/state.wings*Math.PI*2;var wr=state.formation==='SCREEN'?22+wi%3*4:14+wi%4*7;var wx=state.ship.x*W+Math.cos(wa)*wr,wy=state.ship.y*H+Math.sin(wa)*wr*.55;ctx.fillStyle='#35d5cb';ctx.beginPath();ctx.moveTo(wx+5,wy);ctx.lineTo(wx-3,wy-2.5);ctx.lineTo(wx-1,wy);ctx.lineTo(wx-3,wy+2.5);ctx.closePath();ctx.fill();}
    // capital ship
    var sx=state.ship.x*W,sy=state.ship.y*H;ctx.save();ctx.translate(sx,sy);ctx.fillStyle='#d8e9ea';ctx.strokeStyle='#0b1216';ctx.lineWidth=2;ctx.beginPath();ctx.moveTo(34,0);ctx.lineTo(-22,-12);ctx.lineTo(-10,0);ctx.lineTo(-22,12);ctx.clo