<!-- RUBY-BACKUP · part 476 -->
sz: pz+120, tx: (px+175)*0.13, tz: (pz+125)*0.13},
    {name: 'Nalani', color: 0x66ccff, sx: px+170, sz: pz-120, tx: (px+180)*0.13, tz: (pz-110)*0.13}
  ];
  givers.forEach(function(gv){
    var giver = buildMissionGiver(gv.name, gv.color, gv.sx*0.13, 0, gv.sz*0.13, gv.tx, gv.tz);
    scene.add(giver);
    MISSION_GIVERS.push(giver);
  });
  if(!DARK_PLACES) DARK_PLACES = [];
  MISSION_GIVERS.forEach(function(m){ DARK_PLACES.push(m); });
  codeStatus('good souls — Kai, Leilani, Makoa, Nalani. They bring missions.');
}
function buildDarkPlaces(){
  if(typeof scene==='undefined' || typeof mePos==='undefined') return;
  /* bypassed for kids */
  if(typeof KIDMODE!=='undefined' && KIDMODE){
    codeStatus('dark places are bypassed for kids');
    return;
  }
  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 = [];
  var px = mePos.x, pz = mePos.z;
  var beach = buildStormBeach(px-180, pz-120);
  beach.scale.set(0.3, 0.3, 0.3); /* very small */
  scene.add(beach); DARK_PLACES.push(beach);
  var peak = buildLostSoulsPeak(px+180, pz-120);
  peak.scale.set(0.3, 0.3, 0.3);
  scene.add(peak); DARK_PLACES.push(peak);
  var under = buildUnderworldFallacy(px-180, pz+120);
  under.scale.set(0.3, 0.3, 0.3);
  scene.add(under); DARK_PLACES.push(under);
  var worm = buildParadoxWormholes(px+180, pz+120);
  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, 