<!-- SOLIDIVISM · part 66 -->

  return best;
}

/* ---------------- hover / focus raycast ------------------------------ */
const raycaster = new THREE.Raycaster();
const labelEl = document.getElementById('label');
function pickAt(px, py, click) {
  if (mode !== 'street') return;
  const ndc = new THREE.Vector2((px / innerWidth) * 2 - 1, -(py / innerHeight) * 2 + 1);
  raycaster.setFromCamera(ndc, camera);
  const hits = raycaster.intersectObjects(hitMeshes, false);
  if (hits.length) {
    const rec = buildings.find(b => b.path === hits[0].object.userData.path);
    if (rec) {
      labelEl.querySelector('.name').textContent = rec.title;
      labelEl.querySelector('.path').textContent = 'epistles.xyz' + rec.path + ' · ' + rec.cat.name;
      labelEl.style.opacity = '1';
      canvas.style.cursor = 'pointer';
      if (click) { focusRec = rec; toast('Focused: ' + rec.title + ' — walk or press Esc to release.'); }
      return;
    }
  }
  labelEl.style.opacity = '0'; canvas.style.cursor = 'default';
  if (click) focusRec = null;
}

/* ---------------- the walk ------------------------------------------- */
function step(dt) {
  if (mode === 'fading') { placeCamera(dt); return; }
  let mx = input.sx, mz = input.sy;
  if (input.keys['w'] || input.keys['arrowup']) mz -= 1;
  if (input.keys['s'] || input.keys['arrowdown']) mz += 1;
  if (input.keys['a'] || input.keys['arrowleft']) mx -= 1;
  if (input.keys['d'] || input.keys['arrowright']) mx += 1;
  const mag = Math.hypot(mx, mz);
  if (mag > 0.08) {
    focusRec = null;
    const m = Math.min(1, mag), s = Math.sin(cam.yaw), c = Math.cos(cam.yaw);
    const wx = (-mx * c - mz * s) / (mag > 1 ? mag : 1), wz = (-mz * c + mx * s) / (mag > 1 ? mag : 1);
    const sp = (mode === 'interior' ? 5.2 : 8.4) * m;
    moveWithCollision(wx * sp * dt, wz * sp * dt);
    clampWorld();
    const target = Math.atan2(wx, wz);
    const diff = ((target - P.heading + Math.PI * 3) % (Math.PI * 2)) - Math.PI;
    P.heading += diff * Math.min(1, dt * 10);
    P.bob += dt * (5 + m * 5);
  }
  const baseY = mode === 'interior' ? POCKET.y : 0;
  const ty = baseY + (mag > 0.08 ? Math.abs(Math.sin(P.bob)) * 0.05 : 0);
  P.yS += (ty - P.yS) * Math.min(1, dt * 14);
  you.position.set(P.x, P.yS, P.z);
  you.rotation.y = P.heading;
  nearDoor = findNearDoor();
  const prompt = document.getElementById('prompt');
  if (nearDoor) {
    document.getElementById('enterBtn').textContent =
      mode === 'interior' ? '◂ EXIT TO STREET' : 'ENTER ' + nearDoor.title + ' ▸';
    prompt.style.opacity = '1';
  } else prompt.style.opacity = '0';
  placeCamera(dt);
}
function placeCamera(dt) {
  const baseY = mode === 'interior' ? POCKET.y : 0;
  const dist = mode === 'interior' ? 4.6 : 7.8;
  let tx = P.x, ty = baseY + 1.55, tz = P.z;
  if (focusRec && mode === 'street') { tx = focusRec.x; ty = focusRec.h * 0.55; tz = focusRec.z; }
  const cx = tx - Math.sin(cam.yaw) * Math.cos(cam.pitch) * dist;
  const cz = tz - Math.cos(cam.yaw) * Math.cos(cam.pitch) * dist;
  const cy = ty + Math.sin(cam.pitch) * dist * 0.8 + 0.6;
  camera.position.set(cx, Math.max(baseY + 0.5, cy), cz);
  camera.lookAt(tx, ty, tz);
}

/* ---------------- HUD: compass, stats, legend, LOD ------------------- */
const compassCtx = document.getElementById('compass').getContext('2d');
function drawCompass() {
  const g = compassCtx, cx = 74, cy = 74, R = 62;
  g.clearRect(0, 0, 148, 148);
  g.beginPath(); g.arc(cx, cy, R, 0, Math.PI * 2);
  g.fillStyle = 'rgba(6,10,16,0.72)'; g.fill();
  g.strokeStyle = '#d8a833'; g.lineWidth = 2; g.stroke();
  const pts = [['N', 0, '#ff5a4d'], ['E', Math.PI / 2, '#cfe0f2'], ['S', Math.PI, '#cfe0f2'], ['W', -Math.PI / 2, '#cfe0f2']];
  g.font = 'bold 17px Georgia, serif'; g.textAlign = 'center'; g.textBaseline = 'middle';
  for (const [t, a, col] of pts) {
    const ang = a - cam.yaw + Math.PI;
    g.fillStyle = col;
    g.fillText(t, cx + Math.sin(ang) * (R - 15), cy - Math.cos(ang) * (R - 15));
  }
  g.beginPath(); g.moveTo(cx, cy - 12); g.lineTo(cx + 4, cy + 6); g.lineTo(cx - 4, cy + 6); g.closePath();
  g.fillStyle = '#ffe9a8'; g.fill();
}
let toastTimer = 0;
function toast(msg) {
  const el = document.getElementById('toast');
  el.textContent = msg; el.style.opacity = '1';
  clearTimeout(toastTimer); toastTimer = setTimeout(() => { el.style.opacity = '0'; }, 3400);
}
function hudTick(extent) {
  const info = renderer.info.render;
  document.getElementById('stats').innerHTML =
    'DRAWS ' + info.calls + ' · TRIS ' + Math.round(info.triangles / 1000) + 'k<br>' +
    buildings.length + ' BUILDINGS · ' + (mode === 'interior' ? 'INTERIOR' : 'STREET');
  let near = null, nd = 1e9;
  for (const b of buildings) { const d = Math.hypot(P.x - b.x, P.z - b.z); if (d < nd) { nd = d; near = b; } }
  document.getElementById('whereami').textContent = mode === 'interior'
    ? (interiorRec ? interiorRec.title + ' — INSIDE' : 'INSIDE')
    : (nd < 16 ? 'THE PLAZA' : 'NEAR ' + (near ? near.title : '—'));
  /* LOD: