<!-- SAGA-SOLID · part 96 -->

  window._solidDriveInstalled = true;

  var SD = window.SOLID_DRIVE = {
    epoch: null,
    active: false,
    lastSeq: -1,
    lastIntentAt: 0,
    lastIntent: {moveX:0, moveZ:0},
    lastRejection: null,
    acceptedCount: 0,
    rejectedCount: 0,
    deadManTripped: false,
    sse: null,
    deadManTimer: null,
    sessionInvalidated: false,
  };

  var DEAD_MAN_MS = 500;
  var MAX_SPEED_UPS = 2.0; // Solid's max remote speed
  var WALK_SPEED = 12; // must match ActorRuntime.physics.walkSpeed
  var MAX_MAG = MAX_SPEED_UPS / WALK_SPEED; // 0.1667

  function sdLog(s){
    var o = document.getElementById('sd-out');
    if(o){
      o.textContent += s + '\n';
      o.scrollTop = o.scrollHeight;
    }
    if(window.console) console.log('[SolidDrive] ' + s);
  }

  function sdClear(){
    var o = document.getElementById('sd-out');
    if(o) o.textContent = '';
  }

  /* Dead-man: if no fresh intent within 500ms, zero intent + release AI */
  function sdArmDeadMan(){
    if(SD.deadManTimer) clearTimeout(SD.deadManTimer);
    SD.deadManTimer = setTimeout(function(){
      if(!SD.active || SD.sessionInvalidated) return;
      var age = Date.now() - SD.lastIntentAt;
      if(age >= DEAD_MAN_MS){
        sdLog('DEAD-MAN TRIPPED (age=' + age + 'ms) -> zero intent + release');
        SD.deadManTripped = true;
        try{
          var R = window.ACTOR_RUNTIME;
          if(R){
            R.submitIntent("ai", {moveX:0, moveZ:0});
            R.releaseAI();
            R.authority = "legacy";
          }
        }catch(e){}
        SD.active = false;
        sdUpdateDiag();
      }
    }, DEAD_MAN_MS + 50);
  }

  /* Validate and apply incoming SSE intent */
  function sdOnIntent(data){
    var R = window.ACTOR_RUNTIME;
    if(!R || !SD.active || SD.sessionInvalidated) return;

    // 1. Epoch must match active session
    if(data.epoch !== SD.epoch){
      SD.lastRejection = 'epoch_mismatch';
      SD.rejectedCount++;
      sdLog('REJECT: epoch_mismatch');
      sdUpdateDiag();
      return;
    }
    // 2. Sequence must be monotonically increasing
    if(typeof data.seq !== 'number' || data.seq <= SD.lastSeq){
      SD.lastRejection = 'stale_seq seq=' + data.seq + ' last=' + SD.lastSeq;
      SD.rejectedCount++;
      sdLog('REJECT: ' + SD.lastRejection);
      sdUpdateDiag();
      return;
    }
    // 3. Command age must be < 500ms
    var age = Date.now() - data.ts;
    if(age > DEAD_MAN_MS){
      SD.lastRejection = 'expired age=' + age + 'ms';
      SD.rejectedCount++;
      sdLog('REJECT: ' + SD.lastRejection);
      sdUpdateDiag();
      return;
    }
    // 4. Safety Supervisor must be SAFE/PERMIT
    var sv = R.safetySupervisor;
    if(!sv || sv.status !== "SAFE" || !sv.permit){
      SD.lastRejection = 'safety_not_permit';
      SD.rejectedCount++;
      sdLog('REJECT: safety_not_permit -> zero intent');
      try{ R.submitIntent("ai", {moveX:0, moveZ:0}); }catch(e){}
      sdUpdateDiag();
      return;
    }
    // 5. Schema: moveX/moveZ finite numbers in [-1, 1]
    var mx = data.moveX, mz = data.moveZ;
    if(typeof mx !== 'number' || !isFinite(mx) || Math.abs(mx) > 1 ||
       typeof mz !== 'number' || !isFinite(mz) || Math.abs(mz) > 1){
      SD.lastRejection = 'bad_schema';
      SD.rejectedCount++;
      sdLog('REJECT: bad_schema');
      sdUpdateDiag();
      return;
    }

    // 6. Clamp to 2.0 u/s (WALK only, no jump/ascend/descend in Stage 2)
    var mag = Math.sqrt(mx*mx + mz*mz);
    if(mag > MAX_MAG){
      var scale = MAX_MAG / mag;
      mx *= scale;
      mz *= scale;
    }

    // 7. Ensure AI guarded authority is active
    if(R.authority !== "ai_guarded" || !R.ai.active){
      R.authority = "ai_guarded";
      R.ai.active = true;
      R.ai.agentId = "solid-drive";
      R.ai.control = "remote";
    }

    // 8. Submit through existing guarded path
    var ok = R.submitIntent("ai", {moveX: mx, moveZ: mz});
    if(!ok){
      SD.lastRejection = 'submit_rejected';
      SD.rejectedCount++;
      sdLog('REJECT: submitIntent returned false');
      sdUpdateDiag();
      return;
    }

    // Accepted
    SD.lastSeq = data.seq;
    SD.lastIntentAt = Date.now();
    SD.lastIntent = {moveX: mx, moveZ: mz};
    SD.lastRejection = null;
    SD.acceptedCount++;
    SD.deadManTripped = false;
    sdArmDeadMan(); // reset dead-man on every accepted intent
    sdUpdateDiag();
  }

  /* Human takeover detection: if human grabs control, invalidate session */
  function sdWatchHuman(){
    setInterval(function(){
      if(!SD.active || SD.sessionInvalidated) return;
      var R = window.ACTOR_RUNTIME;
      if(!R) return;
      // If input owner is human but we think AI should be driving,
      // a human has taken over outside the bridge -> invalidate
      if(R.input.owner === "human" && R.ai.active){
        sdLog('HUMAN TAKEOVER detected -> invalidating session ' + SD.epoch);
        SD.sessionInvalidated = true;
        SD.active = false;
        try{
     