<!-- RUBY-BACKUP · part 236 -->
;
  worm.scale.set(0.3, 0.3, 0.3);
  scene.add(worm); DARK_PLACES.push(worm);
  /* funny homeless people — gentle souls in the dark places */
  [[px-180, pz-110], [px+180, pz-110], [px-180, pz+130], [px+180, pz+130]].forEach(function(p){
    var h = buildFunnyHomeless(p[0]*0.13, 0, p[1]*0.13);
    scene.add(h);
    DARK_PLACES.push(h);
  });
  placeThreeSouls();
  placeMissionGivers();
  codeStatus('the dark places — storm beach, lost souls peak, underworld fallacy, paradox wormholes.');
}
function clearDarkPlaces(){
  if(DARK_PLACES){
    DARK_PLACES.forEach(function(d){
      try{ scene.remove(d); }catch(e){}
      d.traverse(function(o){
        if(o.userData.anim) clearInterval(o.userData.anim);
        if(o.userData.boltAnim) clearInterval(o.userData.boltAnim);
      });
    });
    DARK_PLACES = null;
  }
  codeStatus('dark places cleared');
}
function buildGatheringPlaza(){
  if(typeof scene==='undefined' || typeof mePos==='undefined') return;
  if(PLAZA_GROUP){
    PLAZA_GROUP.forEach(function(o){ try{ scene.remove(o); }catch(e){} if(o.userData.anim) clearInterval(o.userData.anim); });
  }
  PLAZA_GROUP = [];
  var g = new THREE.Group();
  var px = mePos.x*0.13, pz = mePos.z*0.13;
  /* circular plaza — stone */
  var plaza = new THREE.Mesh(
    new THREE.CylinderGeometry(10, 10.5, 0.3, 32),
    new THREE.MeshStandardMaterial({color: 0x8a7a6a, roughness: 0.8})
  );
  plaza.position.y = 0.15;
  g.add(plaza);
  /* intaglio rings — carved into the stone */
  for(var r=2; r<10; r+=2){
    var ring = new THREE.Mesh(
      new THREE.TorusGeometry(r, 0.05, 6, 48),
      new THREE.MeshStandardMaterial({color: 0xc8aa64, metalness: 0.6, roughness: 0.4})
    );
    ring.rotation.x = Math.PI/2;
    ring.position.y = 0.32;
    g.add(ring);
  }
  /* central monument — the ancestors */
  var monument = new THREE.Mesh(
    new THREE.BoxGeometry(2, 6, 2),
    new THREE.MeshStandardMaterial({color: 0x3a3a4a, metalness: 0.7, roughness: 0.3})
  );
  monument.position.y = 3.3;
  g.add(monument);
  /* ancestor names — spiraling up the monument */
  var names = ['KAMAILE','KAILIWAI','WILCOX','LILOA','UMI','KEAWE','UENUKU'];
  names.forEach(function(n, i){
    var cv = document.createElement('canvas');
    cv.width = 256; cv.height = 48;
    var ctx = cv.getContext('2d');
    ctx.fillStyle = 'rgba(0,0,0,0)';
    ctx.fillStyle = '#ffcc00';
    ctx.font = 'bold 28px Georgia';
    ctx.textAlign = 'center';
    ctx.fillText(n, 128, 32);
    var tex = new THREE.CanvasTexture(cv);
    var label = new THREE.Mesh(
      new THREE.PlaneGeometry(1.8, 0.34),
      new THREE.MeshBasicMaterial({map: tex, transparent: true})
    );
    var ang = i*(Math.PI*2/names.length);
    label.position.set(Math.sin(ang)*1.05, 2+i*0.6, Math.cos(ang)*1.05);
    label.rotation.y = ang;
    g.add(label);
  });
  /* eternal flame — blue, like the tribal holo-fires */
  var flame = new THREE.Mesh(
    new THREE.ConeGeometry(0.5, 1.5, 12),
    new THREE.MeshStandardMaterial({
      color: 0x00aaff, emissive: 0x00aaff, emissiveIntensity: 2.5,
      transparent: true, opacity: 0.85
    })
  );
  flame.position.y = 7;
  g.add(flame);
  var flameAnim = setInterval(function(){
    flame.scale.y = 1 + Math.sin(Date.now()*0.005)*0.15;
    flame.rotation.y += 0.02;
  }, 50);
  g.userData.anim = flameAnim;
  /* three paths — to city, rural, tribal */
  var pathMat = new THREE.MeshStandardMaterial({color: 0xa89888, 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_C