<!-- SOLID CITY · part 2818 -->
am, or coincident
   * corners take different scales and the stone EXPLODES
   * into shards (measured: a 43% tear). Position-hashed
   * jitter moves every coincident corner as one: the stone
   * stays whole, and crystallized. The factory measures
   * its own tear and reports it, so the gate can hold it. */
  function hashJitter(geo, amt, seed) {
    var p = geo.attributes.position, v = new T.Vector3();
    var before = [];
    for (var i = 0; i < p.count; i++) { v.fromBufferAttribute(p, i); before.push([v.x, v.y, v.z]); }
    for (var j = 0; j < p.count; j++) {
      v.fromBufferAttribute(p, j);
      var n = Math.sin(v.x * 127.1 + v.y * 311.7 + v.z * 74.7 + seed) * 43758.5453;
      var s = 1 - amt / 2 + (n - Math.floor(n)) * amt;
      p.setXYZ(j, v.x * s, v.y * s * 0.84, v.z * s);
    }
    geo.computeVertexNormals();
    var 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;
  }
  var integrity = {};
  /* ——— STONES ———
   * Reason for the sizes: real boulder fields are a power
   * law — a hundred pebbles for every boulder. Scale runs
   * 0.25–1.85 m on the same curve (pow 2.2), so the eye
   * meets mostly small stones and, rarely, a giant. */
  var stoneGeo = new T.IcosahedronGeometry(1, 1);
  integrity.stoneTear = hashJitter(stoneGeo, 0.5, 7.7);
  var stones = scatter(hiTier ? 850 : 500, function (x, z, h) { if (h < 2.2) return null; return { x: x, z: z, y: h, ry: R() * 6.28, s: 0.25 + Math.pow(R(), 2.2) * 1.6 }; });
  counts.stones = stones.length; note('stones', stones);
  instanced('stones', stoneGeo, lambert(0xffffff, { flatShading: true }), stones,
    function (d, p) { d.position.set(p.x, p.y + p.s * 0.18, p.z); d.rotation.set(0, p.ry, 0); d.scale.set(p.s, p.s * 0.8, p.s); },
    function (c) { var g = 0.26 + CR() * 0.18; c.setRGB(g * 1.05, g, g * 0.94); });

  /* ——— GRASS: meadow-clustered, wind in its bones ——— */
  var meadowC = []; for (var ci = 0; ci < 70; ci++) meadowC.push({ x: -3200 + R() * 6400, z: -3300 + R() * 6600 });
  function meadowy(x, z) { for (var i = 0; i < meadowC.length; i++) { var dx = x - meadowC[i].x, dz = z - meadowC[i].z; if (dx * dx + dz * dz < 900) return true; } return false; }
  var grassGeo = new T.ConeGeometry(0.17, 0.6, 5); grassGeo.translate(0, 0.28, 0);
  var grass = scatter(hiTier ? 6500 : 2600, function (x, z, h) {
    if (h < 2.5 || h > 140 || slope(x, z, h) > 0.55) return null;
    if (!meadowy(x, z) && R() > 0.30) return null;
    /* blade reason: meadow grass stands 0.1–0.5 m; the
     * tuft cone is 0.6 m at scale 1, so scale 0.6–1.25
     * keeps every blade inside the living range */
    return { x: x, z: z, y: h, ry: R() * 6.28, s: 0.6 + R() * 0.65 };
  });
  counts.grass = grass.length; note('grass', grass);
  instanced('grass', grassGeo, sway(lambert(0xffffff), '0.0'), grass,
    function (d, p) { d.position.set(p.x, p.y - 0.03, p.z); d.rotation.set(0, p.ry, 0); d.scale.setScalar(p.s); },
    function (c) { greens(c, 0.20, 0.16); });

  /* ——— FLOWERS: stems in the wind, heads of colour ——— */
  var flowers = scatter(hiTier ? 850 : 380, function (x, z, h) {
    if (h < 2.5 || h > 120 || slope(x, z, h) > 0.5) return null;
    if (!meadowy(x, z) && R() > 0.45) return null;
    return { x: x, z: z, y: h, ry: R() * 6.28, s: 0.8 + R() * 0.5 };
  });
  counts.flowers = flowers.length; note('flowers', flowers);
  var stemGeo = new T.CylinderGeometry(0.012, 0.02, 0.34, 5); stemGeo.translate(0, 0.17, 0);
  instanced('flower-stems', stemGeo, sway(lambert(0x3d6b2f), '0.0'), flowers,
    function (d, p) { d.position.set(p.x, p.y - 0.02, p.z); d.rotation.set(0, p.ry, 0); d.scale.setScalar(p.s); }, null);
  var headGeo = new T.IcosahedronGeometry(0.06, 0);
  var PALETTE = [0xd84a5a, 0xe8b13c, 0x9a6ae0, 0xf2ead8, 0xe07ab0, 0xe2703a];
  instanced('flower-heads', headGeo, sway(lambert(0xffffff), '0.36'), flowers,
    function (d, p) { d.position.set(p.x, p.y + 0.36 * p.s, p.z); d.rotation.set(0, p.ry, 0); d.scale.setScalar(p.s); },
    function (c) { c.setHex(PALETTE[Math.floor(CR() * PALETTE.length)]); });

  /* ——— BUSHES: squashed crystal blobs of green ——— */
  var bushGeo = new T.IcosahedronGeometry(1, 1); bushGeo.scale(1, 0.62, 1);
  var bushes = scatter(hiTier ? 480 : 260, function (x, z, h) { if (h < 2.5 || h > 170 || slope(x, z, h) > 0.85) return null; return { x: x, z: z, y: h, ry: R() * 6.28, s: 0.5 + R() * 1.1 }; });
  counts.bushes = bushes.length; note('bushes', bushes);
  instanced('bushes', bushGeo, lambert(0xffffff, { flatShading: true }), bushes,
    function (d, p) { d.position.set(p.x, p.y + p.s * 0.3, p.z); d.rotation.set(0, p.ry, 0); d.scale.setScalar(p.s); },
    function (c) { greens(c, 0.16, 0.12); });

  /* ——— PALMS ———
   * Researched (USDA + Bishop Mu