<!-- RUBY-BACKUP · part 142 -->
ement('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).catch(fallback);
  else fallback();
}
function kbCopy(btn){
  /* no auto-copy — only on tap, bottom-left, opposite the old iOS spot */
  var inp = document.getElementById(KB_TARGET==='chat' ? 'chatinput' : 'codeinput');
  var v = inp ? inp.value : '';
  if(!v) return;
  function tick(){
    if(!btn) return;
    var o = btn.innerHTML; btn.innerHTML = '&#10003;';
    setTimeout(function(){ btn.innerHTML = o; }, 900);
  }
  function fallback(){
    try{
      var ta = document.createElement('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 { if(!codeInput) return; 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