<!-- SOLID CITY · part 3607 -->
nction (t) {
      for (var k = 0; k < bandLights.length; k++)
        bandLights[k].intensity = (k === bandLights.length - 1 ? 2.2 : 1.15) + Math.sin(t * 1.7 + k * 1.3) * 0.14;
      var s = 1 + Math.sin(t * 2.2) * 0.12;
      promise.scale.set(s, s, s);
      promiseGlow.intensity = 2.0 + Math.sin(t * 2.2) * 0.35;
    }
  };
  return root;
}
export default createTunnelBore;


===== FILE: public/city/two-turn-test.html =====
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>Two-Turn AI Test — epistles experiment</title>
<style>
  *{box-sizing:border-box}
  html,body{margin:0;padding:0;background:#050308;color:#f5e9e9;font-family:system-ui,-apple-system,sans-serif;min-height:100%}
  body{padding:16px;max-width:560px;margin:0 auto}
  h1{font-size:18px;letter-spacing:2px;color:#e8c872;margin:0 0 4px}
  p.sub{color:#9a8a8a;font-size:13px;margin:0 0 16px}
  #start,#stop{display:block;width:100%;padding:18px;font-size:18px;border-radius:12px;border:2px solid #5eead4;background:#0a1214;color:#fff;cursor:pointer;margin:8px 0}
  #stop{border-color:#c1121f;display:none}
  #start:disabled{opacity:.4}
  .panel{border:1px solid #3a1520;border-radius:12px;padding:12px;margin:12px 0;background:#0c0a10}
  .panel h2{font-size:12px;letter-spacing:2px;color:#e8c872;margin:0 0 8px}
  .row{display:flex;justify-content:space-between;font-size:13px;padding:3px 0;color:#cfc0c0}
  .row b{color:#5eead4;font-weight:600}
  #log{font:12px/1.5 ui-monospace,monospace;color:#9ad;max-height:260px;overflow:auto;white-space:pre-wrap}
  .turn{border-left:3px solid #e8c872;padding:6px 10px;margin:8px 0;background:#100c08;font-size:13px}
  .turn b{color:#e8c872}
  ol{font-size:14px;line-height:1.6;color:#e8d9d9;padding-left:22px}
  ol li{margin:6px 0}
  .warn{color:#ff9d9d;font-size:13px}
</style>
</head>
<body>
  <h1>TWO-TURN AI TEST</h1>
  <p class="sub">Isolated experiment — full voice loop with SABLE, two turns, every delay measured. Nothing is retained.</p>

  <button id="start" type="button">START TEST</button>
  <button id="stop" type="button">STOP TEST</button>

  <div class="panel">
    <h2>STATUS</h2>
    <div class="row"><span>turn</span><b id="s-turn">idle</b></div>
    <div class="row"><span>phase</span><b id="s-phase">idle</b></div>
    <div class="row"><span>mic engine</span><b id="s-mic">—</b></div>
    <div class="row"><span>restarts</span><b id="s-restart">0</b></div>
  </div>

  <div class="panel">
    <h2>TURN TIMINGS</h2>
    <div id="turns"><div class="row"><span>no turns yet</span><b>—</b></div></div>
  </div>

  <div class="panel">
    <h2>EVENT LOG</h2>
    <div id="log">waiting…</div>
  </div>

  <div class="panel">
    <h2>HOW TO RUN IT</h2>
    <ol>
      <li>Tap START TEST. Allow the microphone <b>once</b> if asked.</li>
      <li><b>Turn 1:</b> speak to SABLE. She answers aloud.</li>
      <li><b>Turn 2:</b> when she finishes, speak again. She answers again.</li>
      <li>Read the timings and the log. Report: total time per turn, any restarts, any failures.</li>
    </ol>
    <p class="warn">Keep the screen ON for this test. The locked test is separate.</p>
  </div>

<script>
(function () {
  'use strict';
  var $ = function (id) { return document.getElementById(id); };
  var logEl = $('log'), first = true;
  function stamp() { var t = new Date(); return ('0'+t.getHours()).slice(-2)+':'+('0'+t.getMinutes()).slice(-2)+':'+('0'+t.getSeconds()).slice(-2)+'.'+('00'+t.getMilliseconds()).slice(-3); }
  function log(msg) {
    if (first) { logEl.textContent = ''; first = false; }
    logEl.textContent += '[' + stamp() + '] ' + msg + '\n';
    logEl.scrollTop = 1e9;
  }
  function set(id, v) { $(id).textContent = v; }

  var turn = 0, maxTurns = 2, running = false;
  var rec = null, restarts = 0, maxRestarts = 8, restartTimer = 0;
  var csrf = '', turnT0 = 0, micT0 = 0, brainT0 = 0, synthT0 = 0;
  var turnData = [];
  var SR = window.SpeechRecognition || window.webkitSpeechRecognition;

  function turnCard(n, d) {
    var el = document.createElement('div');
    el.className = 'turn';
    el.innerHTML = '<b>TURN ' + n + '</b><br>' +
      'capture: ' + d.capture + 'ms · brain: ' + d.brain + 'ms<br>' +
      'speech: ' + d.speech + 'ms · <b>total: ' + d.total + 'ms</b>' +
      (d.restarts ? '<br>mic restarts: ' + d.restarts : '') +
      (d.error ? '<br><span style="color:#ff9d9d">error: ' + d.error + '</span>' : '');
    var box = $('turns');
    if (n === 1) box.textContent = '';
    box.appendChild(el);
  }

  function finish() {
    running = false;
    stopMic();
    try { speechSynthesis.cancel(); } catch (e) {}
    set('s-turn', 'done'); set('s-phase', 'idle');
    $('stop').style.display = 'none';
    var b = $('start'); b.style.display = 'block'; b.disabled = false; b