<!-- SOLID CITY · part 1804 -->
and titanium with
   * turning violet hearts: one at the head of the terrace's
   * processional way, one on the Crown's spring plateau.
   * Step into either and the world puts you at the other. */
  var GY0 = opts.groundY;
  var portalDiscs = [];
  function buildMouth(r, a, baseY) {
    var x = px(r, a), z = pz(r, a), phi = Math.atan2(Math.cos(a), Math.sin(a));
    glow.add(new T.TorusGeometry(3.4, 0.14, 6, 32), mat4(x, baseY + 0.12, z, Math.PI / 2, 0, 0), C.gold);
    solid.add(new T.TorusGeometry(3.0, 0.17, 6, 32), mat4(x, baseY + 3.1, z, 0, phi, 0), C.stone);
    var disc = new T.Mesh(new T.CircleGeometry(2.85, 28), new T.MeshBasicMaterial({ color: 0x8a6aff, transparent: true, opacity: 0.5, side: T.DoubleSide, depthWrite: false }));
    disc.position.set(x, baseY + 3.1, z); disc.rotation.y = phi; disc.frustumCulled = false; group.add(disc);
    var inner = new T.Mesh(new T.TorusGeometry(1.6, 0.06, 5, 28), new T.MeshBasicMaterial({ color: 0xd8e6ff }));
    inner.position.set(x, baseY + 3.1, z); inner.rotation.y = phi; inner.frustumCulled = false; group.add(inner);
    portalDiscs.push(disc, inner);
    return { x: x, z: z, y: baseY };
  }
  var mouthLow = buildMouth(500, Math.PI / 2, GY0);
  var mouthHigh = buildMouth(36, 1.57, BASE + crownHeight(36, 1.57));
  var portals = [
    { name: 'terrace', x: mouthLow.x, z: mouthLow.z, y: mouthLow.y },
    { name: 'crown', x: mouthHigh.x, z: mouthHigh.z, y: mouthHigh.y }
  ];

  group.add(solid.build(new T.MeshLambertMaterial({ vertexColors: true })));
  group.add(glassB.build(new T.MeshLambertMaterial({ vertexColors: true, transparent: true, opacity: 0.82, emissive: 0x1d3442 })));
  group.add(glow.build(new T.MeshBasicMaterial({ vertexColors: true })));

  group.userData = {
    center: { x: CX, z: CZ }, baseY: BASE, floorY: FLOOR, radius: CROWN_RADIUS,
    trees: treePts.length, falls: falls, rivers: CROWN_RIVERS.slice(), peaks: CROWN_PEAKS.length,
    solids: peakSolids.concat(treeSolids),
    sheep: flock.length, portals: portals, birds: birds.length, orchards: orchard,
    flockAt: function () { return flock.map(function (f) { return [Math.round(f.x * 10) / 10, Math.round(f.z * 10) / 10]; }); },
    heightAt: function (wx, wz) {
      var dx = wx - CX, dz = wz - CZ, r = Math.hypot(dx, dz);
      if (r > 332) return null;
      return BASE + crownHeight(r, Math.atan2(dz, dx));
    },
    update: function (dt, t) {
      var pos = rainGeo.getAttribute('position');
      for (var i = 0; i < RAIN_N; i++) {
        var y = pos.getY(i) - rainSpd[i] * dt;
        if (y < FLOOR + 5) y = BASE - 4 - (R() * 8);
        pos.setY(i, y);
      }
      pos.needsUpdate = true;
      var pulse = 0.32 + Math.sin((t || 0) * 2.2) * 0.05;
      fallMats.forEach(function (m, i3) { m.opacity = i3 % 2 ? 0.48 + Math.sin((t || 0) * 2.2) * 0.07 : pulse; });
      portalDiscs.forEach(function (d, di) { d.rotation.z = (t || 0) * (di % 2 ? -0.9 : 0.6); });
      birds.forEach(function (bd) {
        var aa = bd.ph + (t || 0) * bd.sp;
        bd.mesh.position.set(CX + Math.cos(aa) * bd.r, BASE + bd.h + Math.sin((t || 0) * 0.9 + bd.ph * 7) * 1.6, CZ + Math.sin(aa) * bd.r);
        bd.mesh.rotation.set(Math.sin((t || 0) * 1.7 + bd.ph * 5) * 0.06, -aa, 0.32);
      });
      var hx = CX + Math.cos(herd.a) * herd.r, hz = CZ + Math.sin(herd.a) * herd.r;
      flock.forEach(function (sh) {
        var cyc = ((t || 0) + sh.seed * 13) % 26;
        var walking = cyc < 14;
        if (walking && dt) {
          sh.head += Math.sin((t || 0) * 0.23 + sh.seed * 9) * 0.4 * dt;
          var dxh = hx - sh.x, dzh = hz - sh.z, dh = Math.hypot(dxh, dzh);
          if (dh > 60) { var want = Math.atan2(dxh, dzh); var diff = ((want - sh.head + Math.PI * 3) % (Math.PI * 2)) - Math.PI; sh.head += diff * Math.min(1, dt * 1.5); }
          sh.x += Math.sin(sh.head) * 1.35 * dt;
          sh.z += Math.cos(sh.head) * 1.35 * dt;
        }
        var rr = Math.hypot(sh.x - CX, sh.z - CZ), th = Math.atan2(sh.z - CZ, sh.x - CX);
        if (rr > 305) { sh.x = CX + Math.cos(th) * 305; sh.z = CZ + Math.sin(th) * 305; rr = 305; }
        sh.mesh.position.set(sh.x, BASE + crownHeight(rr, th), sh.z);
        sh.mesh.rotation.set(walking ? Math.sin((t || 0) * 7 + sh.seed * 20) * 0.02 : 0.09, sh.head, 0);
      });
    }
  };
  return group;
}
export default createCrystalCrown;


===== FILE: public/city/crystal-crown.js.bak-all-20261014 =====
/* THE CRYSTAL CROWN — a letter in the code.
 *
 * The King's word (2026-10-14 ~06:41): "Make another one on
 * top with a crystal mountain and have valleys with rivers
 * that fall to rain on the houses below. And make trees all
 * over big ones" — "the top of the glass one, floating even
 * higher."
 *
 * So above the glass city floats a wild crown: an island of
 * meadow and valley carrying a mountain of crystal. Three
 * rivers rise at the spring lake in its high bowl, run down
 * the carved valleys, and pour off the rim — falling a
 * hundred met