<!-- SOLID CITY · part 3437 -->
OWN', x: -1016, z: -624 }, { name: 'SOUTH LOT', x: -1300, z: -498 },
  { name: 'LONG ROAD JUNCTION', x: -672, z: -498 }, { name: 'FIRST CITY PODIUM', x: -1300, z: -1000 }
];
var C = { panel: null, body: null, curb: false, curbT0: 0, curbLast: 0, widths: [], curbDist: 0, lastPos: null, history: [] };

function bearing(dx, dz) { var b = Math.atan2(dx, -dz) * 180 / Math.PI; return (b + 360) % 360; }
function compass(b) { return ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'][Math.round(b / 45) % 8]; }
function fmt(n, d) { return n == null || !isFinite(n) ? '—' : (+n).toFixed(d == null ? 1 : d); }
/* ray vs the collision registry (2D slab test; the solid must span the body) */
function stopAt(ox, oz, dx, dz, py) {
  var best = Infinity, solids = A.solids || [];
  for (var i = 0; i < solids.length; i++) {
    var q = solids[i];
    if (!(q.y0 < py + 1.8 && q.y1 > py + 0.3)) continue;
    var t0 = 0, t1 = FAR, ok = true;
    if (Math.abs(dx) < 1e-9) { if (ox < q.x0 || ox > q.x1) ok = false; }
    else { var ta = (q.x0 - ox) / dx, tb = (q.x1 - ox) / dx; if (ta > tb) { var tt = ta; ta = tb; tb = tt; } if (ta > t0) t0 = ta; if (tb < t1) t1 = tb; }
    if (ok) {
      if (Math.abs(dz) < 1e-9) { if (oz < q.z0 || oz > q.z1) ok = false; }
      else { var tc = (q.z0 - oz) / dz, td = (q.z1 - oz) / dz; if (tc > td) { var tu = tc; tc = td; td = tu; } if (tc > t0) t0 = tc; if (td < t1) t1 = td; }
    }
    if (ok && t1 >= Math.max(t0, 0) && t0 > 0.05 && t0 < best) best = t0;
  }
  return best === Infinity ? null : best;
}
function visibleAt(ox, oy, oz, dx, dz) {
  if (!T || !T.Raycaster || !A.scene) return null;
  try {
    var rc = new T.Raycaster(new T.Vector3(ox, oy, oz), new T.Vector3(dx, 0, dz).normalize(), 0.1, FAR);
    var hits = rc.intersectObjects(A.scene.children, true);
    for (var i = 0; i < hits.length; i++) { if (hits[i].distance > 0.3) return hits[i].distance; }
  } catch (e) {}
  return null;
}
function fixes(ox, oz, maxD) {
  var out = [];
  function add(name, x, z) { var dx = x - ox, dz = z - oz, d = Math.sqrt(dx * dx + dz * dz); if (d <= maxD) out.push({ name: name, d: d, b: bearing(dx, dz) }); }
  for (var i = 0; i < BENCH.length; i++) add(BENCH[i].name, BENCH[i].x, BENCH[i].z);
  var pois = A.POIS || [];
  for (var j = 0; j < pois.length; j++) { var p = pois[j]; if (p && isFinite(p.x) && isFinite(p.z)) add(p.name || p.title || p.id || 'POI', p.x, p.z); }
  out.sort(function (a, b) { return a.d - b.d; });
  var seen = {}, ded = [];
  for (var k = 0; k < out.length; k++) { var key = out[k].name + '@' + Math.round(out[k].d); if (!seen[key]) { seen[key] = 1; ded.push(out[k]); } }
  return ded.slice(0, 8);
}
function standingAt(ox, oz, py) {
  var best = A.groundAt ? A.groundAt(ox, oz) : null, who = 'terrain';
  var mods = [['CityWardHouses', 'the ward registry'], ['CityWards', 'the ward paths'], ['CityFirstParking', 'the First City registry'], ['CitySkyward', 'the sky registry'], ['CityJubilee', 'the jubilee registry']];
  for (var i = 0; i < mods.length; i++) {
    var m = window[mods[i][0]];
    if (m && typeof m.standY === 'function') {
      try { var v = m.standY(ox, oz, py); if (v != null && v > -999 && (best == null || v > best)) { best = v; who = mods[i][1]; } } catch (e) {}
    }
  }
  return { y: best, who: who };
}
function scan() {
  var ox = S.p.x, oz = S.p.z, py = S.p.y;
  var nMatch = 0, nMis = 0, nVisOnly = 0, nColOnly = 0, nOpen = 0, worst = null, nearest = null, nearVis = null, nearCol = null;
  for (var i = 0; i < RAYS; i++) {
    var a = i / RAYS * Math.PI * 2, dx = Math.sin(a), dz = -Math.cos(a);
    var stop = stopAt(ox, oz, dx, dz, py);
    var vis = visibleAt(ox, py + 1.6, oz, dx, dz);
    if (stop != null && (!nearest || stop < nearest.d)) nearest = { d: stop, b: bearing(dx, dz) };
    if (stop != null && vis != null) {
      var gap = vis - stop;
      if (Math.abs(gap) > GAP_TOL) { nMis++; if (!worst || Math.abs(gap) > Math.abs(worst.gap)) worst = { gap: gap, b: bearing(dx, dz), vis: vis, stop: stop }; }
      else nMatch++;
    } else if (vis != null) { nVisOnly++; if (!nearVis || vis < nearVis.d) nearVis = { d: vis, b: bearing(dx, dz) }; }
    else if (stop != null) { nColOnly++; if (!nearCol || stop < nearCol.d) nearCol = { d: stop, b: bearing(dx, dz) }; }
    else nOpen++;
  }
  var st = standingAt(ox, oz, py);
  var fx = fixes(ox, oz, 400);
  var line = 'SURVEY — (' + fmt(ox, 0) + ', ' + fmt(py, 1) + ', ' + fmt(oz, 0) + ') · standing ' + fmt(st.y) + ' (' + st.who + '; terrain ' + fmt(A.groundAt ? A.groundAt(ox, oz) : null) + ')' +
    ' · nearest stop ' + (nearest ? fmt(nearest.d) + ' m ' + compass(nearest.b) : 'none within ' + FAR + ' m') +
    ' · rays ' + RAYS + ': match ' + nMatch + ' · mismatch ' + nMis + (worst ? ' (worst ' + fmt(worst.gap, 2) + ' m at ' + compass(worst.b) + ' — seen ' + fmt(worst.vis) + ' / stops ' + fmt(worst.stop) + ')' : '') +
    ' · VISUAL ONLY ' + nVisOnly + (nearVis ? ' (nearest ' + fmt(nearVis.d) + ' m ' + compas