<!-- SOLID CITY · part 2827 -->
3D(), 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 between them, and the
   *   lobes together fill the crown's volume. Each tree
   *   below carries a heart lobe + satellite lobes.
   * All of it is BAKED at build (the banyan builders'
   * phone lesson: noise evaluated per-frame starves a
   * phone; baked, it costs nothing forever). */
  function vhash(x, y, z) { var n = Math.sin(x * 127.1 + y * 311.7 + z * 74.7) * 43758.5453; return n - Math.floor(n); }
  function vnoise(x, y, z) {
    var xi = Math.floor(x), yi = Math.floor(y), zi = Math.floor(z);
    var xf = x - xi, yf = y - yi, zf = z - zi;
    var u = xf * xf * (3 - 2 * xf), v = yf * yf * (3 - 2 * yf), w = zf * zf * (3 - 2 * zf);
    function L(a, b, t) { return a + (b - a) * t; }
    return L(
      L(L(vhash(xi, yi, zi), vhash(xi + 1, yi, zi), u), L(vhash(xi, yi + 1, zi), vhash(xi + 1, yi + 1, zi), u), v),
      L(L(vhash(xi, yi, zi + 1), vhash(xi + 1, yi, zi + 1), u), L(vhash(xi, yi + 1, zi + 1), vhash(xi + 1, yi + 1, zi + 1), u), v), w) * 2 - 1;
  }
  function fbm(x, y, z, oct) {
    var val = 0, amp = 1, sum = 0, fx = x, fy = y, fz = z;
    for (var o = 0; o < oct; o++) { val += vnoise(fx, fy, fz) * amp; sum += amp; amp *= 0.5; fx *= 2.03; fy *= 2.03; fz *= 2.03; }
    return val / sum;
  }
  function tearOf(geo, before) {
    var p = geo.attributes.position, v = new T.Vector3(), seen = {}, tear = 0;
    for (var k = 0; k < p.count; k++) {
      var key = before[k].map(function (q) { return q.toFixed(3); }).join(',');
      v.fromBufferAttribute(p, k);
      if (seen[key]) tear = Math.max(tear, v.distanceTo(seen[key])); else seen[key] = v.clone();
    }
    return tear;
  }
  function snapshot(geo) { var p = geo.attributes.position, v = new T.Vector3(), out = []; for (var i = 0; i < p.count; i++) { v.fromBufferAttribute(p, i); out.push([v.x, v.y, v.z]); } return out; }
  /* displace along the radial direction by fbm — the
   * organic lump-maker; squashY keeps crowns wide */
  function fbmDisplace(geo, freq, amp, seed, squashY) {
    var before = snapshot(geo);
    var p = geo.attributes.position, v = new T.Vector3();
    for (var i = 0; i < p.count; i++) {
   