<!-- RUBY-BACKUP · part 470 -->
he east, separate from city and rural */
  var villages = [
    {x: px+200, z: pz-40, level: 2},
    {x: px+220, z: pz+20, level: 1},
    {x: px+190, z: pz+80, level: 2}
  ];
  villages.forEach(function(v){
    for(var h=0; h<5; h++){
      var ang = h*(Math.PI*2/5);
      var hx = v.x + Math.cos(ang)*8;
      var hz = v.z + Math.sin(ang)*8;
      var hut = buildTribalHut(hx, hz, v.level);
      scene.add(hut);
      TRIBAL_GROUP.push(hut);
    }
    /* central fire */
    var fire = new THREE.Mesh(new THREE.ConeGeometry(0.5, 1.0, 8),
      new THREE.MeshStandardMaterial({
        color: 0x00aaff, emissive: 0x00aaff, emissiveIntensity: 2,
        transparent: true, opacity: 0.8
      }));
    fire.position.set(v.x*0.13, 0.5, v.z*0.13);
    scene.add(fire);
    TRIBAL_GROUP.push(fire);
  });
  codeStatus('tribal villages — 3 villages, traditional huts with cyborg features. Separate.');
}
function clearZones(){
  if(RURAL_GROUP){ RURAL_GROUP.forEach(function(o){ try{ scene.remove(o); }catch(e){} }); RURAL_GROUP = null; }
  if(TRIBAL_GROUP){ TRIBAL_GROUP.forEach(function(o){ try{ scene.remove(o); }catch(e){} }); TRIBAL_GROUP = null; }
  codeStatus('rural and tribal cleared');
}

/* THE GATHERING PLAZA — where city, rural, and tribal meet. The heart. */
var PLAZA_GROUP = null;

/* FUTURE PLAYGROUNDS — where kids meet and share joy */
var PLAYGROUNDS = null;
function buildSlide(x, y, z, colorHex){
  var g = new THREE.Group();
  /* ladder */
  var ladder = new THREE.Mesh(new THREE.BoxGeometry(0.4, 2, 0.3),
    new THREE.MeshStandardMaterial({color: 0x888899, metalness: 0.7, roughness: 0.4}));
  ladder.position.y = 1;
  g.add(ladder);
  /* slide — curved */
  var slideCurve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(0, 2, 0),
    new THREE.Vector3(0, 1.2, 0.8),
    new THREE.Vector3(0, 0.3, 1.8)
  ]);
  var slide = new THREE.Mesh(
    new THREE.TubeGeometry(slideCurve, 16, 0.3, 8, false),
    new THREE.MeshStandardMaterial({
      color: colorHex, emissive: colorHex, emissiveIntensity: 0.3,
      roughness: 0.3, metalness: 0.2
    })
  );
  /* flatten the tube to make it slide-shaped */
  slide.scale.x = 1.5;
  g.add(slide);
  g.position.set(x, y, z);
  return g;
}
function buildSwingSet(x, y, z){
  var g = new THREE.Group();
  var frameMat = new THREE.MeshStandardMaterial({color: 0xff66cc, metalness: 0.6, roughness: 0.4});
  /* A-frame */
  [-1,1].forEach(function(s){
    var leg1 = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 2.5, 8), frameMat);
    leg1.position.set(0, 1.25, s*0.8); leg1.rotation.x = s*0.25;
    g.add(leg1);
  });
  var bar = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 2.5, 8), frameMat);
  bar.rotation.z = Math.PI/2; bar.position.y = 2.4;
  g.add(bar);
  /* swings */
  [-0.6, 0.6].forEach(function(sx){
    var swingG = new THREE.Group();
    [-1,1].forEach(function(s){
      var chain = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 1.2, 6),
        new THREE.MeshStandardMaterial({color: 0xcccccc, metalness: 0.8, roughness: 0.3}));
      chain.position.set(0, -0.6, s*0.2);
      swingG.add(chain);
    });
    var seat = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.06, 0.5),
      new THREE.MeshStandardMaterial({color: 0x00ccff, roughness: 0.5}));
    seat.position.y = -1.2;
    swingG.add(seat);
    swingG.position.set(sx, 2.4, 0);
    g.add(swingG);
    /* swing animation */
    var t = Math.random()*10;
    var anim = setInterval(function(){
      t += 0.03;
      swingG.rotation.x = Math.sin(t)*0.4;
    }, 50);
    swingG.userData.anim = anim;
  });
  g.position.set(x, y, z);
  return g;
}
function buildMerryGoRound(x, y, z){
  var g = new THREE.Group();
  /* platform */
  var platform = new THREE.Mesh(new THREE.CylinderGeometry(2, 2.2, 0.2, 20),
    new THREE.MeshStandardMaterial({color: 0xffcc00, roughness: 0.5}));
  platform.position.y = 0.3;
  g.add(platform);
  /* center pole */
  var pole = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.15, 3, 12),
    new THREE.MeshStandardMaterial({color: 0xff66cc, metalness: 0.5, roughness: 0.4}));
  pole.position.y = 1.8;
  g.add(pole);
  /* canopy */
  var canopy = new THREE.Mesh(new THREE.ConeGeometry(2.5, 1.2, 16),
    new THREE.MeshStandardMaterial({
      color: 0xff3399, emissive: 0xff3399, emissiveIntensity: 0.2,
      roughness: 0.6, side: THREE.DoubleSide
    }));
  canopy.position.y = 3.5;
  g.add(canopy);
  /* lights around canopy */
  for(var i=0; i<12; i++){
    var ang = i*(Math.PI*2/12);
    var light = new THREE.Mesh(new THREE.SphereGeometry(0.08, 8, 8),
      new THREE.MeshStandardMaterial({
        color: [0xff0000,0x00ff00,0x0000ff,0xffff00][i%4],
        emissive: [0xff0000,0x00ff00,0x0000ff,0xffff00][i%4],
        emissiveIntensity: 2
      }));
    light.position.set(Math.cos(ang)*2.3, 3.0, Math.sin(ang)*2.3);
    g.add(light);
  }
  /* seats */
  for(var s=0; s<6; s++){
    var sa = s*(Math.PI*2/6);
    var seat = new THREE.Mesh(new THREE.BoxGeometry(0