<!-- RUBY-BACKUP · part 143 -->
('readonly');
  inp.focus();
}
function kbReset(){
  var inp = document.getElementById('chatinput'), kb = document.getElementById('kb');
  KB.sys = false; KB.page = 'abc'; KB.shift = false;
  inp.setAttribute('readonly','readonly');
  kb.style.display = '';
  paintKB();
}
kbReset(); /* after KB exists — never before */
/* ---- the code writes over the view — no box, shrinks when idle ---- */
var codeOpen = false, codeIdleT = null;
function codeTextShow(){
  var el = document.getElementById('codetext'), code = document.getElementById('codetext-code');
  if(!el || !code) return;
  code.innerHTML = codeInput.value ? escHtml(codeInput.value) : '<span class="dim">a line of code</span>';
  el.classList.add('show'); el.classList.remove('idle');
  clearTimeout(codeIdleT);
  codeIdleT = setTimeout(function(){ el.classList.add('idle'); }, 10000);
}
function codeTextHide(){
  var el = document.getElementById('codetext');
  if(el) el.classList.remove('show');
  clearTimeout(codeIdleT);
}

/* ---- room chat: talk without leaving ---- */
(function(){
  var cb = document.getElementById('chatbtn');
  if(!cb) return;
  kbReset();
  cb.addEventListener('click', function(){
    chatOpen = !chatOpen;
    if(chatOpen){
      kbUse('chat'); pullChat();
      codeOpen = false; codeTextHide();
      cursorDot.visible=false;
    }
    syncDock();
  });
  (function(){ var _cs=document.getElementById('chatsend'); if(_cs) _cs.addEventListener('click', function(){ sendChat(); }); })();
  var cinput = document.getElementById('chatinput');
  cinput.addEventListener('keydown', function(e){ if(e.key==='Enter') sendChat(); });
  cinput.addEventListener('focus', function(){ document.body.classList.add('chatting'); });
  cinput.addEventListener('blur', function(){ document.body.classList.remove('chatting'); });
  /* ---- go: a collapsed place for a link, just in case ---- */
  var gb = document.getElementById('gobtn'), gr = document.getElementById('gorow'), gi = document.getElementById('goinput');
  function goLink(){
    if(!gi) return;
    var v = gi.value.trim();
    if(!v) return;
    var url;
    if(/^[a-z0-9-]{1,32}$/i.test(v)) url = '/ruby/?watch='+encodeURIComponent(v.toLowerCase());
    else if(/^https?:\/\//i.test(v)) url = v;
    else if(v.charAt(0)==='/') url = v;
    else url = 'https://'+v;
    location.href = url;
  }
  if(gb) gb.addEventListener('click', function(){
    var open = gr ? gr.classList.toggle('open') : false;
    if(gi){ if(open) gi.focus(); else gi.blur(); }
  });
  (function(){ var _gs=document.getElementById('gosend'); if(_gs) _gs.addEventListener('click', goLink); })();
  if(gi){ gi.addEventListener('keydown', function(e){ if(e.key==='Enter') goLink(); });
  gi.addEventListener('focus', function(){ document.body.classList.add('chatting'); });
  gi.addEventListener('blur', function(){ document.body.classList.remove('chatting'); }); }

/* ---- our keyboard: compact, gold, loaded from the website we made ---- */
})();
(function(){ var f=document.getElementById('flybtn'); if(f) f.addEventListener('click', function(){ if(!WALK) setWalk(true); setFly(!FLYM); }); })();
(function(){ var j=document.getElementById('jumpbtn'); if(j) j.addEventListener('pointerdown', function(e){ e.preventDefault(); jumpQ = true; }); })();
(function(){
  /* UPGRADED MOVE STICK - touch-only, military/NASA/drone spec */
  var joy = document.getElementById('joy'), knob = document.getElementById('joyknob');
  if(!joy || !knob) return; /* null guard */
  var _tid = null, _rect = null;
  var DEAD = 0.12, EXPO = 0.25, MAXR = 40;
  function applyExpo(v){ return v * (1 - EXPO) + Math.pow(v, 3) * EXPO; }
  function setKnob(dx, dy){
    knob.style.transform = 'translate('+dx+'px,'+dy+'px)';
    var nx = dx / MAXR, ny = -dy / MAXR;
    var mag = Math.hypot(nx, ny);
    if(mag < DEAD){ joyX = 0; joyY = 0; return; }
    var ex = applyExpo(nx), ey = applyExpo(ny);
    joyX = ex; joyY = ey;
  }
  joy.addEventListener('touchstart', function(e){
    e.preventDefault();
    var t = e.changedTouches[0];
    _tid = t.identifier;
    _rect = joy.getBoundingClientRect(); /* cache */
    setKnob(0, 0);
  }, {passive:false});
  joy.addEventListener('touchmove', function(e){
    e.preventDefault();
    for(var i = 0; i < e.changedTouches.length; i++){
      var t = e.changedTouches[i];
      if(t.identifier !== _tid) continue;
      var dx = t.clientX - (_rect.left + _rect.width/2);
      var dy = t.clientY - (_rect.top + _rect.height/2);
      var m = Math.hypot(dx, dy);
      if(m > MAXR){ dx *= MAXR/m; dy *= MAXR/m; }
      setKnob(dx, dy);
    }
  }, {passive:false});
  function _tend(e){
    for(var i = 0; i < e.changedTouches.length; i++){
      if(e.changedTouches[i].identifier === _tid){ _tid = null; _rect = null; setKnob(0,0); }
    }
  }
  joy.addEventListener('touchend', _tend);
  joy.addEventListener('touchcancel', _tend);
})();

/* ---- render options: one button opens them all ---- */
var renderBtn = document.getElem