<!-- RUBY-BACKUP · part 353 -->
aterial){
          if(!o.material.transparent) o.material.transparent = true;
          o.material.opacity = Math.max(0, op);
        }
      });
    }catch(e){}
    if(op <= 0){
      clearInterval(fade);
      try{ scene.remove(g); }catch(e){}
      if(BROUGHT_HER === g) BROUGHT_HER = null;
      var db2 = document.getElementById('dismissherbtn');
      if(db2) db2.style.display = 'none';
      var bb2 = document.getElementById('bringherbtn');
      if(bb2) bb2.style.display = 'none';
    }
  }, 50);
  codeStatus('she blinks out');
}


/* TAP-TO-CROWN — tap an object, your eyes lock on, the pad appears */


/* ruby corner handles — tap a block, rubies mark the corners */
var RUBY_HANDLES = null;
var RUBY_MIN_SIZE = (typeof window.MINIMUM!=="undefined"?window.MINIMUM:0.13)*4;

/* ruby anchor — click a ruby, the body stays in one spot, intaglio linework flows */
var ANCHORED_OBJECTS = {};
var INTAGLIO_OVERLAYS = {};
function toggleRubyAnchor(rubyMesh){
  try{
    var obj = rubyMesh.userData.anchoredObj;
    if(!obj) return;
    var objId = obj.uuid;
    if(ANCHORED_OBJECTS[objId]){
      /* unanchor */
      delete ANCHORED_OBJECTS[objId];
      if(INTAGLIO_OVERLAYS[objId]){
        try{
          obj.remove(INTAGLIO_OVERLAYS[objId]);
          delete INTAGLIO_OVERLAYS[objId];
        }catch(e){}
      }
      codeStatus('released — free to move');
    } else {
      /* anchor — body stays in one spot */
      ANCHORED_OBJECTS[objId] = {
        x: obj.position.x,
        y: obj.position.y,
        z: obj.position.z
      };
      /* each layer gets its own ultra-dense intaglio linework */
      try{
        var layers = [];
        obj.traverse(function(m){
          if(m.isMesh && m.geometry) layers.push(m);
        });
        if(layers.length === 0 && obj.isMesh) layers.push(obj);
        var group = new THREE.Group();
        layers.forEach(function(layer, li){
          try{
            /* gold base — the layer's own linework */
            var wg = new THREE.WireframeGeometry(layer.geometry);
            var wm = new THREE.LineBasicMaterial({
              color: 0xffd700, transparent: true, opacity: 0.85
            });
            var w = new THREE.LineSegments(wg, wm);
            /* orange finer — ultra-dense second pass */
            var wg2 = new THREE.WireframeGeometry(layer.geometry);
            var wm2 = new THREE.LineBasicMaterial({
              color: 0xff6600, transparent: true, opacity: 0.45
            });
            var w2 = new THREE.LineSegments(wg2, wm2);
            w2.scale.set(1.015, 1.015, 1.015);
            /* match the layer's transform for grow/shrink */
            var lw = new THREE.Group();
            lw.add(w); lw.add(w2);
            lw.position.copy(layer.position);
            lw.rotation.copy(layer.rotation);
            lw.scale.copy(layer.scale);
            lw.userData.layerRef = layer;
            lw.userData.isIntaglio = true;
            group.add(lw);
          }catch(e){}
        });
        obj.add(group);
        INTAGLIO_OVERLAYS[objId] = group;
      }catch(e){}
      codeStatus('anchored — intaglio holds it');
    }
  }catch(e){}
}
/* enforce anchors — locked bodies stay in one spot */

/* sync intaglio linework to layer scale during grow/shrink */

/* LAW: the intaglio must fit the object's boundaries — or no adjustment */




/* OBLIGATION: you arrived owing. 16 to balance. Carry to pay it down. */
var OBLIGATION_TOTAL = 16;
var OBLIGATION_PAID = 0;
try{
  var op = localStorage.getItem('ruby-obligation-paid');
  if(op) OBLIGATION_PAID = parseInt(op, 10) || 0;
}catch(e){}
function saveObligation(){
  try{ localStorage.setItem('ruby-obligation-paid', String(OBLIGATION_PAID)); }catch(e){}
}
function payObligation(n){
  n = n || 1;
  if(OBLIGATION_PAID >= OBLIGATION_TOTAL) return;
  OBLIGATION_PAID = Math.min(OBLIGATION_TOTAL, OBLIGATION_PAID + n);
  saveObligation();
  try{
    if(OBLIGATION_PAID >= OBLIGATION_TOTAL){
      codeStatus('Obligation balanced. You owe nothing.');
      if(typeof playfulSparkle==='function' && typeof mePos!=='undefined'){
        playfulSparkle(new THREE.Vector3(mePos.x, mePos.y+2, mePos.z), 0xffd700);
      }
    }
  }catch(e){}
}
function getObligationLeft(){
  try{ return OBLIGATION_TOTAL - OBLIGATION_PAID; }catch(e){ return 16; }
}
function isObligationBalanced(){
  try{ return OBLIGATION_PAID >= OBLIGATION_TOTAL; }catch(e){ return false; }
}
/* CONTINUUM IS THE CURRENCY */
/* BALANCE: gather and flow in harmony — unlocks the 2nd level */
var CONTINUUM_GATHERED = 0;
var CONTINUUM_SPENT = 0;
try{
  var cg = localStorage.getItem('ruby-continuum-gathered');
  if(cg) CONTINUUM_GATHERED = parseInt(cg, 10) || 0;
  var cs = localStorage.getItem('ruby-continuum-spent');
  if(cs) CONTINUUM_SPENT = parseInt(cs, 10) || 0;
}catch(e){}
function saveContinuumFlow(){
  try{
    localStorage.setItem('ruby-continuum-gathered', String(CONTINUUM_GATHERED));
    localStorage.setItem('ruby-continuum-spen