<!-- RUBY-BACKUP · part 477 -->
roughness: 0.9});
  [[1,0,'CITY'],[-0.7,0.7,'RURAL'],[-0.7,-0.7,'TRIBAL']].forEach(function(p){
    var path = new THREE.Mesh(new THREE.PlaneGeometry(3, 30), pathMat);
    path.rotation.x = -Math.PI/2;
    path.rotation.z = Math.atan2(p[1], p[0]);
    path.position.set(p[0]*20, 0.05, p[1]*20);
    g.add(path);
  });
  /* benches — for gathering */
  for(var b=0; b<8; b++){
    var ba = b*(Math.PI*2/8);
    var bench = new THREE.Mesh(new THREE.BoxGeometry(1.5, 0.3, 0.4),
      new THREE.MeshStandardMaterial({color: 0x6a4a2a, roughness: 0.8}));
    bench.position.set(Math.cos(ba)*7, 0.45, Math.sin(ba)*7);
    bench.rotation.y = -ba + Math.PI/2;
    g.add(bench);
  }
  g.position.set(px, 0, pz);
  scene.add(g);
  PLAZA_GROUP.push(g);
  codeStatus('the Gathering Plaza — where all zones meet. The heart of the map.');
}
function clearPlaza(){
  if(PLAZA_GROUP){
    PLAZA_GROUP.forEach(function(o){
      try{ scene.remove(o); }catch(e){}
      if(o.userData.anim) clearInterval(o.userData.anim);
    });
    PLAZA_GROUP = null;
  }
  codeStatus('plaza cleared');
}
function buildAncestralCity(){
  if(typeof scene==='undefined' || typeof mePos==='undefined') return;
  /* clear previous */
  if(ANCESTRAL_CITY){
    ANCESTRAL_CITY.forEach(function(o){ try{ scene.remove(o); }catch(e){} });
  }
  ANCESTRAL_CITY = [];
  var px = mePos.x, pz = mePos.z;
  /* downtown — towers */
  buildCity();
  ANCESTRAL_CITY = ANCESTRAL_CITY.concat(CITY_TOWERS);
  /* streets with ancestor names */
  placeAncestorStreets();
  ANCESTRAL_CITY = ANCESTRAL_CITY.concat(STREET_SIGNS);
  /* subdivisions — residential rings around downtown */
  var subdivNames = ['KAMAILE', 'KAILIWAI', 'WILCOX', 'KANAKAOLE'];
  for(var ring=0; ring<4; ring++){
    var radius = 60 + ring*25; /* expanded territories */
    var count = 10 + ring*5;
    for(var i=0; i<count; i++){
      var ang = (i/count)*Math.PI*2 + ring*0.3;
      var x = px + Math.cos(ang)*radius;
      var z = pz + Math.sin(ang)*radius;
      /* skip where streets are */
      if(Math.abs(x-px) < 10 || Math.abs(z-pz) < 10) continue; /* wider streets */
      var hue = 0.08 + (ring*0.05) + Math.random()*0.05;
      var house = buildSubdivisionHouse(x, z, hue);
      scene.add(house);
      ANCESTRAL_CITY.push(house);
    }
  }
  /* wildlife in the green spaces */
  spawnWildlife();
  codeStatus('ANCESTRAL CITY — towers, '+ANCESTOR_STREETS.length+' named streets, subdivisions, wildlife. All intertwined.');
}
function clearAncestralCity(){
  if(ANCESTRAL_CITY){
    ANCESTRAL_CITY.forEach(function(o){ try{ scene.remove(o); }catch(e){} });
    ANCESTRAL_CITY = null;
  }
  clearCity();
  clearAncestorStreets();
  clearWildlife();
  codeStatus('ancestral city cleared');
}
function clearCity(){
  CITY_TOWERS.forEach(function(t){ try{ scene.remove(t); }catch(e){} });
  CITY_TOWERS = [];
  CITY_BUILT = false;
  codeStatus('city cleared');
}
function cosmicSky(){
  if(typeof scene==='undefined') return;
  /* deep space background */
  scene.background = new THREE.Color(0x050510);
  scene.fog = new THREE.Fog(0x050510, 30, 100);
  /* stars */
  var starGeo = new THREE.BufferGeometry();
  var starPos = [];
  for(var i=0; i<2000; i++){
    var r = 60 + Math.random()*40;
    var theta = Math.random()*Math.PI*2;
    var phi = Math.acos(Math.random()*2-1);
    starPos.push(r*Math.sin(phi)*Math.cos(theta), r*Math.cos(phi), r*Math.sin(phi)*Math.sin(theta));
  }
  starGeo.setAttribute('position', new THREE.Float32BufferAttribute(starPos, 3));
  var starMat = new THREE.PointsMaterial({color: 0xffffff, size: 0.3, sizeAttenuation: true});
  var stars = new THREE.Points(starGeo, starMat);
  stars.name = 'cosmic-stars';
  scene.add(stars);
  /* nebula — colored clouds */
  [0xff00ff, 0x00ffff, 0xff6600].forEach(function(col, i){
    var nebGeo = new THREE.SphereGeometry(20, 16, 16);
    var nebMat = new THREE.MeshBasicMaterial({
      color: col, transparent: true, opacity: 0.08, side: THREE.BackSide
    });
    var neb = new THREE.Mesh(nebGeo, nebMat);
    neb.position.set((i-1)*40, 20, -50);
    neb.name = 'cosmic-nebula-'+i;
    scene.add(neb);
  });
  /* planet */
  var planet = new THREE.Mesh(new THREE.SphereGeometry(8, 32, 32),
    new THREE.MeshStandardMaterial({color: 0x4466ff, roughness: 0.7}));
  planet.position.set(30, 25, -60);
  planet.name = 'cosmic-planet';
  scene.add(planet);
  /* ring */
  var ring = new THREE.Mesh(new THREE.TorusGeometry(11, 0.8, 8, 40),
    new THREE.MeshStandardMaterial({color: 0xffcc66, roughness: 0.5}));
  ring.position.copy(planet.position);
  ring.rotation.x = Math.PI/3;
  ring.name = 'cosmic-ring';
  scene.add(ring);
  codeStatus('cosmic sky — stars, nebula, planet');
}
function cosmicHouses(){
  if(typeof scene==='undefined' || typeof mePos==='undefined') return;
  COSMIC_HOUSES.forEach(function(h){ try{ scene.remove(h); }catch(e){} });
  COSMIC_HOUSES = [];
  var px = mePos.x, pz = mePos.z;
  for(var i=0; i<5; i++){
    var ang = (i/5)*Math.PI*2