<!-- SOLID CITY · part 2821 -->
E, H = opts.heightAt;
  var hiTier = opts.tier !== 'lo';
  var group = new T.Group(); group.name = 'nature-layer';
  var R = mulberry(20261021), CR = mulberry(424242);
  var counts = {}, samples = {};
  var uTime = { value: 0 };

  var EXC = [[0, 0, 150], [0, -1600, 600], [-1300, -1000, 280], [-390, 300, 130], [-900, -760, 110], [2035, 1995, 130]];
  function excluded(x, z) {
    if (x > 2130 && x < 3070 && z > 830 && z < 2070) return true;
    for (var i = 0; i < EXC.length; i++) { var dx = x - EXC[i][0], dz = z - EXC[i][1]; if (dx * dx + dz * dz < EXC[i][2] * EXC[i][2]) return true; }
    return false;
  }
  function slope(x, z, h) { var d = 2.5; return Math.max(Math.abs(H(x + d, z) - h), Math.abs(H(x - d, z) - h), Math.abs(H(x, z + d) - h), Math.abs(H(x, z - d) - h)) / d; }
  function scatter(n, rule) {
    var pts = [], tries = 0, max = n * 40;
    while (pts.length < n && tries < max) {
      tries++;
      var x = -3300 + R() * 6600, z = -3400 + R() * 6800;
      if (excluded(x, z)) continue;
      var p = rule(x, z, H(x, z));
      if (p) pts.push(p);
    }
    return pts;
  }
  var giantPts = [];
  function scatterGiants(n, minD, rule, anchors) {
    var pts = [];
    function far(x, z) { for (var i = 0; i < giantPts.length; i++) { var dx = x - giantPts[i].x, dz = z - giantPts[i].z; if (dx * dx + dz * dz < minD * minD) return false; } return true; }
    (anchors || []).forEach(function (a) {
      if (pts.length >= n || excluded(a[0], a[1]) || !far(a[0], a[1])) return;
      var p = rule(a[0], a[1], H(a[0], a[1])); if (p) { pts.push(p); giantPts.push(p); }
    });
    var tries = 0;
    while (pts.length < n && tries < n * 300) {
      tries++;
      var x = -3200 + R() * 6400, z = -3300 + R() * 6600;
      if (excluded(x, z) || !far(x, z)) continue;
      var p2 = rule(x, z, H(x, z));
      if (p2) { pts.push(p2); giantPts.push(p2); }
    }
    return pts;
  }
  function note(kind, list, k) { samples[kind] = list.slice(0, k || 8).map(function (p) { return { x: Math.round(p.x), z: Math.round(p.z), y: Math.round(p.y * 10) / 10 }; }); }

  var dummy = new T.Object3D(), col = new T.Color();
  function instanced(name, geo, mat, list, place, colors) {
    var m = new T.InstancedMesh(geo, mat, Math.max(1, list.length));
    m.name = name; m.count = list.length;
    for (var i = 0; i < list.length; i++) { place(dummy, list[i], i); dummy.updateMatrix(); m.setMatrixAt(i, dummy.matrix); if (colors) { colors(col, list[i], i); m.setColorAt(i, col); } }
    if (m.instanceColor) m.instanceColor.needsUpdate = true;
    m.frustumCulled = false; group.add(m); return m;
  }
  function lambert(c, extra) { var p = { color: c }; if (extra) for (var k in extra) p[k] = extra[k]; return new T.MeshLambertMaterial(p); }
  function sway(mat, base) {
    mat.onBeforeCompile = function (sh) {
      sh.uniforms.uTime = uTime;
      sh.vertexShader = 'uniform float uTime;\n' + sh.vertexShader.replace('#include <begin_vertex>', '#include <begin_vertex>\n float bendK = max(position.y + ' + base + ', 0.0);\n transformed.x += bendK * bendK * 0.30;\n#ifdef USE_INSTANCING\n vec4 ipos = instanceMatrix * vec4(0.0, 0.0, 0.0, 1.0);\n float swy = sin(uTime * 1.7 + ipos.x * 0.35 + ipos.z * 0.41) * 0.055 * (position.y + ' + base + ');\n transformed.x += swy; transformed.z += swy * 0.6;\n#endif');
    };
    mat.customProgramCacheKey = function () { return 'nature-sway-' + base; };
    return mat;
  }
  function greens(c, base, spread) { var g = base + CR() * spread; c.setHSL(0.26 + CR() * 0.05, 0.5, g); }

  /* THE WHOLE-SHAPE RULE (learned from the King's photos,
   * 2026-10-14): icosahedra are NON-INDEXED — every face
   * owns its vertices — so ALL shaping must be a function
   * of POSITION, never of the vertex stream, or coincident
   * corners take different shapes and the form EXPLODES
   * into shards (measured: a 43% tear). Every shaper below
   * is a pure function of position; the factory measures
   * its own tear and reports it, so the gate can hold it.
   *
   * THE BEND LORE (researched on the King's word — "codes
   * that have lore dimensions that stretch and bend them",
   * and "filling spaces in between creating dimensions of
   * leaves and shapes"; studies filed in MEASUREMENTS.md):
   * · FBM — fractal Brownian motion: noise layered at
   *   doubling frequency and halving amplitude gives a form
   *   detail at EVERY scale at once, the way real surfaces
   *   roughen — one octave reads as lumps, three read as
   *   nature. Displacement runs along the surface normal:
   *   position + normal × fbm(position × freq) × amp.
   * · TRUNKS FROM CURVES: real tree codes grow the trunk
   *   along a spline and distort the spline with noise;
   *   ours bakes the curve into the geometry — offset
   *   growing with the SQUARE of height, so the foot stays
   *   planted and the crown end wanders.
   * · CANOPY LOBES: a crown is not a ball — leaf masses
   *   cluster into LOBES with sky betwee