<!-- SAGA-STAGING · part 61 -->
===== */
/* ===== SHADOW LOCOMOTION — v206 ===== */
ActorRuntime.shadowMotion={
 velocity:new THREE.Vector3(0,0,0),
 desired:new THREE.Vector3(0,0,0),
 predicted:new THREE.Vector3(0,0,0),
 dt:0
};
ActorRuntime.calculateShadowMotion=function(dt,input,yaw,startPos){
 var R=ActorRuntime,S=R.shadowMotion,I=input||R.input;
 dt=Math.max(0,Math.min(Number.isFinite(dt)?dt:0,0.05));
 yaw=Number.isFinite(yaw)?yaw:0;
 var x=Number.isFinite(I.moveX)?I.moveX:0,z=Number.isFinite(I.moveZ)?I.moveZ:0;
 var mag=Math.sqrt(x*x+z*z);if(mag>1){x/=mag;z/=mag;mag=1;}
 var sy=Math.sin(yaw),cy=Math.cos(yaw),speed=R.physics.walkSpeed;
 S.desired.x=(sy*x-cy*z)*speed;S.desired.y=0;S.desired.z=(-cy*x-sy*z)*speed;
 var rate=mag>0?R.physics.acceleration:R.physics.braking,max=rate*dt;
 function approach(v,t){var d=t-v;return v+Math.max(-max,Math.min(max,d));}
 S.velocity.x=approach(S.velocity.x,S.desired.x);S.velocity.z=approach(S.velocity.z,S.desired.z);S.velocity.y=0;
 var base=startPos||((typeof kingPos!=="undefined"&&kingPos)?kingPos:null);
 if(base)S.predicted.copy(base).addScaledVector(S.velocity,dt);
 S.dt=dt;R.debug.ticks++;return S;
};
/* ===== END SHADOW LOCOMOTION — v206 ===== */
/* ===== LIVE SHADOW OBSERVER — v206 ===== */
ActorRuntime.shadowLive={enabled:true,lastTime:performance.now(),samples:0,lastReal:new THREE.Vector3(),lastPredictionError:null};
ActorRuntime.tickShadow=function(){
 var R=ActorRuntime,L=R.shadowLive,now=performance.now(),dt=Math.min((now-L.lastTime)/1000,0.05);L.lastTime=now;
 if(!L.enabled||R.authority!=="legacy"&&R.authority!=="ai_guarded"||typeof kingPos==="undefined"||!kingPos)return;
 var before=kingPos.clone();
 R.calculateShadowMotion(dt,R.input,(typeof camYaw!=="undefined"?camYaw:0),before);
 L.samples++;
 L.lastReal.copy(before);
 L.lastPredictionError=R.shadowMotion.predicted.distanceTo(before);
};
setInterval(function(){try{ActorRuntime.tickShadow();}catch(e){ActorRuntime.debug.lastError=String(e);}},16);
/* ===== END LIVE SHADOW OBSERVER — v206 ===== */
/* ===== TERRAIN PREDICTION — v208 ===== */
ActorRuntime.terrainPrediction={classification:"WAIT",support:false,valid:false,groundY:null,delta:null,slope:null,surface:"NONE",step:0,safe:false,samples:0};
ActorRuntime.predictTerrain=function(){
 var R=ActorRuntime,T=R.terrainPrediction,S=R.shadowMotion,WF=window.WORLD_FOUNDATION;
 T.classification="WAIT";T.support=false;T.valid=false;T.groundY=null;T.delta=null;T.slope=null;T.surface="NONE";T.step=0;T.safe=false;
 if(!WF||typeof WF.supportBelow!=="function"||!S||typeof kingPos==="undefined"||!kingPos)return T;
 var probe=S.predicted.clone(),cur=WF.supportBelow(kingPos,{startOffset:2,maxDrop:8,maxSlope:R.physics.maxSlope}),next=WF.supportBelow(probe,{startOffset:2,maxDrop:8,maxSlope:R.physics.maxSlope});
 T.samples++;
 if(!next||!next.hit){T.classification="AIR / DROP";return T;}
 T.support=true;T.valid=!!next.valid;T.groundY=next.height;T.delta=probe.y-next.height;T.slope=next.slope;T.surface=next.kind||"surface";
 var currentY=(cur&&cur.hit)?cur.height:kingPos.y-R.physics.rootClearance;
 T.step=next.height-currentY;
 if(!next.valid||next.slope>R.physics.maxSlope){T.classification="UNSAFE SLOPE";return T;}
 if(T.step>R.physics.stepHeight){T.classification="STEP TOO HIGH";return T;}
 if(T.step < -R.physics.stepHeight){T.classification="DROP";return T;}
 T.safe=true;
 T.classification=Math.abs(T.step)>0.08?"STEP / SLOPE":"SAFE GROUND";
 return T;
};
setInterval(function(){try{if(ActorRuntime.authority==="legacy"||ActorRuntime.authority==="ai_guarded")ActorRuntime.predictTerrain();}catch(e){ActorRuntime.debug.lastError=String(e);}},50);
/* ===== END TERRAIN PREDICTION — v208 ===== */
/* ===== REAL MOVEMENT CANDIDATE — v215 ===== */
ActorRuntime.realCandidate={version:215,verdict:"WAIT",allowed:false,reason:"startup",x:null,y:null,z:null,groundY:null,step:null,slope:null,surface:"NONE",samples:0};
ActorRuntime.evaluateRealCandidate=function(){
 var R=this,C=R.realCandidate,S=R.shadowMotion,WF=window.WORLD_FOUNDATION;
 C.verdict="WAIT";C.allowed=false;C.reason="not_ready";C.groundY=null;C.step=null;C.slope=null;C.surface="NONE";
 if(!S||!WF||typeof WF.supportBelow!=="function"||typeof kingPos==="undefined"||!kingPos)return C;
 var p=S.predicted.clone();
 C.x=p.x;C.y=p.y;C.z=p.z;C.samples++;
 var cur=WF.supportBelow(kingPos,{startOffset:2,maxDrop:8,maxSlope:R.physics.maxSlope});
 var nxt=WF.supportBelow(p,{startOffset:2,maxDrop:8,maxSlope:R.physics.maxSlope});
 if(!nxt||!nxt.hit){C.verdict="BLOCK";C.reason="no_support";return C;}
 C.groundY=nxt.height;C.slope=nxt.slope;C.surface=nxt.kind||"surface";
 var gy=(cur&&cur.hit)?cur.height:kingPos.y-R.physics.rootClearance;
 C.step=nxt.height-gy;
 if(!nxt.valid||C.slope>R.physics.maxSlope){C.verdict="BLOCK";C.reason="slope";return C;}
 if(C.step>R.physics.stepHeight){C.verdict="BLOCK";C.reason="step_high";return C;}
 if(C.step < -R.physics.stepHeight){C.verdict="BLOCK";C.reason="drop";return C;}
 if(p.y<R.safety.killY){C.ve