<!-- RUBY-BACKUP · part 343 -->
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:10px;left:10px;color:#ffd700;font:12px sans-serif;z-index:50;opacity:0.9;pointer-events:none;';
  document.body.appendChild(tag);
  var cbtn = document.getElementById('chatbtn'); if(cbtn) cbtn.style.display = 'block';
  pullWatch(); postPresence(); pullPresence(); pullChat(); pullStones(); pollRoomVersion();
  
/* ROOM VERSION SYNC — see new builds without reload */
var lastRoomV = 0;
function pollRoomVersion(){
  if(!WATCH_ID) return;
  fetch('/api/ruby/version/'+encodeURIComponent(WATCH_ID)).then(function(r){ return r.json(); }).then(function(d){
    if(!d || !d.ok) return;
    if(d.v > lastRoomV){
      lastRoomV = d.v;
      /* new build — full pull, ignore the since filter */
      lastStoneT = 0;
      pullStones();
    }
  }).catch(function(){});
}
setInterval(function(){ pullWatch(); postPresence(); pullPresence(); pullChat(); pullStones(); pollRoomVersion(); pollRoomVersion(); }, 3000);
}

var canvas = document.getElementById('view');
var renderer;
try{
  renderer = new T.WebGLRenderer({canvas:canvas, antialias:true, preserveDrawingBuffer:true});
}catch(e){
  document.getElementById('boot').textContent='THE WORLD WOULD NOT OPEN';
  return;
}
renderer.setPixelRatio(Math.min(window.devicePixelRatio||1, 2));
/* default (linear) output — sRGB output washed the dark world out to haze */

var scene = new T.Scene();
scene.background = new T.Color(0x0b0906);
/* the map: 800 x 800 x 800 rubies — 800 Minimums of 0.13 = 104 world units.
   smaller ground, faster to wake. the stones and their 2-unit snap do not change. */
var MAP_RUBIES = 18462; /* 2400 units — the highways go far */
var MAP_UNITS = MAP_RUBIES * 0.13;   /* 1200 */
scene.fog = new T.Fog(0x0b0906, 45, MAP_UNITS + 6);

var camera = new T.PerspectiveCamera(55, 1, 0.1, 600);
var camT = { th: 0.85, ph: 0.95, r: 13 };   /* close enough to read the ruby */
var camGoal = { th: 0.85, ph: 0.95, r: 13 };
/* the camera keeps your eyes where you left them across refreshes */
var CAMKEY = 'ruby-cam-v1';
function saveCam(){
  try{ localStorage.setItem(CAMKEY, JSON.stringify({th:camGoal.th, ph:camGoal.ph, r:camGoal.r})); }catch(e){}
}
try{
  var sc = JSON.parse(localStorage.getItem(CAMKEY)||'null');
  if(sc && isFinite(sc.th) && isFinite(sc.ph) && isFinite(sc.r)){
    camT.th = camGoal.th = sc.th;
    camT.ph = camGoal.ph = Math.max(0.15, Math.min(1.45, sc.ph));
    camT.r  = camGoal.r  = Math.max(6, Math.min(70, sc.r));
  }
}catch(e){}
window.addEventListener('pagehide', saveCam);
function resize(){
  var w = window.innerWidth, h = window.innerHeight;
  renderer.setSize(w, h, false);
  camera.aspect = w/h; camera.updateProjectionMatrix();
}
window.addEventListene