<!-- SOLID CITY · part 1810 -->
tor3(0, 1, 0), d).normalize();
      var up = new T.Vector3().crossVectors(d, side).normalize();
      var bm = new T.Matrix4().makeBasis(side, up, d);
      bm.setPosition((p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2, (p0[2] + p1[2]) / 2);
      solid.add(new T.BoxGeometry(5, 0.18, len + 1.2), bm, C.pool);
    }
  });

  /* ---------- the crystal mountain ---------- */
  var peakSolids = [];
  CROWN_PEAKS.forEach(function (p) {
    var cx = px(p.r, p.a), cz = pz(p.r, p.a), gy = BASE + crownHeight(p.r, p.a);
    var shards = 4 + Math.floor(R() * 2);
    for (var si2 = 0; si2 < shards; si2++) {
      var off = si2 === 0 ? 0 : 4 + R() * 7, oa = R() * Math.PI * 2;
      var sh = p.crystal * (si2 === 0 ? 1 : 0.45 + R() * 0.4);
      glassB.add(new T.ConeGeometry(sh * 0.24 + 1.4, sh, 6), mat4(cx + Math.cos(oa) * off, gy + sh / 2 - 1.5, cz + Math.sin(oa) * off, (R() - 0.5) * 0.14, R() * 3, (R() - 0.5) * 0.14), C.crystal);
    }
    sph(glow, 2.2, cx, gy + p.crystal * 0.55, cz, C.gold);
    peakSolids.push({ x: cx, z: cz, w: 15, d: 15, a: 0, y0: gy - 2, y1: gy + p.crystal });
  });

  /* ---------- rocks ---------- */
  for (var rk = 0; rk < 24; rk++) {
    var ra2 = R() * Math.PI * 2, rr2 = 55 + R() * 250;
    var rh = crownHeight(rr2, ra2);
    solid.add(new T.DodecahedronGeometry(1.4 + R() * 2.2), mat4(px(rr2, ra2), BASE + rh + 0.6, pz(rr2, ra2), R() * 3, R() * 3, 0), C.rock);
  }

  /* ---------- big trees, wherever the land is kind ---------- */
  var treePts = [], tries = 0;
  function riverClear(r, th) {
    for (var k = 0; k < CROWN_RIVERS.length; k++) {
      var dth = Math.abs(th - CROWN_RIVERS[k]) % (Math.PI * 2); if (dth > Math.PI) dth = Math.PI * 2 - dth;
      if (dth * Math.max(r, 20) < 14) return false;
    }
    return true;
  }
  function peakClear(r, th) {
    var x = Math.cos(th) * r, z = Math.sin(th) * r;
    for (var i = 0; i < CROWN_PEAKS.length; i++) {
      var p = CROWN_PEAKS[i], dx = x - Math.cos(p.a) * p.r, dz = z - Math.sin(p.a) * p.r;
      if (dx * dx + dz * dz < 42 * 42) return false;
    }
    return true;
  }
  while (treePts.length < 85 && tries < 600) {
    tries++;
    var tr = 40 + R() * 272, ta = R() * Math.PI * 2;
    var th2 = crownHeight(tr, ta);
    if (th2 > 30) continue;
    if (!riverClear(tr, ta) || !peakClear(tr, ta)) continue;
    if (Math.abs(crownHeight(tr + 7, ta) - th2) > 5.5) continue;
    var ok = true;
    for (var q = 0; q < treePts.length; q++) { var ddx = treePts[q][0] - tr * Math.cos(ta), ddz = treePts[q][1] - tr * Math.sin(ta); if (ddx * ddx + ddz * ddz < 15 * 15) { ok = false; break; } }
    if (!ok) continue;
    treePts.push([tr * Math.cos(ta), tr * Math.sin(ta), tr, ta, th2]);
  }
  var treeSolids = [];
  treePts.forEach(function (tp) {
    var x = CX + tp[0], z = CZ + tp[1], gy = BASE + tp[4], s = 1.7 + R() * 0.9;
    cyl(solid, 0.34 * s, 0.5 * s, 4.6 * s, 6, x, gy + 2.3 * s, z, C.trunk);
    sph(solid, 2.9 * s, x, gy + (4.6 + 2.2) * s, z, C.leaf, 1, 0.85, 1);
    sph(solid, 2.0 * s, x + 1.6 * s, gy + (4.6 + 1.2) * s, z + 0.8 * s, C.leafLight, 1, 0.8, 1);
    sph(solid, 1.7 * s, x - 1.5 * s, gy + (4.6 + 1.5) * s, z - 1.0 * s, C.leaf, 1, 0.8, 1);
    treeSolids.push({ x: x, z: z, w: 1, d: 1, a: 0, y0: gy, y1: gy + 4.6 * s });
  });

  /* ---------- the falls and the rain ---------- */
  var fallMats = [];
  var falls = [];
  CROWN_RIVERS.forEach(function (ra) {
    var fx = px(331, ra), fz = pz(331, ra);
    var topY = BASE + crownHeight(326, ra) + 0.4, botY = FLOOR + 4;
    var hgt = topY - botY;
    var fm = new T.MeshBasicMaterial({ color: 0xbfe0f2, transparent: true, opacity: 0.34, depthWrite: false });
    var fall = new T.Mesh(new T.CylinderGeometry(1.5, 3.4, hgt, 10, 1, true), fm);
    fall.position.set(fx, botY + hgt / 2, fz); fall.frustumCulled = false; group.add(fall);
    var fm2 = new T.MeshBasicMaterial({ color: 0xeaf6ff, transparent: true, opacity: 0.5, depthWrite: false });
    var core = new T.Mesh(new T.CylinderGeometry(0.7, 1.6, hgt, 8, 1, true), fm2);
    core.position.set(fx, botY + hgt / 2, fz); core.frustumCulled = false; group.add(core);
    var foam = new T.Mesh(new T.CircleGeometry(5.5, 18), new T.MeshBasicMaterial({ color: 0xeaf6ff, transparent: true, opacity: 0.4, depthWrite: false }));
    foam.rotation.x = -Math.PI / 2; foam.position.set(fx, FLOOR + 0.35, fz); foam.frustumCulled = false; group.add(foam);
    fallMats.push(fm, fm2);
    falls.push({ x: fx, z: fz, top: topY, bottom: botY });
  });
  var RAIN_N = 700, rainPos = new Float32Array(RAIN_N * 3), rainSpd = new Float32Array(RAIN_N);
  for (var rn = 0; rn < RAIN_N; rn++) {
    var rra = R() * Math.PI * 2, rrr = Math.sqrt(R()) * 322;
    rainPos[rn * 3] = CX + Math.cos(rra) * rrr;
    rainPos[rn * 3 + 1] = FLOOR + 6 + R() * (BASE - FLOOR - 12);
    rainPos[rn * 3 + 2] = CZ + Math.sin(rra) * rrr;
    rainSpd[rn] = 38 + R() * 22;
  }
  var rainGeo = new T.BufferGeometry();
  rainGeo.setAttribute('position', new T.BufferAttribute(rainPos, 3)