<!-- RUBY-BACKUP · part 35 -->
                 if(c){
                    var rc = new THREE.Raycaster();
                    rc.setFromCamera(ndc, c);
                    var hits = rc.intersectObject(btn3d.disc, false);
                    if(hits && hits.length){
                      openScroll();
                      return true;
                    }
                  }
                }
                /* INSTALL: armed — tap a block to install the code */
                if(window.__installArmed && typeof rayPick==='function'){
                  var hit = rayPick(sx, sy);
                  if(hit && hit.object){
                    var nid = nodeIdOf(hit.object);
                    if(nid){
                      var ok = window.installCodeOn(nid, window.__installArmed.code);
                      var msg = ok
                        ? 'installed "'+window.__installArmed.code+'" — tap another block, or tap INSTALL to stop'
                        : 'already installed there';
                      try{ if(typeof codeStatus==='function') codeStatus(msg, true); }catch(e){}
                      // stay armed for multiple installs
                      return true;
                    }
                    try{ if(typeof codeStatus==='function') codeStatus('that is not a block — tap a block', true); }catch(e){}
                    return true;
                  }
                  return true;
                }
                /* UNINSTALL: armed — tap a block to strip its codes */
                if(window.__uninstallArmed && typeof rayPick==='function'){
                  var hit2 = rayPick(sx, sy);
                  if(hit2 && hit2.object){
                    var nid2 = nodeIdOf(hit2.object);
                    if(nid2){
                      window.uninstallCodesOn(nid2);
                      try{ if(typeof codeStatus==='function') codeStatus('codes removed', true); }catch(e){}
                      window.__uninstallArmed = false;
                      try{ if(typeof codeStatus==='function') setTimeout(function(){ codeStatus(''); }, 1500); }catch(e){}
                      return true;
                    }
                  }
                  return true;
                }
                /* INSTALLED BLOCK: tap shows its mini-scroll (before pulley menu) */
                if(typeof rayPick==='function' && !window.__installArmed && !window.__uninstallArmed){
                  try{
                    var hit3 = rayPick(sx, sy);
                    if(hit3 && hit3.object){
                      var nid3 = nodeIdOf(hit3.object);
                      if(nid3){
                        var ic = window.installedOn(nid3);
                        if(ic && ic.length){
                          openMini(nid3, ic, sx, sy);
                          return true;
                        }
                      }
                    }
                  }catch(e){}
                }
              }catch(e){}
              return orig.apply(this, arguments);
            };
            dDown.__pyrWrapped = true;
            clearInterval(iv);
          }
        }catch(e){}
        if(tries > 40) clearInterval(iv);
      }, 500);
    }catch(e){}
  }

  /* runLine: pyramid code opens the scroll */
  function hookRunLine(){
    try{
      var tries = 0;
      var iv = setInterval(function(){
        tries++;
        try{
          if(typeof window.runLine === 'function' && !window.runLine.__pyrHooked){
            var orig = window.runLine;
            window.runLine = function(line){
              try{
                var tl = String(line||'').trim().toLowerCase();
                if(tl==='pyramid' || tl==='codes' || tl==='scroll'){
                  openScroll();
                  return true;
                }
                if(tl==='uninstall'){
                  window.armUninstall();
                  return true;
                }
                if(tl==='cancel'){
                  window.cancelArm();
                  return true;
                }
                if(tl==='installed'){
                  var n = Object.keys(window.__installed||{}).length;
                  try{ if(typeof codeStatus==='function') codeStatus(n+' blocks carry codes'); }catch(e){}
                  return true;
                }
              }catch(e){}
              return orig.apply(this, arguments);
            };
            window.runLine.__pyrHooked = true;
            clearInterval(iv);
          }
        }catch(e){}
        if(tries > 40) clearInterval(iv);
      }, 500);
    }catch(e){}
  }

  /* Init: wait for scene, then build */
  function init(){
    makeUIButton();
    hookRunLine();
    hookTap();
    loadInstalled();
    // Restore sigils once nodes are stamped (poll)
    var st2 = 0;
    var iv2 = setInterval(function(){
      st2++;
      try{
        if(window.__nodeMap && Object.keys(window.__nodeMap).length){
          restoreSigils();
          clearInterval(iv2);
        }
      }catch(e){}
  