<!-- PLAYA · part 1 -->
===== FILE: public/playa/creator.js =====
/* THE CREATOR v1 — /playa. Standalone studio. Own three.js scene.
   Block shapes, no-overlap rule, align row, 0.13 minimum, Royal Court pillars:
   copied from /saga-solid/saga.js (makeBlock, wouldOverlap, autoAlign, applyMinimum, buildRoyalCourt).
   One delegated click handler for every button (data-act). One pointer handler for the viewport.
   Errors are shown in the status line, never swallowed. */
(function(){
'use strict';

/* ---------- STATUS (fail visibly) ---------- */
var statusEls = [document.getElementById('p-status'), document.getElementById('vp-status')];
function say(msg, err){
  statusEls.forEach(function(el){ if(!el) return; el.textContent = msg; el.classList.toggle('err', !!err); });
}
window.addEventListener('error', function(e){ say('⚠ ' + (e.message || 'error'), true); });
if(typeof THREE === 'undefined'){ say('⚠ three.js did not load — the studio cannot start', true); throw new Error('THREE missing'); }

/* ---------- THE LAW: Minimum 0.13 (from saga-solid) ---------- */
var MINIMUM = 0.13;
function applyMinimum(obj){
  var box = new THREE.Box3().setFromObject(obj);
  var size = new THREE.Vector3(); box.getSize(size);
  var s = Math.min(Math.abs(size.x), Math.abs(size.y), Math.abs(size.z));
  if(s > 0 && s < MINIMUM) obj.scale.multiplyScalar(MINIMUM / s);
  return obj;
}

/* ---------- SCENE ---------- */
var canvas = document.getElementById('cv');
var vp = document.getElementById('viewport');
var renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true, preserveDrawingBuffer: true});
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.localClippingEnabled = true;
renderer.outputEncoding = THREE.sRGBEncoding;
var scene = new THREE.Scene();
scene.background = new THREE.Color(0x07070c);
scene.fog = new THREE.Fog(0x07070c, 120, 420);
var camera = new THREE.PerspectiveCamera(55, 1, 0.1, 3000);
var hemi = new THREE.HemisphereLight(0xfff1d0, 0x2a2014, 0.8);
scene.add(hemi);
var sun = new THREE.DirectionalLight(0xffd9a0, 1.4);
sun.castShadow = true;
sun.shadow.mapSize.set(1024, 1024);
sun.shadow.camera.left = -60; sun.shadow.camera.right = 60; sun.shadow.camera.top = 60; sun.shadow.camera.bottom = -60;
sun.shadow.camera.near = 1; sun.shadow.camera.far = 300;
scene.add(sun); scene.add(sun.target);
var ground = new THREE.Mesh(new THREE.PlaneGeometry(800, 800), new THREE.MeshStandardMaterial({color: 0x2a2418, roughness: 0.95}));
ground.rotation.x = -Math.PI / 2; ground.receiveShadow = true;
scene.add(ground);
var grid = null, gridDiv = 100, gridColor = 0x8a6a1c;
function buildGrid(){
  if(grid){ scene.remove(grid); grid.geometry.dispose(); grid.material.dispose(); grid = null; }
  if(gridDiv > 0){
    grid = new THREE.GridHelper(400, gridDiv, gridColor, gridColor);
    grid.material.transparent = true; grid.material.opacity = 0.45;
    grid.position.y = 0.02;
    scene.add(grid);
  }
}
buildGrid();

/* Royal Court pillars — from saga-solid buildRoyalCourt. Decor only, not selectable. */
(function(){
  var goldMat = new THREE.MeshStandardMaterial({color: 0xd4a017, roughness: 0.3, metalness: 0.7});
  [{c:0xffd700,x:21,z:21},{c:0x00ffff,x:-21,z:21},{c:0xff6600,x:21,z:-21},{c:0x0088ff,x:-21,z:-21}].forEach(function(m){
    var pillar = new THREE.Mesh(new THREE.CylinderGeometry(1.5, 2, 12, 12), new THREE.MeshStandardMaterial({color: m.c, roughness: 0.4, metalness: 0.6}));
    pillar.position.set(m.x, 6.13, m.z); pillar.castShadow = true; scene.add(pillar);
    var cap = new THREE.Mesh(new THREE.SphereGeometry(2, 16, 12), goldMat); cap.position.set(m.x, 12.5, m.z); cap.castShadow = true; scene.add(cap);
    var base = new THREE.Mesh(new THREE.BoxGeometry(5, 0.5, 5), goldMat); base.position.set(m.x, 0.35, m.z); base.receiveShadow = true; scene.add(base);
  });
})();

/* ---------- ENVIRONMENT ---------- */
var ENV = {
  golden: {bg:0x1a1208, fog:0x1a1208, sky:0xffe2b0, gnd:0x2a1a0a, amb:0.75, sunC:0xffc070, sunI:1.6, az:235},
  day:    {bg:0x8fb8de, fog:0x8fb8de, sky:0xffffff, gnd:0x5a4a32, amb:1.0, sunC:0xffffff, sunI:1.5, az:160},
  night:  {bg:0x05070f, fog:0x05070f, sky:0x5a6a9a, gnd:0x0a0a12, amb:0.35, sunC:0x9ab0ff, sunI:0.5, az:300},
  studio: {bg:0x101014, fog:0x101014, sky:0xffffff, gnd:0x303036, amb:1.1, sunC:0xffffff, sunI:1.2, az:200}
};
var sunAz = 235, sunEl = 50;
function placeSun(){
  var a = sunAz * Math.PI / 180, e = sunEl * Math.PI / 180;
  sun.position.set(orbit.target.x + Math.cos(a) * Math.cos(e) * 120, Math.sin(e) * 120, orbit.target.z + Math.sin(a) * Math.cos(e) * 120);
  sun.target.position.copy(orbit.target);
}
function setEnv(k){
  var e = ENV[k];
  scene.background = new THREE.Color(e.bg);
  scene.fog.color.setHex(e.fog);
  hemi.color.setHex(e.sky); hemi.groundColor.setHex(e.gnd); hemi.intensity = e.amb;
  sun.color.setHex(e.sunC); sun.intensity = e.sunI; sunAz = e.az; placeSun();
  d