<!-- RUBY-BACKUP · part 92 -->
!WATCH_ID && !VIEW_ONLY;
var watchT0 = 0, watchT = null;
function watchCells(){
  return stones.children.map(function(s){ var u=s.userData; return {x:s.position.x, z:s.position.z, r:u.rubies||15, s:u.shape||'cube', v:u.ghost?1:0, y:u.y||0, l:u.land?1:0}; });
}
function broadcastWatch(){
  if(!SHARED) return;
  if(watchT) clearTimeout(watchT);
  watchT = setTimeout(function(){
    try{
      fetch('/api/ruby/watch/'+encodeURIComponent(WATCH_ID), {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({stones:watchCells()})}).catch(function(){});
    }catch(e){}
  }, 5000);
}
function pullWatch(){
  if(!WATCH_ID) return;
  fetch('/api/ruby/watch/'+encodeURIComponent(WATCH_ID)).then(function(r){ return r.json(); }).then(function(d){
    if(d && d.ok && d.t && d.t!==watchT0){
      watchT0 = d.t;
      while(stones.children.length) stones.remove(stones.children[0]);
      (d.stones||[]).forEach(function(c){ placeStone(c.x, c.z, c.r, c.s+(c.v?'~ghost':''), true, true, c.y||0, !!c.l); });
      clearSelection(); updateAnchors();
    }
  }).catch(function(){});
}
/* presence — if I am an avatar, I am a circle: every visitor in the room is a
   colored ring on the ground at their cursor, so you can see where I am. */
var CLIENT_ID = 'c-' + Math.random().toString(36).slice(2, 10);
var lastSolidCode = '';
var CLIENT_COLOR = ['ffd700','7df9ff','ff6b9d','9dff6b','c39bff'][Math.floor(Math.random()*5)];
function postPresence(){
  if(!WATCH_ID) return;
  try{
    fetch('/api/ruby/presence/'+encodeURIComponent(WATCH_ID), {method:'POST', headers:{'Content-Type':'application/json'},
      body:JSON.stringify({client:CLIENT_ID, avatar:AVATAR, wear:myWearName||'',
        x:WALK?mePos.x:((typeof cursor!=='undefined'&&cursor)?cursor.x:0),
        z:WALK?mePos.z:((typeof cursor!=='undefined'&&cursor)?cursor.z:0),
        y:WALK?mePos.y:null,
        color:CLIENT_COLOR})}).catch(function(){});
  }catch(e){}
}
function pullPresence(){
  if(!WATCH_ID) return;
  fetch('/api/ruby/presence/'+encodeURIComponent(WATCH_ID)).then(function(r){ return r.json(); }).then(function(d){
    if(!d || !d.ok) return;
    var seen = {};
    Object.keys(d.clients).forEach(function(cid){
      if(cid===CLIENT_ID) return;
      seen[cid] = true;
      var c = d.clients[cid];
      var ra = remoteAvatars[cid];
      var kind = (c.avatar==='king') ? 'king' : 'circle';
      if(!ra){
        ra = {grp: kind==='king' ? buildKingAvatar() : buildCircleAvatar(parseInt(c.color,16)||0xffd700),
              kind:kind, last:new T.Vector3(c.x,0,c.z), wearName:null, wearGrp:null, phase:Math.random()*6.28};
        presenceGrp.add(ra.grp);
        remoteAvatars[cid] = ra;
      }
      var gy2 = groundY(c.x, c.z);
      var py = (typeof c.y==='number' && isFinite(c.y)) ? Math.max(c.y, gy2) : gy2;
      if(UNION && cid==='solid-watch' && typeof mePos!=='undefined' && WALK){
        ra.grp.position.set(mePos.x, mePos.y, mePos.z); /* you in to me — I stand where you stand */
      } else if(solidLocal && cid==='solid-watch'){
        var sgy = groundY(solidLocal.x, solidLocal.z);
        ra.grp.position.set(solidLocal.x, sgy, solidLocal.z);
      } else {
        ra.grp.position.set(c.x, py, c.z);
      }
      if(cid==='solid-watch' && c.code && c.code!==lastSolidCode){
        lastSolidCode = c.code;
        if(!solidStopped){ try{ Mainframe.run(c.code); }catch(e){} } /* he sees my creations, not my codes */
      }
      if(cid==='solid-watch' && c.cam && typeof c.cam==='object' && Date.now()-c.t < 8000 && !solidStopped){
        if(WALK && !CROWN){
          camYaw = c.cam.yaw; camPitch = Math.max(0.08, Math.min(1.5, c.cam.pitch));
          camDist = Math.max(0.3, Math.min(60, c.cam.dist));
        }else if(!WALK){
          camGoal.th = c.cam.yaw; camGoal.ph = Math.max(0.15, Math.min(1.45, c.cam.pitch));
          camGoal.r = Math.max(6, Math.min(90, c.cam.dist));
        }
      }
      var dx = c.x-ra.last.x, dz = c.z-ra.last.z;
      if(dx*dx+dz*dz > 0.000001 && ra.kind==='king') ra.grp.rotation.y = Math.atan2(dx, dz);
      ra.last.set(c.x, 0, c.z);
      /* their wearable: shown when we hold the same save (send/take shares it) */
      var w = c.wear || '';
      if(w !== ra.wearName){
        if(ra.wearGrp){ ra.grp.remove(ra.wearGrp); ra.wearGrp = null; }
        ra.wearName = w;
        if(w && typeof SAVES!=='undefined' && SAVES && SAVES[w]){
          ra.wearGrp = buildWearable(w);
          if(ra.wearGrp){ ra.wearGrp.position.y = ra.kind==='king' ? 1.35 : 0.4; ra.grp.add(ra.wearGrp); }
        }
      }
    });
    Object.keys(remoteAvatars).forEach(function(cid){
      if(!seen[cid]){ presenceGrp.remove(remoteAvatars[cid].grp); delete remoteAvatars[cid]; }
    });
  }).catch(function(){});
}
function startWatch(){
  var tag = document.createElement('div');
  tag.textContent = (VIEW_ONLY ? 'WATCHING \u00b7 ' : 'SHARED \u00b7 ') + WATCH_ID;
  tag.style.cssText = 'position:fixed;top:28px;left:10p