<!-- SOLID CITY · part 2816 -->
(1); }, null);
  instanced('banyan-canopies', banCanGeo, lambert(0xffffff, { flatShading: true }), banyans,
    function (d, p) { d.position.set(p.x, p.y + p.ch, p.z); d.rotation.set(0, p.ry, 0); d.scale.set(p.cr, p.cr * 0.34, p.cr); },
    function (c) { greens(c, 0.17, 0.09); });
  /* lobes between the pillars: the banyan's crown is a
   * CITY of leaf masses — satellites fill the spans the
   * prop roots hold up, each above its own pillar ring */
  var banLobes = [];
  banyans.forEach(function (p) {
    for (var li = 0; li < 6; li++) {
      var la4 = (li / 6) * 6.283 + R() * 0.6, lr4 = p.cr * (0.55 + R() * 0.25);
      banLobes.push({ x: p.x + Math.cos(la4) * lr4, z: p.z + Math.sin(la4) * lr4, y: p.y + p.ch - p.cr * 0.10, s: p.cr * (0.34 + R() * 0.12), ry: R() * 6.28 });
    }
  });
  instanced('banyan-lobes', banCanGeo, lambert(0xffffff, { flatShading: true }), banLobes,
    function (d, p) { d.position.set(p.x, p.y, p.z); d.rotation.set(0, p.ry, 0); d.scale.set(p.s, p.s * 0.4, p.s); },
    function (c) { greens(c, 0.16, 0.09); });
  var props = [], hangers = [];
  banyans.forEach(function (p) {
    var n = 6 + Math.floor(R() * 3);
    for (var i = 0; i < n; i++) {
      var a = (i / n) * 6.283 + R() * 0.5, rr = p.cr * (0.55 + R() * 0.38);
      var rx = p.x + Math.cos(a) * rr, rz = p.z + Math.sin(a) * rr;
      var gy = H(rx, rz), topY = p.y + p.ch - 0.4, len = topY - gy;
      if (len > 2) props.push({ x: rx, z: rz, yMid: gy + len / 2, len: len, s: 0.30 + R() * 0.16 });
    }
    for (var j = 0; j < 4; j++) {
      var a2 = R() * 6.283, rr2 = p.cr * (0.3 + R() * 0.5), hl = 1.8 + R() * 2.4;
      hangers.push({ x: p.x + Math.cos(a2) * rr2, z: p.z + Math.sin(a2) * rr2, yMid: p.y + p.ch - 0.6 - hl / 2, len: hl, s: 0.055 });
    }
  });
  counts.props = props.length; counts.hangers = hangers.length;
  var propGeo = new T.CylinderGeometry(0.5, 0.68, 1, 6);
  var propMat = lambert(0x5d4630);
  instanced('banyan-props', propGeo, propMat, props,
    function (d, p) { d.position.set(p.x, p.yMid, p.z); d.rotation.set(0, 0, 0); d.scale.set(p.s, p.len, p.s); }, null);
  instanced('banyan-hangers', propGeo, propMat, hangers,
    function (d, p) { d.position.set(p.x, p.yMid, p.z); d.rotation.set(0, 0, 0); d.scale.set(p.s, p.len, p.s); }, null);

  /* FALLING LEAVES (Dirksen's particles chapter: drive
   * the vertices directly) — a points cloud seeded under
   * the great crowns; each leaf's fall is computed in the
   * vertex shader from its own seed: a slow cycle down,
   * swirling wider as it drops. No CPU per frame; the
   * wind uniform is the only clock. */
  var fallingLeaves = 0;
  (function () {
    if (!leafSpawns.length) return;
    var n = hiTier ? 260 : 130;
    var pos = new Float32Array(n * 3), seed = new Float32Array(n), hh = new Float32Array(n);
    for (var i = 0; i < n; i++) {
      var sp = leafSpawns[Math.floor(R() * leafSpawns.length)], a = R() * 6.28, rr = R() * R() * 3.4;
      pos[i * 3] = sp.x + Math.cos(a) * rr; pos[i * 3 + 1] = sp.y; pos[i * 3 + 2] = sp.z + Math.sin(a) * rr;
      seed[i] = R(); hh[i] = 4 + R() * 5;
    }
    var g = new T.BufferGeometry();
    g.setAttribute('position', new T.BufferAttribute(pos, 3));
    g.setAttribute('aSeed', new T.BufferAttribute(seed, 1));
    g.setAttribute('aH', new T.BufferAttribute(hh, 1));
    var m = new T.PointsMaterial({ color: 0x86b04c, size: 0.34, sizeAttenuation: true, transparent: true, opacity: 0.9 });
    m.onBeforeCompile = function (sh) {
      sh.uniforms.uTime = uTime;
      sh.vertexShader = 'uniform float uTime;\nattribute float aSeed;\nattribute float aH;\n' + sh.vertexShader.replace('#include <begin_vertex>',
        '#include <begin_vertex>\n float fallK = fract(aSeed * 7.31 + uTime * 0.045 * (0.7 + aSeed));\n transformed.y -= fallK * aH;\n transformed.x += sin(uTime * 1.2 + aSeed * 43.7 + fallK * 8.0) * (0.4 + fallK * 0.9);\n transformed.z += cos(uTime * 0.9 + aSeed * 31.3 + fallK * 6.0) * (0.3 + fallK * 0.7);');
    };
    m.customProgramCacheKey = function () { return 'falling-leaves'; };
    var pts = new T.Points(g, m); pts.frustumCulled = false; pts.name = 'falling-leaves'; group.add(pts);
    fallingLeaves = n;
  })();

  group.userData = { counts: counts, samples: samples, integrity: integrity, fallingLeaves: fallingLeaves, tier: opts.tier || 'hi', tick: function (t) { uTime.value = t; } };
  return group;
}


===== FILE: public/city/nature.js.bak-bend-20261014 =====
/* ============================================================
 * EPISTLE — THE NATURE LAYER
 * To the King, from his General —
 *
 * His word: "create stones now and lace the whole map with
 * them — we need grasses too — flows and bushes and trees
 * of all kinds. Make huge trees: look up monkey pod and
 * banyan."
 *
 * So the wild things come, and they come the way the whole
 * world is now built: measured first (the land is scanned —
 * slope, height, and the paved places refused), instanced
 * (each kind is ONE d