<!-- SOLID CITY · part 2826 -->
rue, 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-hero-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 draw for ten thousand bodies), woven in
 * the two cloths (the strong phone gets the full lace, the
 * struggling phone a lighter weave of the same design), and
 * ALIVE in time — the grass and the flower stems carry a
 * wind in their vertex shader, one uniform, no per-frame
 * cost on the CPU.
 *
 * The giants are true to their study: the MONKEY POD is a
 * short massive trunk under a symmetrical UMBRELLA crown
 * (the books say 15–25 m tall, 30 m across; Humboldt
 * measured one near 60 m wide). The BANYAN sends AERIAL
 * ROOTS down from its branches; where they ground they
 * thicken into PILLAR trunks, and the tree walks outward
 * forever — a forest of one body.
 *
 * Nature is decor: it carries no collision and no standing
 * — feet pass through grass as they should. The paved
 * hearts (plaza, terrace, first city, spaceport, the
 * village green, and the Servitude's own digging) are
 * refused by the scatter, by measurement.
 * ============================================================ */
function mulberry(seed) { var a = seed >>> 0; return function () { a |= 0; a = (a + 0x6D2B79F5) | 0; var t = Math.imul(a ^ (a >>> 15), 1 | a); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }

export function createNature(opts) {
  var T = opts.THREE, 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.Object