<!-- RUBY-BACKUP · part 390 -->
ateElement('textarea');
      ta.value = v; ta.style.position = 'fixed'; ta.style.opacity = '0';
      document.body.appendChild(ta); ta.select();
      document.execCommand('copy'); ta.remove();
    }catch(e){}
  }
  if(navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(v).then(tick, function(){ fallback(); tick(); });
  else { fallback(); tick(); }
}
function kbPaste(btn){
  /* paste — clipboard API first, iOS native long-press as fallback (no permission needed) */
  var inp = document.getElementById(KB_TARGET==='chat' ? 'chatinput' : 'codeinput');
  if(!inp) return;
  function tick(ok){
    if(!btn) return;
    var o = btn.innerHTML; btn.innerHTML = ok ? '&#10003;' : '&#10007;';
    setTimeout(function(){ btn.innerHTML = o; }, 900);
  }
  function pullCodeDrop(){
    /* Solid left a line of code in the room — one tap takes it */
    if(!WATCH_ID || KB_TARGET!=='code'){ nativePaste(); return; }
    fetch('/api/ruby/code/'+encodeURIComponent(WATCH_ID)).then(function(r){ return r.json(); }).then(function(d){
      if(d && d.text){ inp.value = (inp.value + d.text).slice(0, 500); codeTextShow(); tick(true); }
      else nativePaste();
    }).catch(nativePaste);
  }
  function nativePaste(){
    /* the chat line is visible — unlock it in place */
    if(KB_TARGET!=='code'){
      var ci = document.getElementById('chatinput');
      if(!ci) return;
      ci.removeAttribute('readonly');
      try{ ci.focus(); }catch(e){}
      tick(true);
      var relockC = function(){
        ci.setAttribute('readonly','readonly');
        ci.removeEventListener('input', relockC);
        ci.removeEventListener('blur', relockC);
        try{ ci.blur(); }catch(e){}
      };
      ci.addEventListener('input', relockC);
      ci.addEventListener('blur', relockC);
      return;
    }
    /* the code line is hidden — raise a real text box instead */
    var wrap = document.getElementById('pastewrap');
    if(!wrap){
      wrap = document.createElement('div');
      wrap.id = 'pastewrap';
      wrap.innerHTML = '<textarea id="pastebox" rows="4" placeholder="paste your code here"></textarea><button id="pastedone">DONE</button>';
      document.body.appendChild(wrap);
      document.getElementById('pastedone').onclick = function(){
        var pb = document.getElementById('pastebox');
        var v = pb ? pb.value : '';
        wrap.classList.remove('open');
        if(v){ inp.value = (inp.value + v).slice(0, 500); codeTextShow(); }
        try{ if(pb) pb.blur(); }catch(e){}
        if(pb) pb.value = '';
      };
    }
    wrap.classList.add('open');
    tick(true);
    var pb2 = document.getElementById('pastebox');
    try{ if(pb2) pb2.focus(); }catch(e){}
  }
  if(navigator.clipboard && navigator.clipboard.readText){
    navigator.clipboard.readText().then(function(t){
      if(t){ inp.value = (inp.value + t).slice(0, 500); if(KB_TARGET==='code') codeTextShow(); tick(true); }
      else nativePaste();
    }, pullCodeDrop);
  } else pullCodeDrop();
}
function kbTap(k, btn){
  /* quick-keys: tap a letter to run its code */
  if(QUICK_MODE && KB_TARGET==='code' && k.length===1 && QUICK_KEYS[k]){
    try{ Mainframe.run(QUICK_KEYS[k]); }catch(e){}
    codeStatus('quick: '+QUICK_KEYS[k]);
    return;
  }
  var inp = document.getElementById(KB_TARGET==='chat' ? 'chatinput' : 'codeinput');
  if(!inp) return;
  if(k==='cp'){ kbCopy(btn); return; }
  if(k==='ps'){ kbPaste(btn); return; }
  if(k==='bk') inp.value = inp.value.slice(0,-1);
  else if(k==='sh'){ KB.shift = !KB.shift; paintKB(); }
  else if(k==='pg'){ KB.page = KB.page==='abc' ? 'num' : 'abc'; KB.shift = false; paintKB(); }
  else if(k==='sp') inp.value += ' ';
  else if(k==='go'){
    if(KB_TARGET==='chat') sendChat();
    else { var v = codeInput.value; codeInput.value=''; if(v.trim()) Mainframe.run(v); }
  }
  else{
    inp.value += KB.shift ? k.toUpperCase() : k;
    if(KB.shift){ KB.shift = false; paintKB(); }
  }
  if(KB_TARGET==='code') codeTextShow();
}
function kbSystem(){
  var inp = document.getElementById('chatinput');
  KB.sys = true;
  document.getElementById('kb').style.display = 'none';
  inp.removeAttribute('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