<!-- SOLID CITY · part 2460 -->
Lost Wings
 * engine (species-registry.js + bird-engine.js): anatomical models, blended
 * state machines, and the scored AVOID / LAND / RETREAT obstacle judgment.
 * Local frame: origin at the clearing centre, y=0 at clearing level.
 * The placer (lost-wings-place.js) positions the group and registers
 * collision / standY / radar / Hyper POI / Watch.
 */
'use strict';

function V3(x, y, z) { return new THREE.Vector3(x, y, z); }

export function createGrove(deps) {
  var THREE = deps.THREE;
  var Eng = window.LostWings.Engine;
  var group = new THREE.Group();
  group.name = 'lost-wings-grove';

  /* ---------------- world model: canonical habitat + park shelter ------- */
  var world = Eng.createHabitatWorld();
  world.obstacles.push({ id: 'shelter', type: 'box', cx: -7.5, cz: -7, hx: 1.9, hz: 1.4, baseY: 0, topY: 2.56, topSurfaceId: 'shelter-roof' });
  world.surfaces.push({
    id: 'shelter-roof', obstacleId: 'shelter', kind: 'rooftop', pos: V3(-7.5, 2.58, -7),
    center: V3(-7.5, 2.62, -7),
    up: V3(0, 1, 0), angleDeg: 0, radiusM: 1.15, stability: 0.9, approachClear: 2.4, occupant: null
  });

  /* ---------------- materials & shared geometry ------------------------- */
  function lam(c, o) { var m = new THREE.MeshLambertMaterial({ color: c }); if (o && o.opacity != null) { m.transparent = true; m.opacity = o.opacity; } return m; }
  var MAT = {
    earth: lam(0x4a5a34), earthDark: lam(0x3c4a2c), grass: lam(0x56713a),
    trunk: lam(0x5d4630), bark: lam(0x4c3826), leaf: lam(0x4e7d33), leaf2: lam(0x639548),
    stone: lam(0x8d8578), stoneDark: lam(0x6d675c), gold: lam(0xd8a833),
    wood: lam(0x77603f), woodDark: lam(0x54432c), roof: lam(0x74513a),
    red: lam(0x8e2f35), cream: lam(0xe9e2cf), dark: lam(0x2b2b30)
  };
  var BOX = new THREE.BoxGeometry(1, 1, 1), CYL = new THREE.CylinderGeometry(0.5, 0.5, 1, 10),
    SPH = new THREE.SphereGeometry(0.5, 10, 8), CONE = new THREE.ConeGeometry(0.5, 1, 10);
  function mesh(geo, mat, parent, x, y, z, sx, sy, sz, ry) {
    var m = new THREE.Mesh(geo, mat); m.position.set(x, y, z); m.scale.set(sx, sy, sz);
    if (ry) m.rotation.y = ry; parent.add(m); return m;
  }

  /* ---------------- the leveled clearing ------------------------------- */
  var disc = new THREE.Mesh(new THREE.CircleGeometry(40, 40), MAT.earth);
  disc.rotation.x = -Math.PI / 2; disc.position.y = 0.02; group.add(disc);
  var skirt = new THREE.Mesh(new THREE.CylinderGeometry(40, 47, 4.5, 40, 1, true), MAT.earthDark);
  skirt.position.y = -2.24; group.add(skirt);
  // mown inner meadow + forage patch tint
  var meadow = new THREE.Mesh(new THREE.CircleGeometry(24, 32), MAT.grass);
  meadow.rotation.x = -Math.PI / 2; meadow.position.y = 0.035; group.add(meadow);
  var forage = new THREE.Mesh(new THREE.CircleGeometry(3.2, 20), lam(0x6d8140));
  forage.rotation.x = -Math.PI / 2; forage.position.set(1.5, 0.045, -3.5); group.add(forage);
  // rim stones
  for (var ri = 0; ri < 30; ri++) {
    var ra = (ri / 30) * Math.PI * 2, rr = 39.2 + ((ri * 37) % 10) / 14;
    mesh(SPH, ri % 3 ? MAT.stone : MAT.stoneDark, group, Math.cos(ra) * rr, 0.16, Math.sin(ra) * rr, 0.7 + (ri % 4) * 0.22, 0.42, 0.7 + (ri % 5) * 0.18);
  }
  // grass tufts
  for (var gi = 0; gi < 44; gi++) {
    var ga = ((gi * 137) % 360) * Math.PI / 180, gr = 6 + ((gi * 53) % 30);
    var gx = Math.cos(ga) * gr, gz = Math.sin(ga) * gr;
    if (Math.hypot(gx + 9, gz - 11) < 8.5) continue;                       // keep the barn apron clear
    mesh(CONE, gi % 2 ? MAT.grass : MAT.leaf2, group, gx, 0.22, gz, 0.55, 0.5 + (gi % 3) * 0.14, 0.55);
  }
  // rim trees — the grove's living edge (outside every flight line)
  var rimTrees = [[30, -20], [-26, -24], [-33, 12], [24, 26], [4, 35]];
  rimTrees.forEach(function (p, i) {
    mesh(CYL, MAT.trunk, group, p[0], 1.7, p[1], 0.8, 3.4, 0.8);
    mesh(SPH, i % 2 ? MAT.leaf : MAT.leaf2, group, p[0], 4.3, p[1], 5.6, 3.4, 5.6);
    mesh(SPH, MAT.leaf2, group, p[0] + 1.4, 3.6, p[1] - 0.8, 3.8, 2.6, 3.8);
  });

  /* ---------------- habitat structures (match the world model) -------- */
  // the perch tree at (8,-6) — trunk + canopy + the three branch arms
  mesh(CYL, MAT.trunk, group, 8, 2.3, -6, 0.84, 4.6, 0.84);
  mesh(SPH, MAT.leaf, group, 8, 5.6, -6, 6.4, 3.0, 6.4);
  mesh(SPH, MAT.leaf2, group, 6.8, 4.9, -5.2, 4.2, 2.2, 4.2);
  mesh(SPH, MAT.leaf, group, 9.6, 5.1, -7, 4.0, 2.2, 4.0);
  function branchMesh(x1, y1, z1, x2, y2, z2) {
    var a = V3(x1, y1, z1), b = V3(x2, y2, z2), mid = a.clone().add(b).multiplyScalar(0.5);
    var len = a.distanceTo(b);
    var m = new THREE.Mesh(CYL, MAT.bark);
    m.position.copy(mid); m.scale.set(0.17, len, 0.17);
    m.quaternion.setFromUnitVectors(V3(0, 1, 0), b.sub(a).normalize());
    group.add(m);
  }
  branchMesh(8, 3.2, -6, 6.55, 3.52, -5.05);
  branchMesh(8, 3.5, -6, 9.6, 4.02, -7.05);
  branchMesh(8, 2.7, -6, 7.35, 2.92, -7.5);
  // the stone pillar at (0,2) — the obstacle with a landable crown
  mesh(CYL, MAT.stone, gro