<!-- RUBY-BACKUP · part 43 -->
ove it','—'],
 ['duplicate','delete','make another','—'],
 ['fly','land','leave the ground','—'],
 ['duck','stand','see things lower','—'],
 ['speed','','shift the pace','×N'],
 ['span','unspan','rope two objects — or tap empty ground to grow a synced twin','SPAN 1:1 SYNCED / −1:1']
];
var codexEl=null;
function openCodex(){
  try{
    closeCodex();
    var el=document.createElement('div');
    el.id='grandCodex';
    el.style.cssText='position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);'
      +'width:min(430px,92vw);max-height:82vh;z-index:40000;'
      +'background:rgba(8,6,3,0.97);border:2px solid #d8a920;border-radius:18px;'
      +'box-shadow:0 0 40px rgba(216,169,32,0.4);display:flex;flex-direction:column;overflow:hidden;';
    var html='<div style="padding:14px 16px 10px 16px;color:#ffd700;font-family:Georgia,serif;'
      +'font-size:16px;letter-spacing:5px;text-align:center;border-bottom:1px solid #8a6a1c;">CODEX</div>'
      +'<div style="padding:4px 14px 8px 14px;color:#8a6a1c;font-family:Georgia,serif;font-size:11px;'
      +'font-style:italic;text-align:center;">every function · its antonym · its reason · its law</div>'
      +'<div style="overflow-y:auto;-webkit-overflow-scrolling:touch;padding:6px 10px 12px 10px;">';
    for(var i=0;i<CODEX.length;i++){
      var r=CODEX[i];
      html+='<div style="padding:9px 10px;margin:3px 0;border:1px solid #5a451a;border-radius:10px;">'
        +'<div style="color:#ffd700;font-family:Georgia,serif;font-size:15px;">'
        +r[0]+(r[1]?' <span style="color:#8a6a1c;">↔ '+r[1]+'</span>':'')+'</div>'
        +'<div style="color:#c9a227;font-family:Georgia,serif;font-size:12px;font-style:italic;">'+r[2]+'</div>'
        +(r[3]&&r[3]!=='—'?'<div style="color:#8a6a1c;font-family:monospace;font-size:11px;">'+r[3]+'</div>':'')
        +'</div>';
    }
    html+='</div>'
      +'<div id="codexClose" style="padding:12px;text-align:center;color:#ffd700;font-family:Georgia,serif;'
      +'font-size:15px;border-top:1px solid #8a6a1c;cursor:pointer;-webkit-tap-highlight-color:transparent;">✕ CLOSE</div>';
    el.innerHTML=html;
    document.body.appendChild(el);
    codexEl=el;
    var cx=document.getElementById('codexClose');
    function shut(e){ try{ if(e){ e.preventDefault(); e.stopPropagation(); } }catch(x){} closeCodex(); }
    if(cx){ cx.addEventListener('touchstart',shut,{passive:false}); cx.addEventListener('mousedown',shut); }
    el.addEventListener('touchstart',function(e){ e.stopPropagation(); },{passive:true});
    el.addEventListener('mousedown',function(e){ e.stopPropagation(); });
  }catch(e){}
}
function closeCodex(){
  try{ if(codexEl&&codexEl.parentNode) codexEl.parentNode.removeChild(codexEl); }catch(e){}
  codexEl=null;
}


/* ============================================================================
   SPAN — rope two roads together. Move one, both answer.
   `span`: tap road A, tap road B — the rope spans the distance.
   Tap the gold ring: SYNCED (1:1, B follows A) or MIRRORED (−1:1, B opposes A).
   Rigid law: the sync is exact. The rope stays taut and true.
   `unspan` cuts every span — roads move independently again.
   ============================================================================ */
var spans=[];
if(typeof window!=='undefined') window.__spans = spans;
var spanArmed=false, spanA=null;
var spanRunning=false, _lastSpanT=0;

function armSpan(){
  spanArmed=true; spanA=null;
  status('span — tap an object, then tap another object — or tap empty ground to grow a twin');
}
function hitSysPart(cx,cy){
  try{
    if(typeof camera==='undefined'||typeof scene==='undefined') return false;
    setNDC(cx,cy); raycaster.setFromCamera(ndc,camera);
    var hits=raycaster.intersectObjects(scene.children,true);
    for(var i=0;i<hits.length;i++){
      var q=hits[i].object;
      while(q && !q.isScene){
        var ud=q.userData||{};
        if(ud.rigPart||ud.grandPart||ud.rigPulley||ud.deformHandle||ud.deformFrame||ud.liftStake) return true;
        q=q.parent;
      }
    }
  }catch(e){}
  return false;
}
function pickGroundPoint(cx,cy){
  try{
    if(typeof camera==='undefined'||typeof scene==='undefined') return null;
    setNDC(cx,cy); raycaster.setFromCamera(ndc,camera);
    var hits=raycaster.intersectObjects(scene.children,true);
    for(var i=0;i<hits.length;i++){
      var q=hits[i].object, bad=false;
      while(q && !q.isScene){
        var ud=q.userData||{};
        if(ud.rigPart||ud.grandPart||ud.deformHandle||ud.liftStake){ bad=true; break; }
        q=q.parent;
      }
      if(!bad) return hits[i].point.clone();
    }
    var plane=new T.Plane(new T.Vector3(0,1,0),-0.3);
    var pt=new T.Vector3();
    if(raycaster.ray.intersectPlane(plane,pt)) return pt;
  }catch(e){}
  return null;
}
function pickRoad(cx,cy){
  try{
    if(typeof camera==='undefined'||typeof scene==='undefined') return null;
    setNDC(cx,cy); raycaster.setFromCamera(ndc,camera);
    var hits=raycaster.interse