<!-- RUBY-BACKUP · part 128 -->
k) document.body.removeChild(window._blockLink);
                  window._blockLink = document.createElement('button');
                  window._blockLink.textContent = '🔧';
                  window._blockLink.style.cssText = 'position:fixed;left:50%;top:40%;transform:translate(-50%,-50%);z-index:20003;'
                    + 'width:56px;height:56px;font-size:28px;background:rgba(0,0,0,0.9);'
                    + 'border:2px solid #ffd700;border-radius:50%;cursor:pointer;';
                  window._blockLink.onclick = function(){
                    document.body.removeChild(window._blockLink); window._blockLink = null;
                    if(typeof warpFixStation==='function') warpFixStation();
                  };
                  document.body.appendChild(window._blockLink);
                  // auto-hide after 5 seconds
                  setTimeout(function(){
                    try{ if(window._blockLink){ document.body.removeChild(window._blockLink); window._blockLink = null; } }catch(e){}
                  }, 5000);
                }catch(e){}
              }catch(e){}
              if(typeof showRubyHandles!=='undefined') showRubyHandles(obj);
              if(typeof tapToCrown!=='undefined') tapToCrown(obj);
              else if(typeof showHandPad!=='undefined') showHandPad(obj);
            }
          }
        }
      }catch(err){}
    });
  }catch(e){}
}
setTimeout(function(){ try{ enablePartSelection(); }catch(e){} }, 6000);
function dissectProvolta(){
  if(typeof scene==='undefined') return;
  if(typeof THREE==='undefined' || typeof THREE.GLTFLoader==='undefined'){
    codeStatus('GLTFLoader not ready');
    return;
  }
  /* clear old dissection */
  if(DISSECT_GROUP){
    try{ scene.remove(DISSECT_GROUP); }catch(e){}
    DISSECT_GROUP = null;
  }
  /* build the table first */
  var tx = 0, tz = 0;
  try{ if(typeof myAvatar!=='undefined' && myAvatar){ tx=myAvatar.position.x+5; tz=myAvatar.position.z; } }catch(e){}
  buildWhiteTable(tx, tz);
  codeStatus('pulling her apart...');
  var loader = new THREE.GLTFLoader();
  loader.load('/ruby/warrior.glb?v=1',
    function(glb){
      DISSECT_GROUP = new THREE.Group();
      var pieces = [];
      glb.scene.traverse(function(obj){
        if(obj.isMesh){
          pieces.push(obj);
        }
      });
      /* lay them out in a grid on and around the table */
      var cols = 4;
      pieces.forEach(function(piece, i){
        /* clone the mesh so we keep original materials */
        var mesh = piece.clone();
        /* normalize size */
        var box = new THREE.Box3().setFromObject(mesh);
        var size = box.getSize(new THREE.Vector3());
        var maxDim = Math.max(size.x, size.y, size.z);
        var targetSize = 0.5;
        var s = targetSize / maxDim;
        /* reset and rescale */
        var holder = new THREE.Group();
        mesh.position.set(-(box.min.x+box.max.x)/2, -box.min.y, -(box.min.z+box.max.z)/2);
        holder.add(mesh);
        holder.scale.set(s, s, s);
        /* grid position */
        var col = i % cols;
        var row = Math.floor(i / cols);
        var px = tx + (col - 1.5) * 1.2;
        var pz = tz + (row - 1) * 1.2;
        /* alternate: on table (y=1.2) and floating above (y=2.0) */
        var py = (row % 2 === 0) ? 1.25 : 2.1;
        holder.position.set(px, py, pz);
        DISSECT_GROUP.add(holder);
        /* label */
        try{
          var name = piece.name || ('piece ' + (i+1));
          var cv = document.createElement('canvas');
          cv.width = 256; cv.height = 32;
          var ctx = cv.getContext('2d');
          ctx.font = '20px Georgia'; ctx.textAlign = 'center';
          ctx.fillStyle = '#ffd700'; ctx.fillText(name, 128, 24);
          var tex = new THREE.CanvasTexture(cv);
          var label = new THREE.Mesh(new THREE.PlaneGeometry(0.9, 0.11),
            new THREE.MeshBasicMaterial({map: tex, transparent: true}));
          label.position.set(px, py - 0.25, pz);
          DISSECT_GROUP.add(label);
        }catch(e){}
      });
      scene.add(DISSECT_GROUP);
      codeStatus('her ' + pieces.length + ' pieces, laid out — study them');
    },
    undefined,
    function(err){ codeStatus('could not load her to dissect'); }
  );
}

/* INVESTIGATE HER DIMENSIONS */

/* FIX HER PARTS — repair and reshape PROVOLTA piece by piece */
var PROVOLTA_MODEL = null;
var PROVOLTA_ORIGINALS = {};
function getProvoltaModel(cb){
  if(PROVOLTA_MODEL){ cb(PROVOLTA_MODEL); return; }
  if(typeof THREE==='undefined' || typeof THREE.GLTFLoader==='undefined'){ codeStatus('GLTFLoader not ready'); return; }
  var loader = new THREE.GLTFLoader();
  loader.load('/ruby/warrior.glb?v=1', function(glb){
    PROVOLTA_MODEL = glb.scene;
    /* save originals */
    PROVOLTA_MODEL.traverse(function(obj){
      if(obj.isMesh){
        var n = (obj.name || 'unnamed').toLowerCase();
        if(!PROVOLTA_ORIGINALS[n]){
          PROVOLTA_ORIGINALS[n] = {
            color: obj.m