<!-- NEW1 — TEST 1 · THE LIVING DISTRICT · part 446 -->
ite: site, what: what });
  try {
    window.localStorage.setItem(MEMORY_KEY, JSON.stringify(memoryMirror));
  } catch (e) { /* mirror-only logging still counts */ }
}

if (typeof window !== 'undefined' && !window.logBuild) {
  window.logBuild = function (what) { memoryLog('new1', what); };
  window.readMemory = function () { return memoryRead(); };
}

/* ------------------------------------------------------------------ */
/* Small deterministic helper for floor tinting                        */
/* ------------------------------------------------------------------ */

function mulberry32(seed) {
  var a = seed >>> 0;
  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;
  };
}

function clamp(v, lo, hi) { return Math.min(hi, Math.max(lo, v)); }

/* ------------------------------------------------------------------ */
/* The floor's face: a canvas-drawn city base.                         */
/* Dark asphalt-green, city blocks in slightly varied tints, street    */
/* grid cut through. Procedural — no image files, nothing to load.     */
/* ------------------------------------------------------------------ */

function makeFloorTexture(THREE, size) {
  var px = 512;
  var canvas = document.createElement('canvas');
  canvas.width = px; canvas.height = px;
  var g = canvas.getContext('2d');
  var rand = mulberry32(20261015);

  g.fillStyle = '#141a16';                 /* the streets: dark asphalt */
  g.fillRect(0, 0, px, px);

  var cells = 4;                            /* 4x4 blocks per tile */
  var cell = px / cells;
  var road = 14;                            /* street width in px */
  for (var i = 0; i < cells; i++) {
    for (var j = 0; j < cells; j++) {
      var lift = Math.floor(rand() * 14) - 4;
      var r = 30 + lift, gg = 42 + lift, b = 34 + lift;
      g.fillStyle = 'rgb(' + r + ',' + gg + ',' + b + ')';
      g.fillRect(i * cell + road / 2, j * cell + road / 2,
                 cell - road, cell - road);
      /* a faint inner court line so blocks read as blocks */
      g.strokeStyle = 'rgba(0,0,0,0.25)';
      g.lineWidth = 2;
      g.strokeRect(i * cell + road / 2 + 7, j * cell + road / 2 + 7,
                   cell - road - 14, cell - road - 14);
    }
  }
  /* center dashes down each street */
  g.fillStyle = 'rgba(214,178,84,0.35)';
  for (var k = 0; k < cells; k++) {
    for (var d = 8; d < px; d += 32) {
      g.fillRect(k * cell - 1, d, 2, 16);
      g.fillRect(d, k * cell - 1, 16, 2);
    }
  }

  var tex = new THREE.CanvasTexture(canvas);
  tex.wrapS = THREE.RepeatWrapping;
  tex.wrapT = THREE.RepeatWrapping;
  var tiles = Math.max(4, Math.round(size / 300));
  tex.repeat.set(tiles, tiles);
  if ('colorSpace' in tex && THREE.SRGBColorSpace) {
    tex.colorSpace = THREE.SRGBColorSpace;
  }
  tex.anisotropy = 4;
  return tex;
}

/* ------------------------------------------------------------------ */
/* makeStage                                                           */
/* ------------------------------------------------------------------ */

function makeStage(THREE, renderer, opts) {
  opts = opts || {};
  var size = opts.size || 1200;             /* the district, edge to edge */
  var site = opts.site || 'new1';
  var STAND_Y = 0.02;                       /* the contract height */

  var scene = new THREE.Scene();
  var SKY = new THREE.Color(0x0b0e12);
  scene.background = SKY;
  /* Fog matched to the sky: the world edge melts into dark long before
   * the camera could ever frame raw void. */
  scene.fog = new THREE.Fog(SKY.getHex(), size * 0.95, size * 2.4);

  /* --- light: enough to read a city by ------------------------------ */
  scene.add(new THREE.HemisphereLight(0xbdd3ff, 0x1c241c, 0.85));
  var sun = new THREE.DirectionalLight(0xfff1d6, 1.6);
  sun.position.set(size * 0.45, size * 0.7, size * 0.25);
  scene.add(sun);

  /* --- 1. THE MAP FLOOR --------------------------------------------- */
  var floor = new THREE.Mesh(
    new THREE.PlaneGeometry(size, size),
    new THREE.MeshStandardMaterial({
      map: makeFloorTexture(THREE, size),
      roughness: 0.96,
      metalness: 0.0
    })
  );
  floor.rotation.x = -Math.PI / 2;
  floor.position.y = 0;
  floor.name = 'MAP_FLOOR';
  scene.add(floor);

  /* --- 2. THE STAND LAYER ------------------------------------------- */
  /* Invisible on purpose. Standing is resolved by the groundY()
   * contract below — never by raycast, never by decorative geometry —
   * so a character's feet have one law and it cannot be broken by a
   * badly placed floor tile. */
  var standLayer = new THREE.Mesh(
    new THREE.PlaneGeometry(size, size),
    new THREE.MeshBasicMaterial()
  );
  standLayer.rotation.x = -Math.PI / 2;
  standLayer.position.y = STAND_Y;
  standLayer.visible = false;
  standLayer.name = 'STAND_LAYER';
  s