<!-- NEW1 — TEST 1 · THE LIVING DISTRICT · part 11 -->
,
  [9.0, 0x87a6c8, 1.55, 0xfff1cf, 0.95],
  [16.5, 0x87a6c8, 1.55, 0xfff1cf, 0.95],
  [18.5, 0x51383f, 0.70, 0xff9a5c, 0.50],
  [20.0, 0x0a0e14, 0.06, 0x8fa3c7, 0.22],
  [24.0, 0x0a0e14, 0.06, 0x8fa3c7, 0.22],
];
const _cA = new THREE.Color(), _cB = new THREE.Color();
function applySky(clockStr) {
  const h = parseInt(clockStr.slice(0, 2), 10) + parseInt(clockStr.slice(3, 5), 10) / 60;
  let a = SKY_KEYS[0], b = SKY_KEYS[SKY_KEYS.length - 1];
  for (let i = 0; i < SKY_KEYS.length - 1; i++) {
    if (h >= SKY_KEYS[i][0] && h <= SKY_KEYS[i + 1][0]) { a = SKY_KEYS[i]; b = SKY_KEYS[i + 1]; break; }
  }
  const span = Math.max(0.0001, b[0] - a[0]);
  const k = Math.min(1, Math.max(0, (h - a[0]) / span));
  _cA.setHex(a[1]); _cB.setHex(b[1]);
  scene.background.copy(_cA).lerp(_cB, k);
  if (scene.fog) scene.fog.color.copy(scene.background);
  if (sunLight) {
    sunLight.intensity = a[2] + (b[2] - a[2]) * k;
    _cA.setHex(a[3]); _cB.setHex(b[3]);
    sunLight.color.copy(_cA).lerp(_cB, k);
  }
  if (hemiLight) hemiLight.intensity = a[4] + (b[4] - a[4]) * k;
}

/* ---------- entering and leaving: the camera's two worlds ----------
 * SKY: the stage rig flies its fenced orbit. STREET: the boom rides
 * behind the King. Between them, a dive (or a rise) blends the two
 * — positions eased, look-targets eased — over a couple of seconds. */
const enterBtn = document.getElementById('enterBtn');
let mode = 'sky'; // sky | diving | street | rising
let trans = null;
const _stubCam = { position: new THREE.Vector3(), look: new THREE.Vector3(), lookAt(v) { this.look.copy(v); } };
const _v1 = new THREE.Vector3(), _v2 = new THREE.Vector3();
function boomTarget() { // where the street camera wants to be, right now
  _stubCam.position.set(king.x, 99, king.z); // y is recomputed inside boom
  king.boom(_stubCam, 1000); // a huge dt: lands exactly on the desired pose
  return _stubCam;
}
enterBtn.addEventListener('click', () => {
  if (mode === 'sky') {
    king.spawn(-41, 123, Math.PI);
    trans = {
      k: 0, dur: 2.4,
      fromPos: camera.position.clone(),
      fromLook: new THREE.Vector3(0, 2, 0),
      toLook: new THREE.Vector3(),
    };
    mode = 'diving';
    enterBtn.textContent = '···';
  } else if (mode === 'street') {
    // learn where the sky rig would put the camera, then rise to it
    const streetPos = camera.position.clone();
    const streetLook = new THREE.Vector3(king.x + Math.sin(king.yaw) * 5, 1.7, king.z + Math.cos(king.yaw) * 5);
    stage.update(0, clock.elapsedTime);
    trans = { k: 0, dur: 2.0, fromPos: streetPos, fromLook: streetLook, toPos: camera.position.clone(), toLook: new THREE.Vector3(0, 2, 0) };
    camera.position.copy(streetPos);
    mode = 'rising';
    enterBtn.textContent = '···';
  }
});
const smooth = (k) => k * k * (3 - 2 * k);

/* the pads: tappable, held — his interface law */
document.querySelectorAll('#pad button').forEach((btn) => {
  const key = btn.getAttribute('data-k');
  const on = (e) => { e.preventDefault(); king.input[key] = true; btn.classList.add('on'); };
  const off = (e) => { e.preventDefault(); king.input[key] = false; btn.classList.remove('on'); };
  btn.addEventListener('pointerdown', on);
  btn.addEventListener('pointerup', off);
  btn.addEventListener('pointercancel', off);
  btn.addEventListener('pointerleave', off);
});
/* drag anywhere on the world to look; lift to raise the eye */
let dragId = null, dragX = 0, dragY = 0;
renderer.domElement.addEventListener('pointerdown', (e) => { dragId = e.pointerId; dragX = e.clientX; dragY = e.clientY; });
renderer.domElement.addEventListener('pointermove', (e) => {
  if (dragId !== e.pointerId || mode !== 'street') return;
  king.look((dragX - e.clientX) * 0.0052);
  king.lift((dragY - e.clientY) * 0.02);
  dragX = e.clientX; dragY = e.clientY;
});
renderer.domElement.addEventListener('pointerup', () => { dragId = null; });
renderer.domElement.addEventListener('pointercancel', () => { dragId = null; });
/* a quiet extra for desktop visitors — the pads remain the law */
const KEYMAP = { ArrowUp: 'fwd', KeyW: 'fwd', ArrowDown: 'back', KeyS: 'back', ArrowLeft: 'left', KeyA: 'left', ArrowRight: 'right', KeyD: 'right' };
window.addEventListener('keydown', (e) => { const k = KEYMAP[e.code]; if (k) { king.input[k] = true; e.preventDefault(); } });
window.addEventListener('keyup', (e) => { const k = KEYMAP[e.code]; if (k) { king.input[k] = false; e.preventDefault(); } });

function nearestComplex() {
  let best = 'the open street', bd = Infinity;
  for (const c of city.meta.complexes) {
    const d = (c.center.x - king.x) ** 2 + (c.center.z - king.z) ** 2;
    if (d < bd) { bd = d; best = c.name; }
  }
  return best;
}

/* ---------- the loop ---------- */
const clock = new THREE.Clock();
let censusTimer = 0, firstPos = null;
function frame() {
  requestAnimationFrame(frame);
  const dt = Math.min(clock.getDelta(), 0.05);
  const t = clock.elapsedTime;
  life.update(d