<!-- SOLUV · part 500 -->

        if (!out.minObject || m < out.minObject.min) out.minObject = rec; if (m < MINIMUM - 1e-6) out.violations.push(rec);
      });
      const ws = root.matrixWorld.getMaxScaleOnAxis(); out.minGlyphEm = +(EM * ws).toFixed(4); out.lineHeightWorld = +(EM * L.LINE * ws).toFixed(4);
      if (out.minGlyphEm < MINIMUM - 1e-6) out.violations.push({ glyphEm: out.minGlyphEm });
      return out;
    },
    dispose() {
      S.disposed = true; if (S.es) S.es.close(); clearTimeout(S.retry); clearInterval(S.poll);
      for (const t of S.tiles.values()) { freeTile(t); t.mesh.material.dispose(); } if (S.header) { freeTile(S.header); S.header.mesh.material.dispose(); }
      for (const o of slab.children) o.geometry && o.geometry.dispose();
      tileGeo.dispose(); materials.forEach(m => m.dispose());
      if (avatar) avatar.traverse(o => { if (o.isMesh) { o.geometry.dispose(); o.material.dispose(); } });
      if (root.parent) root.parent.remove(root);
    },
  };
  root.wall = api; root.userData.soluvWall = room;
  relayout();
  if (opts.eager && source) startSafe();
  return root;
}


===== FILE: public/soluv/world/demo.html =====
<!doctype html>
<html lang="en"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,viewport-fit=cover">
<link rel="icon" href="data:,"><title>Soluv walls placed in another scene (demo)</title>
<style>html,body{margin:0;height:100%;background:#000;overflow:hidden}canvas{display:block;width:100%;height:100%}
#note{position:fixed;left:8px;right:8px;top:8px;font:14px/1.3 sans-serif;color:#9fe9ff;background:#000a;padding:6px 8px;border-radius:8px;pointer-events:none}</style>
</head><body>
<canvas id="c"></canvas>
<div id="note">Demo: a separate three.js scene (the saga's own r128 build, global THREE) that owns its renderer, camera and loop. Walls 1 (the site lobby) and 2 (the Soluv room) are dropped in with createChatWall(), both live, both solid.</div>
<!-- the host page's three.js, exactly as the saga loads it -->
<script src="/saga/three.min.js"></script>
<script>
  // ----- a stand-in "existing map" (any scene works) -----
  window.MINIMUM = 0.13;
  var canvas = document.getElementById('c');
  var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
  renderer.setPixelRatio(Math.min(2, devicePixelRatio || 1));
  var scene = new THREE.Scene(); scene.background = new THREE.Color(0x000000);
  var camera = new THREE.PerspectiveCamera(55, 1, 0.1, 5000);
  var ground = new THREE.Mesh(new THREE.BoxGeometry(400, 0.26, 400), new THREE.MeshBasicMaterial({ color: 0x0b1410 })); ground.position.y = -0.13; scene.add(ground);
  var grid = new THREE.GridHelper(400, 80, 0x2a6a4a, 0x16301f); grid.position.y = 0.01; scene.add(grid);
  for (var i = 0; i < 14; i++) { var h = 2 + (i * 7) % 9; var b = new THREE.Mesh(new THREE.BoxGeometry(2, h, 2), new THREE.MeshBasicMaterial({ color: 0x3a2a5a + i * 0x030201 })); var a = i / 14 * Math.PI * 2; b.position.set(Math.cos(a) * 34, h / 2, Math.sin(a) * 34); scene.add(b); }
  function resize() { var w = innerWidth, h = innerHeight; renderer.setSize(w, h, false); camera.aspect = w / h; camera.updateProjectionMatrix(); }
  addEventListener('resize', resize); resize();
  var t0 = performance.now();
  (function loop() { requestAnimationFrame(loop); var t = (performance.now() - t0) / 1000; var a = 0.35 + Math.sin(t * 0.12) * 0.25; camera.position.set(Math.sin(a) * 60, 14, Math.cos(a) * 60 + 20); camera.lookAt(5, 18, -45); renderer.render(scene, camera); })();
</script>
<!-- ===== the snippet for Solid: everything a host page needs ===== -->
<script type="module">
  import { createChatWall } from '/soluv/world/chat-wall.js';
  const wall1 = createChatWall({ THREE, room: 1, position: [-30, 0, -40], rotationY: 0.35, avatar: true });  // the site lobby, live (read + post through the lobby itself)
  const wall2 = createChatWall({ THREE, room: 2, position: [40, 0, -50], rotationY: -0.45, scale: 1 });  // Playa's /soluv room, live
  scene.add(wall1, wall2);
  // ===== end of snippet =====
  window.demo = { ready: true, walls: [wall1, wall2], report() {
    return { threeRevision: THREE.REVISION, walls: [wall1, wall2].map(w => ({ ...w.wall.info(), law: w.wall.lawCheck(), thrift: w.wall.thrift() })) };
  } };
</script>
</body></html>


===== FILE: public/soluv/world/soulprint-tower.js =====
/*
 * Soul Print — a FREE open chat tower on the /soluv map (add-on; hooks 'soluv-world-ready', world.js is untouched).
 * King's word (Oct 5, 11:22 PM): "let her free". She is a plain dark-glass tower with a normal chat face: the existing
 * Soluv room (/api/soluv-room, same login-to-post rules as every wall: guests read, logged-in people post under their
 * own session name). No lock, no admin check, no private page, no AI: this file talks to nothing but the Soluv room.
 * She stands far out near the west edge of the 2000 map and is drawn only when you are near her, aimed at her,
 * or l