<!-- SAGA-STAGING · part 67 -->
= b[1];
    buttonWall.add(mesh);
    wallButtons.push(mesh);
  });
  
  /* Face the player */
  buttonWall.rotation.y = -camYaw + Math.PI;
  scene.add(buttonWall);
  say('button wall built — 56 buttons, tap to press');
}

/* Raycast tapping for wall buttons */
var raycaster = new THREE.Raycaster();
var tapPos = new THREE.Vector2();
function onWallTap(event){
  if(!buttonWall) return;
  var rect = renderer.domElement.getBoundingClientRect();
  tapPos.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
  tapPos.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
  raycaster.setFromCamera(tapPos, camera);
  var hits = raycaster.intersectObjects(wallButtons);
  if(hits.length > 0){
    var btn = hits[0].object;
    /* Press animation: scale down briefly */
    btn.scale.set(0.85, 0.85, 1);
    setTimeout(function(){ btn.scale.set(1, 1, 1); }, 150);
    /* Do the task */
    say('pressed: ' + btn.userData.buttonName);
    btn.userData.buttonFn();
  }
}
/* Attach tap listener */
if(typeof renderer !== 'undefined' && renderer.domElement){
  renderer.domElement.addEventListener('click', onWallTap);
}

/* ===== PRACTICE DISTRICT ===== */
registerDistrict('practice', 'Practice District', function(){
  var CX=600, CZ=600, PHI=1.6180339887;
  var entry=new THREE.Mesh(new THREE.CircleGeometry(20,24), goldMat);
  entry.rotation.x=-Math.PI/2; entry.position.set(CX,0.3,CZ+60); scene.add(entry);
  var green=new THREE.Mesh(new THREE.CircleGeometry(30,24),
    new THREE.MeshStandardMaterial({color:0x2d6a2d,roughness:0.9}));
  green.rotation.x=-Math.PI/2; green.position.set(CX,0.3,CZ); scene.add(green);
  var pad=new THREE.Mesh(new THREE.CircleGeometry(25,24), goldMat);
  pad.rotation.x=-Math.PI/2; pad.position.set(CX,0.3,CZ-62); scene.add(pad);
  var r1=new THREE.Mesh(new THREE.BoxGeometry(6,0.5,40), roadMat);
  r1.position.set(CX,0.5,CZ+30); scene.add(r1);
  var r2=new THREE.Mesh(new THREE.BoxGeometry(6,0.5,37), roadMat);
  r2.position.set(CX,0.5,CZ-31); scene.add(r2);
  var tg=new THREE.MeshStandardMaterial({color:0xffd700,emissive:0xffd700,emissiveIntensity:0.7,roughness:0.3,metalness:0.6});
  var pw=16,ph=26,pyy=0;
  for(var i=0;i<4;i++){
    var t=new THREE.Mesh(new THREE.BoxGeometry(pw,ph,pw), tg);
    t.position.set(CX,pyy+ph/2,CZ-62); window.applyMinimum(t); scene.add(t);
    pyy+=ph; pw/=PHI; ph/=PHI;
  }
  for(var p=0;p<6;p++){
    var a=(p/6)*Math.PI*2;
    var post=new THREE.Mesh(new THREE.CylinderGeometry(0.8,1,12,8), goldMat);
    post.position.set(CX+Math.cos(a)*10,6,CZ+Math.sin(a)*10);
    window.applyMinimum(post); scene.add(post);
  }
  var roof=new THREE.Mesh(new THREE.ConeGeometry(14,8,6), goldMat);
  roof.position.set(CX,16,CZ); window.applyMinimum(roof); scene.add(roof);
});
dOrder.push('practice');

/* ===== LOOP ===== */

/* 1. AUTO-SWIM — water zones, auto-enter/exit like Genshin */
var waterBodies = [
  {x: 100, z: 100, r: 30, surfaceY: 0.5},
  {x: -80, z: 60, r: 25, surfaceY: 0.5}
];
function waterAt(x, z){
  for(var i=0; i<waterBodies.length; i++){
    var w = waterBodies[i];
    var dx = x - w.x, dz = z - w.z;
    if(dx*dx + dz*dz < w.r*w.r) return w;
  }
  return null;
}

  var animPhase = {walk:0, swim:0};
  var animAmp = {walk:0, swim:0};
function animate(){
  requestAnimationFrame(animate);
  /* body movement — left stick rules here */
  /* Body follows kingPos — STATE-GATED animations with BLEND */
  if(typeof playerBody !== 'undefined' && playerBody){
    playerBody.position.set(kingPos.x, kingPos.y, kingPos.z);
    /* 2. FACE TRAVEL DIRECTION */
    var mdx = lastDX, mdz = lastDZ;
    if(Math.abs(mdx) > 0.01 || Math.abs(mdz) > 0.01){
      var targetYaw = Math.atan2(mdx, mdz);
      var dyaw = targetYaw - playerBody.rotation.y;
      dyaw = Math.atan2(Math.sin(dyaw), Math.cos(dyaw));
      playerBody.rotation.y += dyaw * 0.15;
    }
    /* 6. SEPARATE clocks, 7. BLEND amplitudes, 4. STATE-GATED */
    var moving = Math.abs(lastDX) > 0.1 || Math.abs(lastDZ) > 0.1;
    var tWalk = (moveMode==='walk' && moving) ? 0.5 : 0;
    var tSwim = (moveMode==='swim') ? 0.8 : 0;
    animAmp.walk += (tWalk - animAmp.walk) * 0.15;
    animAmp.swim += (tSwim - animAmp.swim) * 0.15;
    if(moveMode==='walk' && moving){
      animPhase.walk += 0.15;
      if(playerBody.userData.legL) playerBody.userData.legL.rotation.x = Math.sin(animPhase.walk) * animAmp.walk;
      if(playerBody.userData.legR) playerBody.userData.legR.rotation.x = -Math.sin(animPhase.walk) * animAmp.walk;
      if(playerBody.userData.armL) playerBody.userData.armL.rotation.x = -Math.sin(animPhase.walk) * animAmp.walk * 0.8;
      if(playerBody.userData.armR) playerBody.userData.armR.rotation.x = Math.sin(animPhase.walk) * animAmp.walk * 0.8;
      playerBody.rotation.x = 0;
    } else if(moveMode==='swim'){
      animPhase.swim += 0.1;
      if(playerBody.userData.armL) playerBody.userData.armL.rotation.x = Math.sin(animPhase.swim) * animAmp.swim;
      if(playerBody.userData.armR) playerBody.use