<!-- SOLID CITY · part 3110 -->
ofed 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) {
    var g = new T.BufferGeometry();
    g.setAttribute('position', new T.Float32BufferAttribute(this.pos, 3));
    g.setAttribute('normal', new T.Float32BufferAttribute(this.nor, 3));
    g.setAttribute('color', new T.Float32BufferAttribute(this.col, 3));
    g.setIndex(this.idx);
    var m = new T.Mesh(g, mat); m.frustumCulled = false; return m;
  };
  var solid = new Batch(), glow = new Batch(), glassB = new Batch();
  function box(b, w, h, d, m, color) { b.add(new T.BoxGeometry(w, h, d), m, color); }
  function cyl(b, rt, rb, h, s, m, color) { b.add(new T.CylinderGeometry(rt, rb, h, s), m, color); }
  function sph(b, r, m, color, sx, sy, sz) { b.add(new T.SphereGeometry(r, 10, 7), m, color); }
  function cone(b, r, h, s, m, color) { b.add(new T.ConeGeometry(r, h, s), m, color); }

  /* red and black marble — one canvas, veined by hand */
  function marbleTex(redBase) {
    var cv = document.createElement('canvas'); cv.width = 256; cv.height = 256;
    var g = cv.getContext('2d');
    g.fillStyle = redBase ? '#57101b' : '#0d0b0e'; g.fillRect(0, 0, 256, 256);
    for (var i = 0; i < 46; i++) {
      g.strokeStyle = redBase ? 'rgba(255,120,120,' + (0.05 + R() * 0.16) + ')' : 'rgba(200,30,50,' + (0.08 + R() * 0.2) + ')';
      g.lineWidth = 0.6 + R() * 1.8; g.beginPath();
      var x = R() * 256, y = R() * 256; g.moveTo(x, y);
      for (var k = 0; k < 4; k++) { x += (R() - 0.5) * 90; y += (R() - 0.5) * 90; g.lineTo(x, y); }
      g.stroke();
    }
    for (var j = 0; j < 26; j++) { g.fillStyle = 'rgba(240,220,190,' + (0.03 + R() * 0.06) + ')'; g.fillRect(R() * 256, R() * 256, 1.4, 1.4); }
    var t = new T.CanvasTexture(cv); t.encoding = T.sRGBEncoding; t.wrapS = t.wrapT = T.RepeatWrapping;
    return t;
  }
  var marbleBlack = marbleTex(false), marbleRed = marbleTex(true);
  var marbleMats = [];
  function marbleDisc(x, y, z, r, tex, rep) {
    var t2 = tex.clone(); t2.needsUpdate = true; t2.repeat.set(rep || 3, rep || 3);
    var m = new T.Mesh(new T.CylinderGeometry(r, r + 1.5, 0.5, 40), new T.MeshLambertMaterial({ map: t2 }));
    m.position.set(x, y - 0.13, z); m.frustumCulled = false; group.add(m); marbleMats.push(m);
    return m;
  }
  function textPlate(lines, w, h, fs) {
    var cv = document.createElement('canvas'); cv.width = 1024; cv.height = Math.round(1024 * h / w);
    var g = cv.getContext('2d');
    g.fillStyle = '#0d0a0c'; g.fillRect(0, 0, cv.width, cv.height);
    g.strokeStyle = '#d8a833'; g.lineWidth = 7; g.strokeRect(12, 12, cv.width - 24, cv.height - 24);
   