<!-- RUBY-BACKUP · part 118 -->
;
      if(k < 1) requestAnimationFrame(spin);
    }
    requestAnimationFrame(spin);
  }catch(e){}
}
/* PROGRESSION: you start with 1 layer — the work earns deeper levels */
var PLAYER_LEVEL = 1;
var PLAYER_EDITS = 0;
try{
  var pl = localStorage.getItem('ruby-player-level');
  if(pl) PLAYER_LEVEL = parseInt(pl, 10) || 1;
  var pe = localStorage.getItem('ruby-player-edits');
  if(pe) PLAYER_EDITS = parseInt(pe, 10) || 0;
}catch(e){}
function saveProgress(){
  try{
    localStorage.setItem('ruby-player-level', String(PLAYER_LEVEL));
    localStorage.setItem('ruby-player-edits', String(PLAYER_EDITS));
  }catch(e){}
}
function recordEdit(){
  try{
    PLAYER_EDITS++;
    /* level 2 requires continuum balance — the work alone is not enough */
    var newLevel = PLAYER_LEVEL;
    if(PLAYER_LEVEL < 2){
      /* must earn via balance */
    } else {
      /* levels 3-6 via work */
      newLevel = Math.min(6, 2 + Math.floor(PLAYER_EDITS / 10));
    }
    if(newLevel > PLAYER_LEVEL){
      PLAYER_LEVEL = newLevel;
      try{ codeStatus('deeper level earned — ' + PLAYER_LEVEL + ' layers'); }catch(e){}
    }
    saveProgress();
  }catch(e){}
}
function layersAllowed(){
  return PLAYER_LEVEL;
}
/* LEVEL LAW: a 2-level block is not a 3-level — adjust only what it has */
function getObjectLevel(obj){
  try{
    if(!obj) return 3;
    var box = new THREE.Box3().setFromObject(obj);
    var size = new THREE.Vector3();
    box.getSize(size);
    var minDim = (typeof window.MINIMUM!=='undefined'?window.MINIMUM:0.13);
    var levels = 0;
    if(size.x > minDim*1.5) levels++;
    if(size.y > minDim*1.5) levels++;
    if(size.z > minDim*1.5) levels++;
    return Math.max(2, levels);
  }catch(e){ return 3; }
}
function levelAllowsAdjust(obj, dir){
  var lvl = getObjectLevel(obj);
  /* 2-level: only shrink in its own plane, never add a level */
  if(lvl === 2 && dir > 0) return false;
  return true;
}
function intaglioFits(obj){
  try{
    if(!obj || !obj.uuid) return false;
    var overlay = INTAGLIO_OVERLAYS[obj.uuid];
    if(!overlay) return false;
    /* each layer's linework must hug its boundaries */
    var fits = true;
    overlay.children.forEach(function(lw){
      try{
        var lr = lw.userData.layerRef;
        if(!lr) return;
        /* compare world bounding boxes */
        var layerBox = new THREE.Box3().setFromObject(lr);
        var lineBox = new THREE.Box3().setFromObject(lw);
        /* the bar is high on 2 levels — the fit must be exact */
        var eps = 0.05;
        try{
          if(typeof PLAYER_LEVEL!=='undefined' && PLAYER_LEVEL === 2) eps = 0.02;
        }catch(e){}
        if(Math.abs(layerBox.min.x - lineBox.min.x) > eps) fits = false;
        if(Math.abs(layerBox.min.y - lineBox.min.y) > eps) fits = false;
        if(Math.abs(layerBox.min.z - lineBox.min.z) > eps) fits = false;
        if(Math.abs(layerBox.max.x - lineBox.max.x) > eps) fits = false;
        if(Math.abs(layerBox.max.y - lineBox.max.y) > eps) fits = false;
        if(Math.abs(layerBox.max.z - lineBox.max.z) > eps) fits = false;
      }catch(e){ fits = false; }
    });
    return fits;
  }catch(e){ return false; }
}
function canAdjust(obj){
  if(!intaglioFits(obj)){
    try{ codeStatus('intaglio must fit the boundaries first'); }catch(e){}
    return false;
  }
  return true;
}
function syncIntaglioToScale(obj){
  try{
    var overlay = INTAGLIO_OVERLAYS[obj.uuid];
    if(!overlay) return;
    var allowed = (typeof layersAllowed==='function' ? layersAllowed() : 1);
    overlay.children.forEach(function(lw, idx){
      try{
        /* only your earned layers adjust */
        if(idx >= allowed) return;
        var lr = lw.userData.layerRef;
        if(lr){
          lw.scale.copy(lr.scale);
        }
      }catch(e){}
    });
  }catch(e){}
}
function enforceAnchors(){
  try{
    for(var id in ANCHORED_OBJECTS){
      var a = ANCHORED_OBJECTS[id];
      /* find the object by uuid */
      scene.traverse(function(o){
        if(o.uuid === id){
          o.position.x = a.x;
          o.position.y = a.y;
          o.position.z = a.z;
          /* sync each layer's intaglio to its grow/shrink */
          var overlay = INTAGLIO_OVERLAYS[id];
          if(overlay){
            overlay.children.forEach(function(lw){
              try{
                var lr = lw.userData.layerRef;
                if(lr){
                  lw.position.copy(lr.position);
                  lw.rotation.copy(lr.rotation);
                  lw.scale.copy(lr.scale);
                }
              }catch(e){}
            });
          }
        }
      });
    }
  }catch(e){}
}
setInterval(function(){ try{ enforceAnchors(); }catch(e){} }, 100);
function showRubyHandles(obj){
  try{
    clearRubyHandles();
    if(!obj || !obj.isMesh) return;
    /* measure the block */
    var box = new THREE.Box3().setFromObject(obj);
    var size = new THREE.Vector3();
    box.getSize(size);
    var minDim = Math.min(size.x, size.y, size.z);
