<!-- SAGA · part 60 -->
kingPos.x, kingPos.y + 2, kingPos.z);
}
var lTX=0, lTY=0, touching=false;
var cameraMode = false; /* Camera toggle controls this */
var pinchD = 0;
canvas.addEventListener('touchstart', function(e){
  touching=true; lTX=e.touches[0].clientX; lTY=e.touches[0].clientY;
  if(e.touches.length === 2){
    pinchD = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
  }
}, {passive:true});
canvas.addEventListener('touchmove', function(e){
  if(!touching) return;
  /* Pinch: two fingers zoom the camera in/out */
  if(e.touches.length === 2){
    var nd = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
    if(pinchD > 0 && nd > 0){ camDist = Math.max(8, Math.min(150, camDist * (pinchD / nd))); }
    pinchD = nd;
    return;
  }
  /* Camera drag always live (right joystick also works) */
  camYaw -= (e.touches[0].clientX - lTX) * 0.005;
  camPitch = Math.max(0.1, Math.min(1.4, camPitch + (e.touches[0].clientY - lTY) * 0.005));
  lTX=e.touches[0].clientX; lTY=e.touches[0].clientY;
}, {passive:true});
canvas.addEventListener('touchend', function(){ touching=false; }, {passive:true});

/* ===== STATUS ===== */
var statusEl = document.getElementById('status'), sT=null;
function say(m){
  statusEl.textContent = m; statusEl.classList.add('show');
  if(sT) clearTimeout(sT);
  sT = setTimeout(function(){ statusEl.classList.remove('show'); }, 3000);
}

/* ===== THE OS ===== */
var osEl = document.getElementById('os');
var osToggle = document.getElementById('os-toggle');
var osOpen = false;

osToggle.onclick = function(){ osOpen=!osOpen; osEl.classList.toggle('open', osOpen);; };
function osButton(emoji, title, fn){
  var b = document.createElement('button');
  b.innerHTML = emoji; b.title = title;
  b.onclick = function(){ fn(); osOpen=false; osEl.classList.remove('open');; };
  osEl.appendChild(b);
  return b;
}

/* ===== DISTRICTS ===== */
var districts = {};
function registerDistrict(n, l, fn){ districts[n] = {label:l, build:fn, loaded:false}; }
function activateDistrict(n){
  var d = districts[n];
  if(!d) return say('unknown');
  if(d.loaded) return say(d.label + ' already stands');
  try { d.build(); d.loaded=true; say(d.label + ' rises'); }
  catch(e){ say('stumbled'); }
}
var dOrder=[], dIdx=0;
function nextDistrict(){
  for(var i=0;i<dOrder.length;i++){
    var n=dOrder[(dIdx+i)%dOrder.length];
    if(districts[n] && !districts[n].loaded){
      activateDistrict(n); dIdx=(dIdx+i+1)%dOrder.length; return;
    }
  }
  say('all districts stand');
}

/* ================================================================
   THE CODES — organized by category, proper
   ================================================================ */

/* ---- SPECIAL: the 12 ---- */
var lastBam = null;

function doBam(args){
  var shape = args[0] || 'box';
  var size = parseFloat(args[1]) || 10;
  var count = 1, row = 0;

  /* bam box 10 x5 — batch */
  for(var i=0;i<args.length;i++){
    if(args[i][0]==='x') count = parseInt(args[i].slice(1)) || 1;
    if(args[i]==='line') row = parseInt(args[i+1]) || 0;
  }

  lastBam = {shape:shape, size:size, count:count, row:row};

  for(var c=0;c<count;c++){
    var mesh;
    if(shape==='box') mesh = new THREE.Mesh(new THREE.BoxGeometry(size,size,size), goldMat);
    else if(shape==='wall'){
      var len = row || 5;
      mesh = new THREE.Mesh(new THREE.BoxGeometry(size*len, size, 1), goldMat);
    }
    else mesh = new THREE.Mesh(new THREE.BoxGeometry(size,size,size), goldMat);

    var ox = row ? c * size * 1.2 : c * size * 1.5;
    mesh.position.set(kingPos.x + ox, groundY + size/2, kingPos.z);
    window.applyMinimum(mesh);
    scene.add(mesh);
  }
  say(shape + ' stands' + (count>1 ? ' x'+count : ''));
}


/* THE EPISTOLARY — the King's proper body: hyper-realistic cyborg superhero */
function spawnNewBody(){
  var g = new THREE.Group();
  var cyberSkin = new THREE.MeshStandardMaterial({color:0x2a2a35, roughness:0.35, metalness:0.85});
  var gold = new THREE.MeshStandardMaterial({color:0xd8a920, roughness:0.3, metalness:0.8});
  var cyanGlow = new THREE.MeshStandardMaterial({color:0x00ccff, roughness:0.2, metalness:0.5, emissive:0x00aacc, emissiveIntensity:1.2});
  var capeMat = new THREE.MeshStandardMaterial({color:0x8b0000, roughness:0.8, metalness:0.1, side:THREE.DoubleSide});
  /* Head (handsome, heroic proportions) */
  var head = new THREE.Mesh(new THREE.SphereGeometry(0.55, 24, 24), cyberSkin);
  head.position.y = 2.35; head.scale.set(0.9, 1.1, 0.95);
  g.add(head);
  /* Cyan circuit lines on face */
  var faceLine = new THREE.Mesh(new THREE.TorusGeometry(0.4, 0.02, 8, 24), cyanGlow);
  faceLine.position.set(0, 2.4, 0.35); faceLine.rotation.x = 0.3;
  g.add(faceLine);
  /* Glowing eyes */
  var eyeL = new THREE.Mesh(new THREE.SphereGeometry(0.08, 8, 8), cyanGlow);
  eyeL.position.set(-0.18, 2.45, 0.45); g.add(eyeL);
  var eyeR = new THREE.Mesh(new THREE.Spher