<!-- SOLID CITY · part 2915 -->
div>
          <div class="control-group" data-controls="protection"><div class="control-title">Your defense console</div><div class="control-row two"><button class="control-btn" id="fireBtn">Point defense</button><button class="control-btn" id="pulseBtn">Shield pulse</button></div></div>
          <div class="control-group" data-controls="support"><div class="control-title">Your engineering deck</div><div class="control-row two"><button class="control-btn" id="repairBtn">Repair vessel</button><button class="control-btn" id="lifeBtn">Boost life-support</button></div></div>
        </div>
        <div class="mission-state">
          <div class="count"><b id="rescuedCount">0</b><span>Rescued</span></div>
          <div class="count"><b id="remainingCount">3</b><span>Remaining</span></div>
          <div class="count"><b id="riskCount">0</b><span>At risk</span></div>
          <div class="count"><b id="lostCount">0</b><span>Lost</span></div>
        </div>
      </aside>
    </div>

    <div class="lower">
      <details open>
        <summary>Mission STATS · recorded events</summary>
        <div class="stats-grid">
          <div class="stat"><strong id="statRescued">0</strong><span>Pods rescued</span></div>
          <div class="stat"><strong id="statLost">0</strong><span>Pods lost</span></div>
          <div class="stat"><strong id="statHuman">0</strong><span>Human extractions</span></div>
          <div class="stat"><strong id="statAI">0</strong><span>AI extractions</span></div>
          <div class="stat"><strong id="statIntercept">0</strong><span>Interceptions</span></div>
          <div class="stat"><strong id="statLatency">—</strong><span>Median handoff ms</span></div>
          <div class="stat"><strong id="statHandoffs">0</strong><span>Handoffs</span></div>
          <div class="stat"><strong id="statRejected">0</strong><span>Stale rejected</span></div>
          <div class="stat"><strong id="statRepairs">0</strong><span>Repair actions</span></div>
          <div class="stat"><strong id="statEvents">0</strong><span>Coordination events</span></div>
        </div>
        <div class="log" id="eventLog" aria-live="polite"></div>
      </details>
      <details>
        <summary>How the rescue works</summary>
        <div class="story">
          <h3>Four moments, every life</h3>
          <ol><li><b>Approach:</b> navigate within the pod's rescue ring.</li><li><b>Stabilize:</b> extraction holds position while the rig calms the pod.</li><li><b>Recover:</b> the tractor brings the pilot aboard.</li><li><b>Protect:</b> defend the recovered pod until it reaches the blue gate.</li></ol>
          <h3>Twin-lightning law</h3>
          <p>Each station has one owner and an ownership epoch. A claim advances the epoch immediately; commands from the previous owner are rejected. AI advisories remain separate from actuator commands, so the crew may warn you but cannot override you.</p>
        </div>
      </details>
    </div>
  </main>
  <div class="toast" id="toast" role="status"></div>

<script>
(function(){
  'use strict';
  const $ = (s,root=document)=>root.querySelector(s);
  const $$ = (s,root=document)=>Array.from(root.querySelectorAll(s));
  const canvas=$('#battle'), ctx=canvas.getContext('2d');
  const W=960,H=600;
  let running=false,last=0,elapsed=0,adviceTimer=0,spawnTimer=0,finishTimer=0;
  let toastTimer=0;

  class ControlCoordination {
    constructor(tasks,onEvent){
      this.tasks={}; this.onEvent=onEvent; this.seen=new Set();
      tasks.forEach(name=>this.tasks[name]={owner:'ai',epoch:1,seq:0,connected:true});
    }
    owner(task){return this.tasks[task].owner}
    claim(task,owner){
      const t=this.tasks[task], started=performance.now();
      if(!t || t.owner===owner) return null;
      const from=t.owner; t.owner=owner; t.epoch++; t.seq=0;
      const latency=Math.max(0,performance.now()-started);
      this.onEvent('handoff',{task,from,to:owner,epoch:t.epoch,latency});
      return {task,owner,epoch:t.epoch,latency};
    }
    release(task){return this.claim(task,'ai')}
    command(task,owner,epoch,action){
      const t=this.tasks[task]; if(!t) return false;
      const key=task+':'+owner+':'+epoch+':'+action;
      if(t.owner!==owner || t.epoch!==epoch){this.onEvent('rejected',{task,owner,epoch,reason:'stale ownership'});return false}
      if(this.seen.has(key)){this.onEvent('rejected',{task,owner,epoch,reason:'duplicate command'});return false}
      this.seen.add(key); if(this.seen.size>160)this.seen.clear();
      return true;
    }
    snapshot(){return JSON.parse(JSON.stringify(this.tasks))}
  }

  let events=[], coordination;
  const state={};
  function resetState(){
    Object.assign(state,{
      ship:{x:118,y:310,vx:0,vy:0,angle:0,hp:100,shield:100,cargo:null,repairCd:0,pulse:0,fireCd:0},
      gate:{x:885,y:300,r:56},
      pods:[
        {id:'Aster',x:365,y:165,hp:100,status:'adrift',stabilize:0,story:'Test pilot Aster · oxygen stable'},