<!-- RUBY-BACKUP · part 70 -->
tap an anchored structure to free it':'unanchor off');
}

/* ---------------- RIGS — rope through pulleys to a structure ---------------- */
var rigs=[];
var rigArmed=false, rigChain=[];
function armRig(){
  closeMenu();
  rigArmed=!rigArmed; rigChain=[];
  status(rigArmed?'tap a pulley — then tap the road, bridge, or block':'rig off');
}
function finalizeRig(target, pointW){
  try{
    mats();
    if(typeof scene==='undefined') return;
    var chain=rigChain.slice();
    if(!chain.length){ status('tap a pulley first'); return; }
    var Pn=chain[chain.length-1];
    var body=targetBody(target);
    body.updateWorldMatrix(true,false);
    var attachLocal=body.worldToLocal(pointW.clone());
    // handle starts on the far side of the first pulley, away from the target
    var away=Pn.pos.clone().sub(pointW); away.y=0;
    if(away.lengthSq()<1e-6) away.set(0,0,1);
    away.normalize();
    var h0=2.2;
    var hg=new T.Group();
    var ring=new T.Mesh(new T.TorusGeometry(0.22,0.06,12,24), MAT.gold);
    hg.add(ring);
    var hit=new T.Mesh(new T.SphereGeometry(0.5,8,6), MAT.hit);
    hg.add(hit);
    var hp=Pn.pos.clone().add(away.clone().multiplyScalar(h0));
    hg.position.copy(hp);
    scene.add(hg);
    var rig={
      pulleys:chain, target:target, body:body, attachLocal:attachLocal,
      handle:{group:hg, hit:hit}, ropes:[],
      axis:away.clone(), P1:chain[0].pos.clone(),
      h:h0, theta:0, snap:null, targetSeg:0, fixedSegs:0, Lt:0,
      _deformCorner:null
    };
    hit.userData.rigPart={rig:rig, part:'handle'};
    // rope segments: handle->P0, P0->P1, ..., Pn->attach
    var nseg=chain.length+1;
    for(var i=0;i<nseg;i++){
      var rm=new T.Mesh(new T.CylinderGeometry(0.035,0.035,1,8), MAT.rope);
      rm.userData.rigPart={rig:rig, part:'rope'};
      scene.add(rm);
      rig.ropes.push(rm);
    }
    // lengths: fixed segments between pulleys, variable handle + target segs
    var fixed=0, prev=hp;
    for(var j=0;j<chain.length;j++){ fixed+=prev.distanceTo(chain[j].pos); prev=chain[j].pos; }
    var lastSeg=prev.distanceTo(pointW);
    rig.fixedSegs=fixed-h0; // fixed excludes the handle segment
    rig.Lt=h0+(fixed-h0)+lastSeg;
    rig.targetSeg=lastSeg;
    rigs.push(rig);
    refreshRig(rig);
    status('rigged — pull the gold ring. the '+(target.kind==='frame'?'frame':'block')+' is on a swivel now');
  }catch(e){ status('rig failed: '+e.message); }
}
/* redraw a rig's ropes: handle -> pulleys -> live attach point */
function refreshRig(rig){
  try{
    if(typeof scene==='undefined') return;
    rig.body.updateWorldMatrix(true,false);
    var Aw=rig.body.localToWorld(rig.attachLocal.clone());
    var P1=rig.pulleys[0].pos;
    var hp=P1.clone().add(rig.axis.clone().multiplyScalar(rig.h));
    rig.handle.group.position.copy(hp);
    var pts=[hp];
    for(var i=0;i<rig.pulleys.length;i++) pts.push(rig.pulleys[i].pos);
    pts.push(Aw);
    for(var j=0;j<rig.ropes.length&&j+1<pts.length;j++) aimCyl(rig.ropes[j],pts[j],pts[j+1]);
  }catch(e){}
}
function refreshAllRopes(){ for(var i=0;i<rigs.length;i++) refreshRig(rigs[i]); }
function clearRigs(){
  closeMenu();
  for(var i=0;i<rigs.length;i++){
    var r=rigs[i];
    for(var j=0;j<r.ropes.length;j++) if(r.ropes[j].parent) r.ropes[j].parent.remove(r.ropes[j]);
    if(r.handle.group.parent) r.handle.group.parent.remove(r.handle.group);
  }
  rigs=[]; activePull=null;
  for(var k=0;k<pulleys.length;k++) if(pulleys[k].group.parent) pulleys[k].group.parent.remove(pulleys[k].group);
  pulleys=[];
  rigArmed=false; rigChain=[]; pulleyArmed=false; anchorArmed=false; unanchorArmed=false;
  status('rigging cleared — ropes, handles, pulleys gone');
}

/* snapshot exposures — the rewind system needs these */
try{
  window.__clearRigs = (typeof clearRigs === 'function') ? clearRigs : null;
  window.__clearLifts = (typeof clearLiftStakes === 'function') ? clearLiftStakes : null;
  window.__getRigs = function(){ try{ return (typeof rigs !== 'undefined') ? rigs : []; }catch(e){ return []; } };
  window.__getPulleys = function(){ try{ return (typeof pulleys !== 'undefined') ? pulleys : []; }catch(e){ return []; } };
  window.__placePulleyAt = function(x, y, z){
    try{
      if(typeof placePulley !== 'function') return null;
      var v = new THREE.Vector3(x, y, z);
      return placePulley(v);
    }catch(e){ return null; }
  };
  window.__getLiftStakes = function(){ try{ return (typeof liftStakes !== 'undefined') ? liftStakes : []; }catch(e){ return []; } };
  window.__getLifts = function(){ try{ return (typeof lifts !== 'undefined') ? JSON.parse(JSON.stringify(lifts)) : []; }catch(e){ return []; } };
  window.__setLifts = function(arr){
    try{
      if(typeof lifts === 'undefined') return false;
      lifts.length = 0;
      var list = arr || [];
      for(var i=0;i<list.length;i++){
        var L = list[i];
        lifts.push({x:+L.x||0, z:+L.z||0, r:+L.r||6, h:+L.h||0});
      }
      if(typeof persistLifts === 'functio