<!-- SOLID CITY · part 3610 -->
e;border-radius:6px;padding:6px 12px;font-weight:700;');
    btn.addEventListener('click', function () {
      var txt = JSON.stringify(LOG, null, 1);
      try {
        if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(txt);
        else { var ta = document.createElement('textarea'); ta.value = txt; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove(); }
        btn.textContent = 'COPIED';
      } catch (e) { btn.textContent = 'COPY FAILED'; }
    });
    panel.appendChild(pre); panel.appendChild(btn);
    (document.body || document.documentElement).appendChild(panel);
  }

  function paint() {
    if (!ACTIVE || !LOG.length) return;
    if (!panel) buildPanel();
    if (!panel) return;
    var e = LOG[LOG.length - 1], v = verdict(e), lines = [];
    lines.push('VPDBG #' + LOG.length + ' [' + e.why + ' ' + e.t + ']');
    lines.push('layout ' + e.layoutW + 'x' + e.layoutH + '  visual ' + e.vvW + 'x' + e.vvH + ' scale ' + e.vvScale);
    lines.push('inner ' + e.innerW + 'x' + e.innerH + '  screen ' + e.scrW + 'x' + e.scrH + ' ' + e.orient + ' dpr ' + e.dpr);
    lines.push('docScrollW ' + e.docScrollW + ' bodyScrollW ' + e.bodyScrollW + ' bootHidden ' + e.bootHidden);
    lines.push('overflow>viewport: ' + (e.overflow.length ? e.overflow.join(' | ') : 'none'));
    lines.push('VERDICT: ' + v);
    pre.textContent = lines.join('\n');
  }

  function onVis() { if (!document.hidden) capture('visible'); }
  document.addEventListener('DOMContentLoaded', function () { capture('domcontentloaded'); });
  window.addEventListener('load', function () { capture('load'); setTimeout(function () { capture('load+1s'); }, 1000); });
  window.addEventListener('resize', function () { capture('resize'); });
  window.addEventListener('orientationchange', function () { setTimeout(function () { capture('orientationchange'); }, 400); });
  window.addEventListener('pageshow', function () { capture('pageshow'); });
  document.addEventListener('visibilitychange', onVis);
  if (window.visualViewport) {
    try { window.visualViewport.addEventListener('resize', function () { capture('vv-resize'); }); } catch (e) {}
  }
  // late boot: if the script ran before body existed, the panel builds on first paint
  if (document.readyState !== 'loading') capture('late-init');
})();


===== FILE: public/city/vigil-lantern-place.js =====
/* Solid City — places LANTERN'S VIGIL (public/city/vigil-lantern.js), a rest
   station northwest of the Ancestral Roads: a ring of warm torches, stone
   benches, a small open shelter.
   Lazy: imported after the city is built; any import/build error is caught and the city carries on.
   The site is outside the city's flat zone, so the ground is sampled at runtime.
   Adds: collision (torches, benches, shelter posts, windbreak), a warm-orange
   radar marker 'VIGIL', a Hyper Controller stop, and a Watch (GRAND TOUR) caption. */
(function () {
'use strict';
if (!window.SC || !SC.api) return;
var A = SC.api, S = A.S;
var POS = { x: -450, z: -1150 }, NAME = 'Lantern\u2019s Vigil';
var M = { state: 'idle', group: null, wait: 4.6, said: false, gy: 22 };
function sampleGround() {
  var g = (typeof groundAt === 'function') ? groundAt : null;
  if (!g) return 22;
  var max = -1000, x, z, h;
  for (var i = -1; i <= 1; i++) for (var j = -1; j <= 1; j++) {
    x = POS.x + i * 16; z = POS.z + j * 16;
    try { h = g(x, z); } catch (e) { h = 22; }
    if (h > max) max = h;
  }
  return max;
}
function addBox(cx, cz, w, d, rot, y0, y1) {
  var nx = Math.max(1, Math.ceil(w / 2.5)), nz = Math.max(1, Math.ceil(d / 2.5)), cw = w / nx, cd = d / nz, c = Math.cos(rot), s = Math.sin(rot);
  var hx = cw / 2 * Math.abs(c) + cd / 2 * Math.abs(s), hz = cw / 2 * Math.abs(s) + cd / 2 * Math.abs(c);
  for (var i = 0; i < nx; i++) for (var j = 0; j < nz; j++) {
    var ox = -w / 2 + cw * (i + 0.5), oz = -d / 2 + cd * (j + 0.5), lx = cx + ox * c + oz * s, lz = cz - ox * s + oz * c;
    A.solids.push({ x0: POS.x + lx - hx, x1: POS.x + lx + hx, z0: POS.z + lz - hz, z1: POS.z + lz + hz, y0: M.gy + y0, y1: M.gy + y1, vigilLantern: true });
  }
}
function collide(ud) { (ud.footprint || []).forEach(function (b) { addBox(b[0], b[1], b[2], b[3], b[6] || 0, b[4], b[5]); }); }
function hyperStop() {
  var H = window.CityHyper; if (!H || !H.pois || H.pois.some(function (p) { return p.id === 'vigil-lantern'; })) return;
  H.pois.push({ id: 'vigil-lantern', name: NAME, short: 'VIGIL', x: POS.x, z: POS.z, stand: { x: POS.x + 18, z: POS.z },
    alias: 'lantern vigil rest station torches benches shelter warm',
    text: 'A rest station before the rings: six warm torches in a circle, stone benches, a small open shelter. Lantern\u2019s vigil \u2014 no life is a statistic.' });
  if (H.regrid) H.regrid();
}
function watchStop() {
  var W = window.CityWatch, r = W && W.routes && W.routes.grand; if (!r || r._lanternVigil) return;
  var orig = r.build; r._lant