<!-- RUBY-BACKUP · part 37 -->
T.TorusGeometry(r||0.22,0.06,10,24), mats().gold);
  m.userData.grandPart=true;
  return m;
}
function hitSphere(r){
  var m=new T.Mesh(new T.SphereGeometry(r||0.6,8,8), mats().ghost);
  m.userData.grandPart=true;
  return m;
}

/* ---------------- machine registry ---------------- */
var MACHINES=[]; /* {kind, group, cleanup} */
function regMachine(kind,group,cleanup){
  group.userData.rigPart=true; /* rigging.js menu ignores our machines */
  (function tag(o){ o.userData.rigPart=true; for(var i=0;i<o.children.length;i++) tag(o.children[i]); })(group);
  var M={kind:kind,group:group,cleanup:cleanup||function(){}};
  MACHINES.push(M);
  return M;
}
/* snapshot exposures — the rewind system needs these */
try{
  window.__unspanAll = (typeof unspanAll === 'function') ? unspanAll : null;
  window.__getMachines = function(){ try{ return (typeof MACHINES !== 'undefined') ? MACHINES : []; }catch(e){ return []; } };
  window.__clearMachines = function(){
    try{
      if(typeof MACHINES === 'undefined') return;
      for(var i=MACHINES.length-1;i>=0;i--){
        try{ MACHINES[i].cleanup(); }catch(e){}
        try{ if(MACHINES[i].group.parent) MACHINES[i].group.parent.remove(MACHINES[i].group); }catch(e){}
        try{ if(typeof unregisterHits === 'function') unregisterHits(MACHINES[i].group); }catch(e){}
      }
      MACHINES.length = 0;
    }catch(e){}
  };
  window.__buildMachineAt = function(kind, x, z){
    try{
      var builders = {
        compound: (typeof buildCompound === 'function') ? buildCompound : null,
        counterweight: (typeof buildCounterweight === 'function') ? buildCounterweight : null,
        winch: (typeof buildWinch === 'function') ? buildWinch : null,
        gearlink: (typeof buildGearlink === 'function') ? buildGearlink : null,
        lever: (typeof buildLever === 'function') ? buildLever : null,
        spring: (typeof buildSpring === 'function') ? buildSpring : null
      };
      var b = builders[kind];
      if(!b) return null;
      var before = (typeof MACHINES !== 'undefined') ? MACHINES.length : 0;
      b();
      if(typeof MACHINES !== 'undefined' && MACHINES.length > before){
        var M = MACHINES[MACHINES.length - 1];
        if(M && M.group){ M.group.position.x = x; M.group.position.z = z; }
        return M;
      }
      return null;
    }catch(e){ return null; }
  };
}catch(e){}

function killMachines(kind){
  for(var i=MACHINES.length-1;i>=0;i--){
    if(MACHINES[i].kind===kind){
      try{ MACHINES[i].cleanup(); }catch(e){}
      try{ if(MACHINES[i].group.parent) MACHINES[i].group.parent.remove(MACHINES[i].group); }catch(e){}
      unregisterHits(MACHINES[i].group);
      MACHINES.splice(i,1);
    }
  }
}

/* ---------------- hit registry (our own drag/tap) ---------------- */
var HITREG=[]; /* {mesh, onDown,onMove,onUp,onTap, machine} */
function regHit(mesh,cbs){
  HITREG.push({mesh:mesh,onDown:cbs.onDown,onMove:cbs.onMove,onUp:cbs.onUp,onTap:cbs.onTap});
}
function unregisterHits(group){
  for(var i=HITREG.length-1;i>=0;i--){
    var o=HITREG[i].mesh, p=o;
    while(p){ if(p===group){ HITREG.splice(i,1); break; } p=p.parent; }
  }
}
function pickHit(cx,cy){
  try{
    if(typeof camera==='undefined') return null;
    setNDC(cx,cy); raycaster.setFromCamera(ndc,camera);
    var meshes=[]; for(var i=0;i<HITREG.length;i++) meshes.push(HITREG[i].mesh);
    var hits=raycaster.intersectObjects(meshes,false);
    if(!hits.length) return null;
    for(var j=0;j<hits.length;j++){
      for(var k=0;k<HITREG.length;k++) if(HITREG[k].mesh===hits[j].object) return HITREG[k];
    }
  }catch(e){}
  return null;
}

/* ---------------- drag helpers ---------------- */
function vPlaneY(anchorW,cx,cy){
  /* world Y of the touch on a vertical plane through anchorW facing the camera */
  try{
    setNDC(cx,cy); raycaster.setFromCamera(ndc,camera);
    var n=new T.Vector3().subVectors(camera.position,anchorW); n.y=0;
    if(n.lengthSq()<1e-6) n.set(0,0,1); n.normalize();
    var plane=new T.Plane().setFromNormalAndCoplanarPoint(n,anchorW);
    var pt=new T.Vector3();
    if(!raycaster.ray.intersectPlane(plane,pt)) return null;
    return pt.y;
  }catch(e){ return null; }
}
function screenAngle(worldPt,cx,cy){
  try{
    var p=worldPt.clone().project(camera);
    var sx=(p.x*0.5+0.5)*window.innerWidth, sy=(-p.y*0.5+0.5)*window.innerHeight;
    return Math.atan2(cy-sy,cx-sx);
  }catch(e){ return 0; }
}
function wrapPi(a){ return Math.atan2(Math.sin(a),Math.cos(a)); }

/* ---------------- touch wiring (coexists with rigging.js) ---------------- */
var gDrag=null;
function gDown(id,x,y){
  try{
    var h=pickHit(x,y);
    if(!h) return false;
    gDrag={hit:h,id:id,sx:x,sy:y,moved:false};
    if(h.onDown) h.onDown(x,y);
    return true;
  }catch(e){ return false; }
}
function gMove(id,x,y){
  try{
    if(!gDrag||gDrag.id!==id) return false;
    var dx=x-gDrag.sx, dy=y-gDrag.sy;
    if(dx*dx+dy*dy>144) gDrag.moved=true;
    if(gDrag.hit.onMove) gDrag.