<!-- RUBY-BACKUP · part 23 -->
dow.__restoreNodes(saved);
        nSpans = restoreSpans(saved);
        status('world awake — ' + nNodes + ' nodes, ' + nSpans + ' spans restored');
      } else {
        nNodes = wireDefaults();
        try{ window.saveNodes(false); }catch(e){}
        status('world awake — ' + nNodes + ' significant blocks wired');
      }

      /* 5. verify runLine chains are hooked (all eager, but confirm) */
      var chains = [];
      try{
        if(window.runLine && window.runLine.__modeHooked) chains.push('nodes');
        if(window.runLine && window.runLine.__solidHooked) chains.push('solid');
        /* probe a code through the chain without side effects */
        if(typeof window.nodemodes === 'undefined'){ /* nodemodes is a code, not a fn — skip */ }
      }catch(e){}

      /* 6. autosave: every 20s + on unload (only after boot, never empty-over-good) */
      try{
        setInterval(function(){
          try{
            var sns = window.__solidNodes || {};
            if(Object.keys(sns).length) window.saveNodes(false);
          }catch(e){}
        }, 20000);
        window.addEventListener('beforeunload', function(){ try{ window.saveNodes(false); }catch(e){} });
      }catch(e){}

      try{ console.log('[autoload] boot complete — stamped=' + stamped + ' nodes=' + nNodes + ' spans=' + nSpans); }catch(e){}
    }catch(e){}
  }

  /* ---------- manual codes ---------- */
  (function(){
    var __triesal=0;
    function __hookal(){
      __triesal++;
      try{
        if(typeof window.runLine === 'function' && !window.runLine.__alHooked){
      var prevRL = window.runLine;
      window.runLine = function(line){
        try{
          var tl = String(line == null ? '' : line).trim().toLowerCase();
          if(tl === 'save nodes'){ window.saveNodes(true); return; }
          if(tl === 'forget nodes'){ window.forgetNodes(); return; }
          if(tl === 'autoload'){
            var d = window.loadNodesRaw();
            var msg = d
              ? ('autoload: ' + d.nodes.length + ' nodes, ' + d.portals.length + ' portals, ' +
                 (d.spans||[]).length + ' spans' + (d.network && d.network.connected ? ', network ON' : ''))
              : 'autoload: nothing saved yet — defaults wire on boot';
            status(msg);
            return;
          }
        }catch(e){}
        return prevRL.apply(this, arguments);
      };
          window.runLine.__alHooked=true;
          return;
        }
      }catch(e){}
      if(__triesal<60) setTimeout(__hookal,500);
    }
    __hookal();
  })();

  /* ---------- go: poll-and-wait ---------- */
  if(document.readyState === 'complete'){ poll(); }
  else{ window.addEventListener('load', poll); setTimeout(poll, 1500); }

  try{ console.log('[autoload] watching for map readiness'); }catch(e){}
})();


===== FILE: public/ruby-backup/code-builder.js =====
/* CODE BUILDER — the King builds new codes by tapping.
   "How about you build a code builder now."
   - `codebuilder` opens the panel: tap actions to chain them, name it, save it.
   - The chain becomes a real runnable code: typing it runs every action in order.
   - `mycodes` lists built codes. `forgetcode <name>` deletes one.
   - Stored in localStorage `ruby-custom-codes-v1`.
   Law: touch-only, gold aesthetic, no JavaScript typing ever. */
(function(){
  if(window.__codeBuilder) return;
  window.__codeBuilder = true;

  var STORE_KEY = 'ruby-custom-codes-v1';
  var PANEL_Z = 30005;

  /* ================= ACTION PALETTE =================
     args: {n:'label', t:'node'|'text'|'num'} — 'node' = tap-a-block capture */
  var SECTIONS = [
    {title:'NODES', ic:'\u25C6', acts:[
      {code:'solidify', args:[{n:'node',t:'node'}], why:'make the block solid'},
      {code:'solid',    args:[{n:'node',t:'node'}], why:'gold platform to stand on'},
      {code:'latch',    args:[{n:'node',t:'node'}], why:'grab things that come near'},
      {code:'portal',   args:[{n:'node A',t:'node'},{n:'node B',t:'node'}], why:'step through between two nodes'},
      {code:'switch',   args:[{n:'node',t:'node'}], why:'toggle it on / off'},
      {code:'sensor',   args:[{n:'node',t:'node'}], why:'watch and report movement'},
      {code:'spring',   args:[{n:'node',t:'node'}], why:'bounce pad'},
      {code:'anchor',   args:[{n:'node',t:'node'}], why:'deep-pin the block'},
      {code:'unanchor', args:[{n:'node',t:'node'}], why:'release the deep pin'},
      {code:'conveyor', args:[{n:'node',t:'node'}], why:'factory belt ride'},
      {code:'elevator', args:[{n:'node',t:'node'},{n:'height',t:'num'}], why:'rise to a height'},
      {code:'catapult', args:[{n:'node',t:'node'}], why:'arm it, then launch'},
      {code:'counter',  args:[{n:'node',t:'node'}], why:'count what crosses'},
      {code:'timer',    args:[{n:'node',t:'node'},{n:'seconds',t:'num'}], why:'count down, then chime'},
      {code:'messenger',args:[{n:'node',t:'node'},{n:'words',t:'text'}], why:'speak your words when tripped'},
      {