<!-- SOLID CITY · part 2932 -->
et<br><span class="kbd">ARROWS</span> + <span class="kbd">SPACE</span></div></div>
        </div>
      </section>
      <section class="panel">
        <h2 class="panel-title">LIVE TELEMETRY <span>THIS RUN</span></h2>
        <div class="telemetry"><div><b id="handoffCount">0</b><span>HANDOFFS</span></div><div><b id="conflictCount">0</b><span>CONFLICTS</span></div><div><b id="warningCount">0</b><span>WARNINGS</span></div></div>
      </section>
      <section class="panel log">
        <h2 class="panel-title">OWNERSHIP LOG <span>NEWEST FIRST</span></h2>
        <ol class="log-list" id="logList" aria-live="polite"></ol>
      </section>
    </aside>
  </div>

  <section class="architecture" aria-labelledby="archTitle">
    <div class="architecture-head"><div><h2 id="archTitle">Rider architecture</h2><p>Movement and weapons are separate authority lanes. A human claim never spills across lanes; the coordinator merges their outputs only after ownership is settled.</p></div><p><b>Control law:</b> touch → immediate human ownership → AI yields that task → release → 800 ms guarded coast → smooth AI resume. Hazard warnings advise; they never seize a human-held task.</p></div>
    <div class="arch-grid">
      <article class="arch-node"><h3>1 · INDEPENDENT INPUTS</h3><p>Movement receives heading, thrust, dodge, and destination intent. Weapons receives aim vector, target choice, fire, and defensive priority.</p><p class="rule">The lanes never imply ownership of each other.</p></article>
      <div class="arch-arrow" aria-hidden="true">→</div>
      <article class="arch-node"><h3>2 · TASK ARBITER</h3><p>Human contact is the highest-priority claim. Release opens a guarded handoff window; AI intent blends in rather than snapping.</p><p class="rule">Per-task yield target: same input frame.</p></article>
      <div class="arch-arrow" aria-hidden="true">→</div>
      <article class="arch-node"><h3>3 · FLIGHT COORDINATOR</h3><p>Accepted movement and weapons commands reach one ship. Teal Wing follows formation and communicates threats without taking control.</p><p class="rule">Mission truth is destination + survival, not kill count.</p></article>
    </div>
    <div class="research-strip">
      <div><b>INDEPENDENT OWNERSHIP</b><span>Track each lane without a shared lock.</span></div>
      <div><b>SIMULTANEOUS HANDOFF</b><span>Human may claim both in the same frame.</span></div>
      <div><b>NAVIGATION UNDER FIRE</b><span>Measure destination progress while evading.</span></div>
      <div><b>AIM DURING DODGE</b><span>Weapons remain stable through movement changes.</span></div>
      <div><b>RECOVERY</b><span>Observe smooth AI resumption after intervention.</span></div>
    </div>
  </section>
</main>
<script>
(function(){
  'use strict';
  var canvas=document.getElementById('game'),ctx=canvas.getContext('2d');
  var els={
    moveOwner:document.getElementById('moveOwner'),weaponOwner:document.getElementById('weaponOwner'),moveLatency:document.getElementById('moveLatency'),weaponLatency:document.getElementById('weaponLatency'),
    hullBar:document.getElementById('hullBar'),hullText:document.getElementById('hullText'),distanceText:document.getElementById('distanceText'),killsText:document.getElementById('killsText'),warning:document.getElementById('warning'),
    handoffCount:document.getElementById('handoffCount'),conflictCount:document.getElementById('conflictCount'),warningCount:document.getElementById('warningCount'),logList:document.getElementById('logList'),
    startCard:document.getElementById('startCard'),debrief:document.getElementById('debrief'),debriefEyebrow:document.getElementById('debriefEyebrow'),debriefTitle:document.getElementById('debriefTitle'),scoreHull:document.getElementById('scoreHull'),scoreHandoffs:document.getElementById('scoreHandoffs'),scoreTime:document.getElementById('scoreTime')
  };
  var W=1000,H=560,dpr=1,last=0,raf=0;
  var state,keys={},pointerMove=false,pointerWeapon=false;
  var input={move:{x:0,y:0},weapon:{x:1,y:0},firing:false};
  var owners={move:'human',weapon:'ai'},releaseAt={move:0,weapon:0};
  var mode='rider';
  var colors={gold:'#f0b83b',gold2:'#ffde78',teal:'#43d8cf',teal2:'#8afff6',red:'#ff6259',white:'#e8f7f3'};

  function resize(){
    var r=canvas.getBoundingClientRect();dpr=Math.min(window.devicePixelRatio||1,2);
    canvas.width=Math.max(1,Math.round(r.width*dpr));canvas.height=Math.max(1,Math.round(r.height*dpr));
    W=r.width;H=r.height;ctx.setTransform(dpr,0,0,dpr,0,0);
  }
  window.addEventListener('resize',resize,{passive:true});resize();

  function reset(){
    state={running:false,elapsed:0,hull:100,progress:0,target:3600,kills:0,handoffs:0,conflicts:0,warnings:0,warningOn:false,warningCooldown:0,spawn:1.1,shot:0,player:{x:W*.30,y:H*.52,vx:0,vy:0},wing:{x:W*.22,y:H*.61},enemies:[],rocks:[],bullets:[],enemyBullets:[],stars:[],particles:[]};
    for(var i=0;i<100;i++)state.stars.push({x:Math.random()*W,y:Math.random()*H,z:.25+Ma