<!-- SOLID CITY · part 1809 -->
(0, (r - 25) / 65));
    h -= 6.5 * Math.exp(-(lateral / 13) * (lateral / 13)) * gate;
  }
  if (r > 295) { var kk = (r - 295) / 35; h = h * (1 - kk) + 2.2 * kk; }
  if (r < 30) { var pk = r < 12 ? 0 : (r - 12) / 18; h = 30 * (1 - pk) + h * pk; }
  return h;
}

export function createCrystalCrown(opts) {
  var T = opts.THREE;
  var CX = opts.center.x, CZ = opts.center.z, BASE = opts.baseY, FLOOR = opts.floorY;
  var R = mulberry(20261014);
  var group = new T.Group(); group.name = 'the-crystal-crown';
  var C = {
    stone: 0x55504c, stoneDark: 0x35302c, gold: 0xd8a833, lawn: 0x4d7d33, lawnDeep: 0x3a6126,
    leaf: 0x2a5c2e, leafLight: 0x3f7a38, trunk: 0x5a4630, rock: 0x6a7076,
    crystal: 0xbfe4f5, pool: 0x2e7db0, water: 0x7fb8dd
  };
  function Batch() { this.pos = []; this.nor = []; this.col = []; }
  Batch.prototype.add = function (geo, matrix, color) {
    var g = geo.index ? geo.toNonIndexed() : geo;
    var p = g.getAttribute('position'), n = g.getAttribute('normal');
    var nm = new T.Matrix3().getNormalMatrix(matrix);
    var c = new T.Color(color), v = new T.Vector3(), nv = new T.Vector3();
    for (var i = 0; i < p.count; i++) {
      v.fromBufferAttribute(p, i).applyMatrix4(matrix);
      this.pos.push(v.x, v.y, v.z);
      nv.fromBufferAttribute(n, i).applyMatrix3(nm).normalize();
      this.nor.push(nv.x, nv.y, nv.z);
      this.col.push(c.r, c.g, c.b);
    }
  };
  Batch.prototype.build = function (mat) {
    var g = new T.BufferGeometry();
    g.setAttribute('position', new T.Float32BufferAttribute(this.pos, 3));
    g.setAttribute('normal', new T.Float32BufferAttribute(this.nor, 3));
    g.setAttribute('color', new T.Float32BufferAttribute(this.col, 3));
    var m = new T.Mesh(g, mat); m.frustumCulled = false; return m;
  };
  var solid = new Batch(), glow = new Batch(), glassB = new Batch();
  function mat4(x, y, z, ex, ey, ez, sx, sy, sz) {
    return new T.Matrix4().compose(new T.Vector3(x, y, z),
      new T.Quaternion().setFromEuler(new T.Euler(ex || 0, ey || 0, ez || 0)),
      new T.Vector3(sx == null ? 1 : sx, sy == null ? 1 : sy, sz == null ? 1 : sz));
  }
  function box(b, w, h, d, x, y, z, color, ex, ey, ez) { b.add(new T.BoxGeometry(w, h, d), mat4(x, y, z, ex, ey, ez), color); }
  function cyl(b, rt, rb, h, seg, x, y, z, color, ex, ey, ez) { b.add(new T.CylinderGeometry(rt, rb, h, seg), mat4(x, y, z, ex, ey, ez), color); }
  function sph(b, r, x, y, z, color, sx, sy, sz) { b.add(new T.SphereGeometry(r, 10, 7), mat4(x, y, z, 0, 0, 0, sx, sy, sz), color); }
  function px(r, a) { return CX + Math.cos(a) * r; }
  function pz(r, a) { return CZ + Math.sin(a) * r; }

  /* ---------- the island's body ---------- */
  cyl(solid, 330, 42, 46, 48, CX, BASE - 23, CZ, C.stoneDark);
  glow.add(new T.TorusGeometry(330, 0.35, 6, 72), mat4(CX, BASE - 0.2, CZ, Math.PI / 2, 0, 0), C.gold);
  for (var hi = 0; hi < 12; hi++) {
    var ha = hi / 12 * Math.PI * 2;
    glassB.add(new T.ConeGeometry(2.2, 9, 5), mat4(px(322, ha), BASE - 6, pz(322, ha), Math.PI, 0, 0), C.crystal);
  }

  /* ---------- the land itself: a radial heightfield ---------- */
  var RINGS = 26, SECT = 84;
  var tPos = [], tCol = [], tIdx = [];
  var cGrass = new T.Color(C.lawn), cDeep = new T.Color(C.lawnDeep), cRock = new T.Color(C.rock), cPale = new T.Color(0x9aa8ad), tmpC = new T.Color();
  for (var ri = 0; ri <= RINGS; ri++) {
    var rr = ri / RINGS * 330;
    for (var si = 0; si <= SECT; si++) {
      var th = si / SECT * Math.PI * 2;
      var hh = crownHeight(rr, th);
      tPos.push(px(rr, th), BASE + hh, pz(rr, th));
      var slope = Math.abs(crownHeight(Math.min(rr + 8, 330), th) - hh) / 8;
      if (hh > 40 || slope > 0.85) tmpC.copy(cPale); else if (hh > 24 || slope > 0.5) tmpC.copy(cRock); else if ((ri + si) % 3 === 0) tmpC.copy(cDeep); else tmpC.copy(cGrass);
      tCol.push(tmpC.r, tmpC.g, tmpC.b);
      if (ri < RINGS && si < SECT) {
        var a0 = ri * (SECT + 1) + si, b0 = a0 + SECT + 1;
        tIdx.push(a0, b0, a0 + 1, b0, b0 + 1, a0 + 1);
      }
    }
  }
  var tGeo = new T.BufferGeometry();
  tGeo.setAttribute('position', new T.Float32BufferAttribute(tPos, 3));
  tGeo.setAttribute('color', new T.Float32BufferAttribute(tCol, 3));
  tGeo.setIndex(tIdx); tGeo.computeVertexNormals();
  var terrain = new T.Mesh(tGeo, new T.MeshLambertMaterial({ vertexColors: true }));
  terrain.frustumCulled = false; group.add(terrain);

  /* ---------- the spring lake and the three rivers ---------- */
  cyl(solid, 20, 23, 0.5, 24, CX, BASE + 30.15, CZ, C.pool);
  CROWN_RIVERS.forEach(function (ra) {
    var pts = [];
    for (var r2 = 28; r2 <= 330; r2 += 9.5) pts.push([px(r2, ra), BASE + crownHeight(r2, ra) + 0.22, pz(r2, ra)]);
    for (var i2 = 0; i2 < pts.length - 1; i2++) {
      var p0 = pts[i2], p1 = pts[i2 + 1];
      var d = new T.Vector3(p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]);
      var len = d.length(); d.normalize();
      var side = new T.Vector3().crossVectors(new T.Vec