<!-- RUBY-BACKUP · part 73 -->
(); return; }
  if(act==='setdown'){ try{closeMenu();}catch(e){} if(typeof doSetdown==='function') doSetdown(); return; }
  if(act==='detach'){ try{closeMenu();}catch(e){} if(typeof armDetach==='function') armDetach(); return; }
  if(act==='unrig'){ try{closeMenu();}catch(e){} if(typeof clearRigs==='function') clearRigs(); return; }
  var mt=menuTarget, tg=mt?mt.tg:null, pw=mt?mt.pointW.clone():null;
  closeMenu();
  if(!tg||!pw) return;
  try{
    if(act==='grab'){ startCarry(tg,pw); }
    else if(act==='pulleyhere'){ placePulley(worldPosOf(targetBody(tg)).clone()); }
    else if(act==='rigfrom'){
      if(tg.kind==='pulley'&&tg.pulley){
        rigChain=[tg.pulley]; rigArmed=true;
        status('pulley ready — tap the road, bridge, or block');
      }
    }
    else if(act==='rigthis'){
      rigTargetPre=tg; rigTargetPrePoint=pw; rigChain=[]; rigArmed=true;
      status('tap a pulley — this block is ready to rope');
    }
    else if(act==='anchor'){ addAnchor(tg,pw); }
    else if(act==='unanchor'){ removeAnchor(tg); }
    else if(act==='diamond'){
      if(tg.kind==='frame'){
        if(!getAnchor(tg)) addAnchor(tg,pw);
        status('anchored — pull the gold rings to make the diamond');
      }else status('only frames reshape — this one is solid');
    }
    else if(act==='delete'){ menuDelete(tg); }
    else if(act==='duplicate'){ menuDuplicate(tg); }
  }catch(e){ status('menu: '+e.message); }
}
function menuDelete(tg){
  try{
    if(tg.kind==='pulley'&&tg.pulley){
      var P=tg.pulley;
      for(var i=rigs.length-1;i>=0;i--){
        var r=rigs[i], uses=false;
        for(var j=0;j<r.chain.length;j++) if(r.chain[j]===P) uses=true;
        if(uses){
          for(var k=0;k<r.ropes.length;k++) if(r.ropes[k].parent) r.ropes[k].parent.remove(r.ropes[k]);
          if(r.handle.group.parent) r.handle.group.parent.remove(r.handle.group);
          rigs.splice(i,1);
        }
      }
      if(P.group.parent) P.group.parent.remove(P.group);
      for(var m=pulleys.length-1;m>=0;m--) if(pulleys[m]===P) pulleys.splice(m,1);
      status('pulley removed');
      return;
    }
    var root=rootOf(targetBody(tg));
    if(root){
      if(root.parent) root.parent.remove(root);
      try{ if(typeof save==='function') save(); }catch(e){}
      try{ if(typeof refreshAllRopes==='function') refreshAllRopes(); }catch(e){}
      status('deleted');
    }
  }catch(e){ status('delete: '+e.message); }
}
function menuDuplicate(tg){
  try{
    if(tg.kind==='pulley'&&tg.pulley){
      var P0=tg.pulley, g0=P0.group.clone(true);
      g0.position.x+=3;
      scene.add(g0);
      var wheel=null;
      g0.traverse(function(o){ if(o.isMesh&&o.geometry&&o.geometry.type==='TorusGeometry'&&!wheel) wheel=o; });
      var P1={group:g0, wheel:wheel||g0.children[0], pos:g0.position.clone()};
      g0.userData.rigPulley=true; g0.userData.pulleyRef=P1;
      pulleys.push(P1);
      status('pulley duplicated');
      return;
    }
    var root=rootOf(targetBody(tg));
    if(root&&typeof scene!=='undefined'){
      var cp=root.clone(true);
      cp.position.x+=3;
      (root.parent||scene).add(cp);
      try{ if(typeof save==='function') save(); }catch(e){}
      status('duplicated');
    }
  }catch(e){ status('duplicate: '+e.message); }
}

/* ---------------- input: touch (thumb) + mouse (desktop) ---------------- */
function dDown(id,x,y){
  // returns true if consumed
  try{
    if(typeof scene==='undefined'||typeof camera==='undefined') return false;
    // 0. pulley menu: any canvas tap dismisses it
    if(menuOpen){ closeMenu(); return true; }
    // 1. rig handles pull any time
    if(!activePull){
      var rig=handleHit(x,y);
      if(rig){ activePull={rig:rig,id:id}; return true; }
    }
    // 2. pulley placement
    if(pulleyArmed){
      var hit=rayPick(x,y);
      placePulley(hit?hit.point.clone():forwardPoint(8,3));
      pulleyArmed=false;
      return true;
    }
    // 3. rig sequence: pulley taps, then target tap
    if(rigArmed){
      var P=pulleyHit(x,y);
      if(P){
        if(rigTargetPre){
          if(rigChain.indexOf(P)<0) rigChain.push(P);
          finalizeRig(rigTargetPre, rigTargetPrePoint?rigTargetPrePoint.clone():worldPosOf(targetBody(rigTargetPre)));
          rigArmed=false; rigChain=[]; rigTargetPre=null; rigTargetPrePoint=null;
          return true;
        }
        if(rigChain.indexOf(P)<0) rigChain.push(P);
        status(rigChain.length+' pulley(s) — tap another pulley, or tap the road, bridge, block');
        return true;
      }
      var hit2=rayPick(x,y);
      if(hit2){
        var tg=resolveTarget(hit2.object);
        if(tg&&tg.kind==='pulley'){ status('pulleys redirect — tap the road, bridge, or block'); }
        else if(tg&&rigChain.length){ finalizeRig(tg,hit2.point.clone()); rigArmed=false; rigChain=[]; }
        else status('tap a pulley first, then the road');
      }
      return true;
    }
    // 4. anchor
    if(anchorArmed){
      var hit3=rayPic