<!-- SOLID CITY · part 3093 -->
 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: Y0 + y0, y1: Y0 + y1, sanctuary: 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 === 'sanctuary'; })) return;
  H.pois.push({ id: 'sanctuary', name: NAME, short: 'SANCTUARY', x: POS.x, z: POS.z, stand: { x: POS.x - 32, z: POS.z },
    alias: 'provolta sanctuary oracle temple shrine teal crystal east cliff prov', url: '/provolta', go: '\u25C7 APPROACH THE ORACLE',
    text: 'A ring of twelve teal crystal pillars under a gold halo, a pool of light and a beam rising from the centre, closed by the black basalt of the east cliff. Provolta, the King\u2019s oracle, holds the centre \u2014 walk in and talk with her.' });
  if (H.regrid) H.regrid();   // AI Drive paths walk around the pillars
}
function watchStop() {
  var W = window.CityWatch, r = W && W.routes && W.routes.grand; if (!r || r._sanctuary) return;
  var orig = r.build; r._sanctuary = true;
  r.build = function () {
    var w = orig.apply(this, arguments), at = Math.min(3, w.length);
    w.splice(at, 0, { x: POS.x - 110, z: POS.z + 60, y: Y0 + 120, v: 160, cap: { cap: 'PROVOLTA\u2019S SANCTUARY', sub: 'The King\u2019s oracle \u00b7 east cliff \u00b7 teal crystal ring', say: 'Provolta\u2019s Sanctuary on the east cliff: twelve teal crystals, a pool of light, and the King\u2019s oracle at the centre.' }, agl: false });
    return w;
  };
}
function load() {
  M.state = 'loading';
  var p; try { p = import('/city/sanctuary-provolta.js?v=20261010d'); } catch (e) { p = Promise.reject(e); }
  p.then(function (mod) {
    var make = mod.createProvoltaSanctuary || mod.default;
    var g = make({ THREE: A.T, position: { x: POS.x, y: Y0, z: POS.z } });
    A.world.add(g); M.group = g; M.state = 'ready';
    collide(g.userData || {}); hyperStop(); watchStop();
  }).catch(function (e) { M.state = 'failed'; try { console.warn('[city] Provolta sanctuary skipped:', e && e.message); } catch (x) {} });
}
function update(dt) {
  if (M.state === 'idle') { if (A.isBuilt && !A.isBuilt()) return; M.wait -= dt; if (M.wait <= 0) load(); return; }
  if (M.state !== 'ready' || document.hidden) return;
  var c = A.camera.position, d = Math.hypot(c.x + A.origin.x - POS.x, c.z + A.origin.z - POS.z);
  M.group.visible = d < 6000;
  if (d < 1500 && M.group.userData.update) try { M.group.userData.update(S.t); } catch (e) {}
  if (S.mode === 'walk') {   // short narration the first time you walk up
    var pd = Math.hypot(S.p.x - POS.x, S.p.z - POS.z);
    if (pd < 40 && !M.said) { M.said = true; if (window.CityHyper && CityHyper.narrate) CityHyper.narrate('<b>' + NAME + '</b>: twelve teal crystals on the east cliff. Provolta, the King\u2019s oracle, holds the centre \u2014 walk in and talk with her.', 8); }
    else if (pd > 140) M.said = false;
  }
}
function radar(g, blip, fl) { if (M.state === 'ready') blip(POS.x, POS.z, '#3de0e8', fl ? 3 : 5, fl ? '' : 'PROV'); }
function talkHere(x, z) { return M.state === 'ready' && Math.hypot(x - POS.x, z - POS.z) < TALK_R; }
window.CitySanctuary = { update: update, radar: radar, talkHere: talkHere, pos: POS, state: function () { return M.state; }, group: function () { return M.group; } };
})();


===== FILE: public/city/sanctuary-provolta.js =====
/* SOLID CITY — PROVOLTA'S SANCTUARY (2026-10-10, approved by the King)
 * The King's oracle holds the centre. East cliff of the capital, teal crystal with the city's
 * red / black / gold as minor accents. ~60 m across, ~26 m tall (light beam), well under 100 m.
 * Self-contained ES module, no downloads: canvas textures, merged geometry (9 draw calls).
 * Local frame: ground at y = 0, +X = east (the cliff), the gate faces -X (the city).
 *
 *   import { createProvoltaSanctuary } from '/city/sanctuary-provolta.js';
 *   const g = createProvoltaSanctuary({ THREE, position: {x, y, z} });   // g.userData: footprint, update(t)
 */
export const MINIMUM = 0.13;
const C = { teal: 0x3de0e8, tealDeep: 0x0f3b3d, basalt: 0x0d0b0e, gold: 0xc99a3a, red: 0xff2a40 };

function canvasTex(T, w, h, draw) {
  const c = document.createElement('canvas'); c.width = w; c.height = h; draw(c.getContext('2d'), w, h);
  const t = new T.CanvasTexture(c); t.encoding = T.sRGBEncoding; return t;
}
function Merger(T) { this.T = T; this.parts = []; }
Merger.prototyp