<!-- PLAYA 2.0'S WORLD · part 3 -->
.box(mat, len, t, d, mx - nx * t / 2, my - ny * t / 2, z, 0, 0, rz);
}
function walkDeck(k, M, opts, L, W, dh, gW, railX, railH) {
  if (!opts.ctx) return false;
  const Lw = L - 2 * RAMP;
  k.box(M.black, Lw, 0.5, W, 0, dh - 0.14 - 0.25, 0);
  k.box(M.graphite, Lw, 0.14, gW, 0, dh - 0.07, 0);
  for (const xe of [-L / 2, L / 2]) {
    rampBox(k, M.black, W, 0.5, W, xe, 0, dh);
    for (const sd of [-1, 1]) rampBox(k, M.black, 0.2, railH, 0.2, xe, sd * railX, dh, railH); // sloped railing on ramp
  }
  return true;
}

// Suspension bridge: two red portal towers, parabolic main cables, vertical hangers.
export function createSuspensionBridge(THREE, scene, opts = {}) {
  const g = new THREE.Group(); g.name = 'SuspensionBridge';
  const M = mats(THREE, opts), k = kit(THREE, g);
  const L = opts.length ?? 60, W = opts.width ?? 6, dh = opts.deckHeight ?? 3, th = opts.towerHeight ?? 16;
  const tx = L * 0.3;
  const walk = walkDeck(k, M, opts, L, W, dh, W - 1.4, W / 2 - 0.1, 0.6), Ld = walk ? L - 2 * RAMP : L, ys = walk ? -0.14 : 0;
  if (!walk) { k.box(M.black, L, 0.5, W, 0, dh - 0.25, 0); k.box(M.graphite, L, 0.14, W - 1.4, 0, dh + 0.07, 0); }
  [-1, 1].forEach(s => { k.box(M.glow, Ld, 0.14, 0.14, 0, dh + 0.05 + ys, s * (W / 2 - 0.2)); k.box(M.black, Ld, 0.6, 0.2, 0, dh + 0.3, s * (W / 2 - 0.1)); });
  for (const x of [-tx, tx]) {
    for (const s of [-1, 1]) k.box(M.red, 0.9, th, 0.9, x, th / 2, s * (W / 2 + 0.3));
    for (const y of [dh + 4, th - 3, th - 0.4]) k.box(M.red, 0.6, 0.8, W + 1.5, x, y, 0);
    k.box(M.glow, 0.14, 0.14, W + 1.6, x + 0.35, th - 0.4, 0);
  }
  const cableY = x => { const ax = Math.abs(x); if (ax <= tx) { const t = x / tx; return dh + 1.2 + (th - dh - 1.2) * t * t; } const t = (ax - tx) / (L / 2 - tx); return th - (th - dh) * t; };
  const seg = 40;
  for (const s of [-1, 1]) {
    const z = s * (W / 2 + 0.3);
    for (let i = 0; i < seg; i++) { const x0 = -L / 2 + i * L / seg, x1 = x0 + L / seg; k.rod(M.black, 0.11, [x0, cableY(x0), z], [x1, cableY(x1), z]); }
    for (let x = -tx + 2; x < tx - 1; x += 2) if (cableY(x) - dh > 0.4) k.rod(M.red, 0.07, [x, dh, z], [x, cableY(x), z], 4);
  }
  if (!walk) for (const x of [-L / 2, L / 2]) k.box(M.stone, 2, dh, W + 1, x, dh / 2, 0);
  for (const x of [-tx, tx]) k.box(M.stone, 2.4, 0.6, W + 2.4, x, 0.3, 0);
  if (opts.ctx) for (const x of [-tx, tx]) k.box(M.stone, 2.4, 2.6, W + 2.4, x, -1.3, 0); // piers down to the riverbed
  const rw = s => [-L / 2, L / 2, s * (W / 2 - 0.1) - 0.1, s * (W / 2 - 0.1) + 0.1, dh + 0.6];
  const cols = [rw(-1), rw(1)];
  // railings only: no end-abutment / pier / tower-base colliders (the core deck makes the walkway passable)
  k.build(); return place(scene, g, opts, { kind: 'bridge', hx: L / 2 + 1, hz: Math.max(W / 2 + 0.75, (W + 2.4) / 2), pref: 3, cols });
}

// Arch bridge: tied red steel arch over a black deck with hangers and glowing deck lights.
export function createArchBridge(THREE, scene, opts = {}) {
  const g = new THREE.Group(); g.name = 'ArchBridge';
  const M = mats(THREE, opts), k = kit(THREE, g);
  const L = opts.length ?? 40, W = opts.width ?? 5, dh = opts.deckHeight ?? 2, rise = opts.rise ?? 9;
  const walk = walkDeck(k, M, opts, L, W, dh, W - 1, W / 2 + 0.2, 0.6), Ld = walk ? L - 2 * RAMP : L, ys = walk ? -0.14 : 0;
  if (!walk) { k.box(M.black, L, 0.5, W, 0, dh - 0.25, 0); k.box(M.graphite, L, 0.14, W - 1, 0, dh + 0.07, 0); }
  const seg = 32, arch = x => dh + rise * (1 - (2 * x / L) ** 2);
  for (const s of [-1, 1]) {
    const z = s * (W / 2 + 0.2);
    for (let i = 0; i < seg; i++) { const x0 = -L / 2 + i * L / seg, x1 = x0 + L / seg; k.rod(M.red, 0.3, [x0, arch(x0), z], [x1, arch(x1), z], 8); k.rod(M.glow, 0.07, [x0, arch(x0) + 0.32, z], [x1, arch(x1) + 0.32, z], 4); }
    for (let x = -L / 2 + 2.5; x < L / 2 - 2; x += 2.5) k.rod(M.black, 0.07, [x, dh, z], [x, arch(x), z], 4);
    k.box(M.glow, Ld, 0.14, 0.14, 0, dh + 0.05 + ys, s * (W / 2 - 0.15));
  }
  for (let x = -L / 2 + 5; x < L / 2 - 4; x += 5) k.rod(M.red, 0.15, [x, arch(x), -W / 2 - 0.2], [x, arch(x), W / 2 + 0.2], 6);
  if (!walk) for (const x of [-L / 2, L / 2]) k.box(M.stone, 2.4, dh, W + 1.2, x, dh / 2, 0);
  const cols = [];
  for (const s of [-1, 1]) cols.push([-L / 2, L / 2, s * (W / 2 + 0.2) - 0.3, s * (W / 2 + 0.2) + 0.3, dh + rise]); // arch ribs + railing line
  // railings only: no end-abutment colliders
  k.build(); return place(scene, g, opts, { kind: 'bridge', hx: L / 2 + 1.2, hz: Math.max((W + 1.2) / 2, W / 2 + 0.5), pref: 1, cols });
}
export default { createSuspensionBridge, createArchBridge };


===== FILE: playa-world/fireflies.js =====
// fireflies.js — Playa 2.0 original: drifting glowing red/amber light motes (single Points draw call). fn(THREE, scene, opts) -> Group
export function createFireflies(THREE, scene, opts = {}) {
  const g = new THREE.Group(); g.name = 'Fireflies'; g.userData.minDim = Infinity;
  const n = opts.count ?? 160, R = opts.radius ?? 55, base = ne