<!-- PLAYA 2.0'S WORLD · part 12 -->
metry(WORLD_R + 6, 64), new THREE.MeshStandardMaterial({ color: 0x111014, roughness: 0.95 }));
ground.rotation.x = -Math.PI / 2; scene.add(ground);
const grid = new THREE.GridHelper(2 * WORLD_R, 31, 0x5a0612, 0x2a0309); grid.position.y = 0.01; scene.add(grid);
const rim = new THREE.Mesh(new THREE.TorusGeometry(WORLD_R + 0.5, 0.2, 8, 96), new THREE.MeshStandardMaterial({ color: 0xe01937, emissive: 0xe01937, emissiveIntensity: 2 }));
rim.rotation.x = Math.PI / 2; rim.position.y = 0.25; scene.add(rim);
const channel = new THREE.Mesh(new THREE.BoxGeometry(2 * WORLD_R + 12, 0.14, CH1 - CH0), new THREE.MeshStandardMaterial({ color: 0x1a0306, emissive: 0x6a0014, emissiveIntensity: 0.9, metalness: 0.9, roughness: 0.15 }));
channel.position.set(0, 0.03, (CH0 + CH1) / 2); scene.add(channel);

// ---- colliders [x0,x1,z0,z1,top,bottom]
const colliders = [];
const ctx = { addCollider: (x0, x1, z0, z1, top, bot = 0) => colliders.push([x0, x1, z0, z1, top, bot]) };

// ---- reused modules (legacy position placement + ctx colliders)
const ok = [], groups = []; const tryMod = (name, fn) => { try { const g = fn(); ok.push(name); groups.push(g); return g; } catch (e) { console.warn('module failed', name, e.message); } };
tryMod('plaza', () => createCentralPlaza(THREE, scene, { ctx, radius: 11, position: [0, 0, 0] }));
tryMod('spire', () => createTwistingSpire(THREE, scene, { ctx, position: [-28, 0, -18] }));
tryMod('obelisk', () => createRingedObelisk(THREE, scene, { ctx, position: [28, 0, -16] }));
tryMod('lattice', () => createLatticeTower(THREE, scene, { ctx, position: [-40, 0, 8] }));
const bridges = [];
const addBridge = (fn, x, L, dh, gW) => { const g = tryMod(fn.name, () => fn(THREE, scene, { ctx, length: L, position: [x, 0.1, (CH0 + CH1) / 2], rotationY: Math.PI / 2 })); if (g) bridges.push({ x, z: (CH0 + CH1) / 2, L, dh, gW }); };
addBridge(createSuspensionBridge, 16, 40, 3, 4.6);
addBridge(createArchBridge, -22, 30, 2, 4);
// ---- new original landmarks
const island = tryMod('island', () => createFloatingIsland(THREE, scene, { ctx, position: [34, 0, 12] }));
tryMod('gate', () => createArchwayGate(THREE, scene, { ctx, position: [0, 0, -30] }));
tryMod('spiral', () => createSpiralTower(THREE, scene, { ctx, position: [-10, 0, -48] }));
tryMod('monorail', () => createMonorailLoop(THREE, scene, { ctx, radius: 56, height: 8 }));
tryMod('lanterns', () => createLanternPath(THREE, scene, { ctx, points: [[0, -12], [0, -27]], width: 4.4 }));
tryMod('lanterns2', () => createLanternPath(THREE, scene, { ctx, points: [[0, -33], [-5, -43]], width: 4 }));
tryMod('portal', () => createRingPortal(THREE, scene, { ctx, position: [30, 0, -38] }));
tryMod('fireflies', () => createFireflies(THREE, scene, { count: 160 }));

// bridge deck height (runs along z after rotation); STREET = -0.1 local, group y = 0.1
function deckY(x, z) {
  for (const b of bridges) {
    const lz = z - b.z, lx = x - b.x;
    if (Math.abs(lx) < b.gW / 2 && Math.abs(lz) < b.L / 2) { const e = b.L / 2 - Math.abs(lz); return e >= 5 ? b.dh + 0.1 : (b.dh + 0.1) * e / 5; }
  }
  return null;
}
function support(x, z, y) { // highest standable surface under (x,z) not above y+STEP; NaN = blocked (channel)
  let h = 0; const d = deckY(x, z);
  if (d !== null) h = d <= y + STEP ? d : 0; else if (z > CH0 && z < CH1) h = -2;
  for (const c of colliders) if (x + RAD > c[0] && x - RAD < c[1] && z + RAD > c[2] && z - RAD < c[3] && c[4] <= y + STEP && c[4] > h) h = c[4];
  return h;
}
function blocked(x, z, y) {
  y = Math.max(y, support(x, z, y)); // step up first (stairs / ramps), then test for walls
  if (Math.hypot(x, z) > WORLD_R - RAD) return true;
  const d = deckY(x, z); if (d === null && z > CH0 - RAD && z < CH1 + RAD) return true; // water: only via decks
  if (d !== null && d > y + STEP) return true;
  for (const c of colliders) if (x + RAD > c[0] && x - RAD < c[1] && z + RAD > c[2] && z - RAD < c[3] && c[4] > y + STEP && c[5] < y + AV_H) return true;
  return false;
}

// ---- avatar (1.56 tall, merged per material; min dim >= 0.13)
const avatar = new THREE.Group(); scene.add(avatar);
{ const red = new THREE.MeshStandardMaterial({ color: 0xc8102e, roughness: 0.4 }), blk = new THREE.MeshStandardMaterial({ color: 0x141418, roughness: 0.5 }),
    glw = new THREE.MeshStandardMaterial({ color: 0xff2d4b, emissive: 0xff2d4b, emissiveIntensity: 2 });
  const part = (geo, mat, x, y, z) => { const m = new THREE.Mesh(geo, mat); m.position.set(x, y, z); avatar.add(m); return m; };
  part(new THREE.BoxGeometry(0.16, 0.7, 0.18), blk, -0.12, 0.35, 0); part(new THREE.BoxGeometry(0.16, 0.7, 0.18), blk, 0.12, 0.35, 0); // legs 0..0.70
  part(new THREE.BoxGeometry(0.46, 0.52, 0.26), red, 0, 0.96, 0);                                     // torso 0.70..1.22
  part(new THREE.BoxGeometry(0.13, 0.5, 0.14), blk, -0.31, 0.95, 0); part(new THREE.BoxGeometry(0.13, 0.5, 0.14), blk, 0.31, 0.95, 0);
  part(new THREE.BoxGeometry(0.28, 0.3, 0.28), blk, 0, 1.39, 0)