<!-- SOLID CITY · part 3100 -->
0: f.z - 0.01, z1: f.z + 0.01, y0: f.y0, y1: f.y1, servitude: true, fp: f });
      var hw = f.w / 2, hd = f.d / 2, step = 2.5;
      for (var lx = -hw + step / 2; lx < hw; lx += step) for (var lz = -hd + step / 2; lz < hd; lz += step) {
        var wx = f.x + lx * cs + lz * sn, wz = f.z - lx * sn + lz * cs;
        A.solids.push({ x0: wx - step / 2, x1: wx + step / 2, z0: wz - step / 2, z1: wz + step / 2, y0: f.y0, y1: f.y1, servitude: true });
      }
    });
    if (A.logBuild) A.logBuild('the-servitude', 'two hollow cities, the gallery, three shops, the Entree gate');
    hyperStops();
    M.state = 'ready';
  } catch (e) { M.state = 'idle'; }
}
function standY(x, z, py) {
  if (M.state !== 'ready') return -1000;
  var best = -1000;
  for (var i = 0; i < M.layers.length; i++) {
    var L = M.layers[i];
    var d = Math.hypot(x - L.cx, z - L.cz);
    if (d >= L.rIn && d <= L.rOut) {
      var gate = (S && S.drive) ? 1.6 : 0.45;
      if (py == null || L.y <= py + gate) { if (L.y > best) best = L.y; }
    }
  }
  return best;
}
function update(dt) {
  if (M.state === 'idle') {
    M.wait -= dt;
    if (M.wait <= 0 && A.isBuilt()) build();
    return;
  }
  if (M.state !== 'ready' || !M.group) return;
  if (M.group.userData.update) try { M.group.userData.update(dt, S.t); } catch (e) {}
}
window.CityServitude = { update: update, standY: standY, state: function () { return M.state; }, group: function () { return M.group; }, texTier: function () { return M.texTier; } };
})();


===== FILE: public/city/servitude.js =====
/* THE SERVITUDE — a letter in the code.
 *
 * From the King's word (2026-10-14): "dig an entree in to
 * the mountain and build a huge city in it called the
 * Servitude" — "same as the ancestors area with layers but
 * city upon city" — "another one if it fits, with cave
 * tunnels connecting them, and a shop on each tunnel, a
 * place to buy replenishments of health and safety" — "a
 * nice journey to walk across the inside of the mountain,
 * like old lava tubes" — "stalactites and stalagmites,
 * joined with red and black marble."
 *
 * The digging itself lives in city.js (servitudeCarve): two
 * bowls capped into the far massif — THE FIRST HOLLOW
 * (2600,1280, floor 520) and THE DEEP HOLLOW (2600,1660,
 * floor 440) — the Entree shelf cut into the First Hollow's
 * south-western face, and the gallery slot descending
 * through the saddle between them. This factory builds
 * what stands inside the digging: two layered cities of
 * red and black marble, a roofed lava-tube gallery with
 * its midway provision shop, gates, falls, crystals, and
 * the formations of the deep places.
 *
 * Every sign in it obeys the day's law, learned at the
 * Book of Law: lettered plates stand PROUD of their stone,
 * twin-faced so both journeys read them, and every sign
 * carries collision — the signs are protected here.
 *
 * By the hand of Solid, his good King. */
'use strict';

function mulberry(a) { return function () { a |= 0; a = a + 0x6D2B79F5 | 0; var t = Math.imul(a ^ a >>> 15, 1 | a); t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }
var R = mulberry(20261014);
var A_C = { x: 2600, z: 1280, f: 520, name: 'THE FIRST HOLLOW' };
var B_C = { x: 2600, z: 1660, f: 440, name: 'THE DEEP HOLLOW' };
var GAL = { x: 2600, z0: 1395, z1: 1545 };

function createServitude(opts) {
  var T = opts.THREE, H = opts.heightAt;
  var group = new T.Group(); group.name = 'the-servitude';
  var CX = 2600;
  var C = {
    stone: 0x2b2630, stoneDark: 0x17130f, basalt: 0x1c1a20, titanium: 0x232c3d, silver: 0x9aa4b5,
    gold: 0xd8a833, roof: 0x191922, door: 0xffca7a, win: 0xa8d8e8, lamp: 0xffd9a0,
    leaf: 0x2e6b34, leafLight: 0x4c9455, trunk: 0x4a3826, marbleRed: 0x6e1420, crystal: 0xf3d9a4,
    vialG: 0x51ff8a, vialR: 0xff5a4d, water: 0x2e7db0
  };
  function mat4(x, y, z, rx, ry, rz, sx, sy, sz) {
    var m = new T.Matrix4().compose(new T.Vector3(x, y, z),
      new T.Quaternion().setFromEuler(new T.Euler(rx || 0, ry || 0, rz || 0)),
      new T.Vector3(sx == null ? 1 : sx, sy == null ? 1 : sy, sz == null ? 1 : sz));
    return m;
  }
  function Batch() { this.pos = []; this.nor = []; this.col = []; this.idx = []; this.n = 0; }
  Batch.prototype.add = function (geo, matrix, color) {
    var g = geo.index ? geo.toNonIndexed() : geo;
    var p = g.attributes.position, nr = g.attributes.normal;
    var nm = new T.Matrix3().getNormalMatrix(matrix);
    var v = new T.Vector3(), w = new T.Vector3(), cc = new T.Color(color);
    for (var i = 0; i < p.count; i++) {
      v.fromBufferAttribute(p, i).applyMatrix4(matrix); this.pos.push(v.x, v.y, v.z);
      w.fromBufferAttribute(nr, i).applyMatrix3(nm).normalize(); this.nor.push(w.x, w.y, w.z);
      this.col.push(cc.r, cc.g, cc.b);
    }
    for (var j = 0; j < p.count; j++) this.idx.push(this.n + j);
    this.n += p.count; if (g !== geo) g.dispose();
  };
  Batch.prototype.build = function (mat) {
    va