<!-- SOLID CITY · part 2447 -->
n of world + bird state:
   * the bird does not know it is in a demonstration. */
  SpatialWorld.prototype.evaluateResponse = function (bird, encounter, destPos) {
    var obs = encounter.obstacle;
    var energy = bird.needs ? bird.needs.energy : 0.8;

    // --- AVOID ---
    var corridor = this.findCorridor(bird, obs, destPos);
    var avoidScore = 0, avoidReason = 'no safe corridor';
    if (corridor) {
      avoidScore = clamp(0.92 - (corridor.detourRatio - 1) * 0.55 - (1 - energy) * 0.12, 0, 1);
      avoidReason = 'corridor open, detour x' + corridor.detourRatio.toFixed(2);
    }

    // --- LAND ---
    var landScore = 0, landSurface = null, landDetail = null, landReason = 'no suitable surface';
    var candidates = [];
    if (obs.topSurfaceId) candidates.push(this.surfaceById(obs.topSurfaceId));
    for (var i = 0; i < this.surfaces.length; i++) {
      var sf = this.surfaces[i];
      if (candidates.indexOf(sf) < 0 && sf.obstacleId === obs.id) candidates.push(sf);
    }
    for (i = 0; i < candidates.length; i++) {
      var surf = candidates[i]; if (!surf) continue;
      var sc = this.scoreSurface(bird, surf);
      if (!sc) continue;
      // approach: bird must be able to reach a staging point above the surface
      var stage = V3(surf.center.x, surf.center.y + 1.1, surf.center.z);
      var from = bird.group.position.clone();
      var approachOk = this.isSegmentClear(from, stage, bird.clearanceRadius() * 0.7) || bird.group.position.y > surf.center.y + 0.5;
      var total = sc.total * (approachOk ? 1 : 0.35);
      if (total > landScore) { landScore = total; landSurface = surf; landDetail = sc; landReason = 'surface ' + surf.id + ' scored ' + total.toFixed(2); }
    }
    // A bird in good shape values CONTINUING its journey; a tired bird
    // values a landing. The surface's raw quality is discounted by need —
    // this is what stops a fresh bird abandoning its route for every rock.
    var landEff = landScore * (0.55 + 0.45 * (1 - energy));
    var avoidEff = avoidScore + (energy > 0.5 ? 0.12 : 0);

    // --- RETREAT --- always available, unglamorous, safe.
    var retreatScore = 0.34 + ((avoidScore < 0.4 && landEff < 0.4) ? 0.25 : 0);

    var action, reason;
    if (avoidScore < 0.25 && landEff < 0.25) {
      action = 'RETREAT'; reason = 'neither corridor nor surface is safe';
    } else if (landSurface && landEff > avoidEff + 0.05) {
      action = 'LAND'; reason = landReason + ' (avoid ' + avoidEff.toFixed(2) + ' vs land ' + landEff.toFixed(2) + ')';
    } else if (avoidEff >= landEff && corridor) {
      action = 'AVOID'; reason = avoidReason;
    } else if (landSurface) {
      action = 'LAND'; reason = landReason;
    } else {
      action = 'RETREAT'; reason = 'fallback: no viable corridor or surface';
    }
    return {
      action: action, obstacle: obs, corridor: corridor, surface: landSurface,
      scores: { avoid: avoidEff, land: landEff, retreat: retreatScore },
      surfaceDetail: landDetail, reason: reason
    };
  };

  /* A fallback destination for RETREAT: nearest safe surface/spot that is
   * NOT beyond the blocking obstacle. */
  SpatialWorld.prototype.retreatDestination = function (bird, obs) {
    var pos = bird.group.position, best = null, bestD = Infinity, i, s;
    for (i = 0; i < this.surfaces.length; i++) {
      s = this.surfaces[i];
      if (s.obstacleId === obs.id) continue;
      if (!this.scoreSurface(bird, s)) continue;
      var d = pos.distanceTo(s.center);
      if (d < bestD && this.isSegmentClear(pos, s.center, bird.clearanceRadius() * 0.8)) { bestD = d; best = { kind: 'surface', surface: s }; }
    }
    if (best) return best;
    for (i = 0; i < this.groundSpots.length; i++) {
      var g = this.groundSpots[i];
      var dg = pos.distanceTo(g.pos);
      if (dg < bestD) { bestD = dg; best = { kind: 'ground', spot: g }; }
    }
    return best;
  };

  /* ================================================================
   * THE REFERENCE HABITAT (Part VIII) — one tree with real branch
   * perches, open ground, a landable stone pillar on the tree-meadow
   * line, a long barn with a steep roof (a surface the evaluation must
   * REJECT), a snag pole, two meadows. Geometry doubles as scene hints.
   * ================================================================ */
  function createHabitatWorld() {
    var def = {
      bounds: { cx: 0, cz: 0, radius: 34 },
      obstacles: [
        { id: 'tree-trunk', kind: 'cylinder', x: 8, z: -6, r: 0.42, topY: 4.6, material: 'wood', topSurfaceId: null },
        { id: 'pillar', kind: 'cylinder', x: 0, z: 2, r: 0.9, topY: 3.4, material: 'stone', topSurfaceId: 'pillar-top' },
        { id: 'snag', kind: 'cylinder', x: -10, z: -8, r: 0.22, topY: 3.1, material: 'wood', topSurfaceId: 'snag-top' },
        { id: 'barn', kind: 'box', minX: -16, maxX: -2, minZ: 9, maxZ: 13, topY: 5.2, material: 'metal', topSurfaceId: 'barn-roof' }
      ],
      surfaces: [
        // tree 