<!-- LIVE · part 82 -->
.set((Math.random()-0.5)*100, Math.random()*60, (Math.random()-0.5)*100);
    p.userData.vy = 2 + Math.random()*2;
    g.add(p);
  }
  g.userData.update = function(){
    g.children.forEach(function(p){
      p.position.y -= p.userData.vy;
      if(p.position.y < 0) p.position.y = 60;
    });
  };
  scene.add(g);
  _atmosFx = g;
  if(typeof say === "function") say("Rain falls.");
}
/* Hook atmos updates into animate */
(function(){
  setInterval(function(){
    if(_atmosFx && _atmosFx.userData && _atmosFx.userData.update){
      _atmosFx.userData.update();
    }
  }, 50);
})();

/* ===== REAL VIEW — v101 ===== */
function saveView(){
  if(typeof kingPos === "undefined") return;
  var v = {x: kingPos.x, y: kingPos.y, z: kingPos.z, yaw: (typeof camYaw !== "undefined" ? camYaw : 0), pitch: (typeof camPitch !== "undefined" ? camPitch : 0)};
  localStorage.setItem("saga-view", JSON.stringify(v));
  if(typeof say === "function") say("View saved.");
}
function goToView(){
  var s = localStorage.getItem("saga-view");
  if(!s){ if(typeof say === "function") say("No saved view."); return; }
  var v = JSON.parse(s);
  if(typeof kingPos !== "undefined"){ kingPos.x = v.x; kingPos.y = v.y; kingPos.z = v.z; }
  if(typeof camYaw !== "undefined") camYaw = v.yaw;
  if(typeof camPitch !== "undefined") camPitch = v.pitch;
  if(typeof say === "function") say("To your saved view.");
}
function showPresets(){
  var presets = [["Cloud 9", 9, 8], ["Playa 2.0", 60, 0], ["Spawn", 500, 500]];
  var div = document.createElement("div");
  div.style.cssText = "position:fixed;top:30%;left:20%;right:20%;z-index:300;background:rgba(0,0,0,0.95);border:2px solid #d8a920;border-radius:12px;padding:20px;color:#ffd700;text-align:center;";
  div.innerHTML = "<h3>PRESETS</h3>" + presets.map(function(p, i){
    return "<button data-i='"+i+"' style='display:block;width:80%;margin:8px auto;padding:10px;background:#111;border:1px solid #d8a920;color:#ffd700;border-radius:8px;cursor:pointer;'>"+p[0]+"</button>";
  }).join("") + "<button id='presets-close' style='margin-top:10px;padding:8px 20px;background:#333;color:#ffd700;border:1px solid #d8a920;border-radius:8px;cursor:pointer;'>CLOSE</button>";
  document.body.appendChild(div);
  div.querySelectorAll("[data-i]").forEach(function(b){
    b.addEventListener("click", function(){
      var p = presets[parseInt(b.getAttribute("data-i"))];
      if(typeof kingPos !== "undefined"){ kingPos.x = p[1]; kingPos.z = p[2]; }
      if(typeof say === "function") say("To " + p[0]);
      div.remove();
    });
  });
  document.getElementById("presets-close").addEventListener("click", function(){ div.remove(); });
}
function crownView(){
  if(typeof kingPos !== "undefined" && typeof playerBody !== "undefined" && playerBody){
    kingPos.x = playerBody.position.x + 10;
    kingPos.z = playerBody.position.z + 10;
    if(typeof say === "function") say("Crown view.");
  }
}

/* ===== REAL BUILD — v102 ===== */
function fillGaps(){
  if(typeof runCode === "function"){ runCode("fillgaps"); }
  else if(typeof say === "function") say("Filling gaps.");
}
function recolorBlocks(){
  /* Cycle selected block through colors */
  if(typeof selected !== "undefined" && selected && selected.material){
    var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff, 0xd8a920];
    var cur = selected.material.color ? selected.material.color.getHex() : 0;
    var idx = colors.indexOf(cur);
    var next = colors[(idx + 1) % colors.length];
    if(selected.material.color) selected.material.color.setHex(next);
    if(typeof say === "function") say("Recolored.");
  } else {
    if(typeof say === "function") say("Tap a block first.");
  }
}

/* ===== REAL SOCIAL — v103 ===== */
function sendInvite(){
  var link = window.location.origin + "/saga-solid/";
  if(navigator.share){
    navigator.share({title: "Join my kingdom", url: link});
  } else {
    prompt("Share this link:", link);
  }
  if(typeof say === "function") say("Invite sent.");
}
function showPresence(){
  fetch("/api/chat/online").then(function(r){ return r.json(); }).then(function(d){
    var users = (d.users || []).map(function(u){ return u.username || u; }).join(", ");
    if(typeof say === "function") say(users ? ("Here: " + users) : "Just you.");
  }).catch(function(){ if(typeof say === "function") say("Just you."); });
}
function manageWaypoints(){
  if(typeof runCode === "function") runCode("wps");
  else if(typeof say === "function") say("Waypoints.");
}
function startTreasure(){
  if(typeof say === "function") say("Treasure hunt! Find the gold beacon.");
  /* Place a gold beacon at random location */
  if(typeof scene !== "undefined" && typeof THREE !== "undefined"){
    var g = new THREE.Mesh(new THREE.CylinderGeometry(0.5, 0.5, 30),
      new THREE.MeshBasicMaterial({color: 0xffd700, transparent: true, opacity: 0.6}));
    var x = (Math.random()-0.5)*200, z = (Math.random()-0.5)*200;
    g.position.set(x, 15, z);
    g.userData