<!-- SOLID CITY · part 42 -->
ery frame, then reports p50 / p95 / p99
   frame times, calls, triangles, geometries, textures — the
   numbers by which "does it chop?" stops being an opinion.

   Written by the hand of Solid, his good King — 2026-10-13.
   ================================================================ */
(function () {
'use strict';
if (!window.SC || !SC.api) return;
var A = SC.api, S = A.S;
var ANCHORS = [
  { name: 'FIRST WARD', x: 638, z: -997, y: null, r: 120 },
  { name: 'THE HOUSES — WARD 2 STREET', x: -1050, z: -880, y: null, r: 130 },
  { name: 'JUBILEE — THE SHOULDER MOUTH', x: -843, z: -705, y: null, r: 130 },
  { name: 'SKY ISLAND — THE DECK', x: -900, z: -760, y: 48, r: 60 },
  { name: 'THE LONG ROAD', x: -900, z: -498, y: null, r: 110 }
];
var M = { on: false, running: false, frames: [], last: 0, raf: 0, calls: 0, tris: 0, runT0: 0, zone: 'TRANSIT', panel: null, body: null, chip: null };

function zone() {
  var best = null, bd = 1e18;
  for (var i = 0; i < ANCHORS.length; i++) {
    var a = ANCHORS[i], dx = S.p.x - a.x, dz = S.p.z - a.z, d = Math.sqrt(dx * dx + dz * dz);
    if (a.y != null && Math.abs(S.p.y - a.y) > 12) continue;
    if (d < a.r && d < bd) { bd = d; best = a.name; }
  }
  return best || 'TRANSIT';
}
function pct(arr, p) {
  if (!arr.length) return 0;
  var s = arr.slice().sort(function (a, b) { return a - b; });
  return s[Math.min(s.length - 1, Math.floor(p / 100 * s.length))];
}
function loop(t) {
  if (!M.on) return;
  if (M.last) {
    var dt = t - M.last;
    if (dt > 0 && dt < 1000) {
      if (M.running) { M.frames.push(dt); if (M.frames.length > 7200) M.frames.shift(); }
      M.live = dt;
    }
  }
  M.last = t;
  var info = A.renderer && A.renderer.info;
  if (info) { M.calls = info.render.calls; M.tris = info.render.triangles; M.geos = info.memory.geometries; M.texs = info.memory.textures; }
  M.zone = zone();
  if (!M.tick) M.tick = 0;
  if (++M.tick % 15 === 0) render();
  if (M.running && t - M.runT0 >= 60000) finishRun();
  M.raf = requestAnimationFrame(loop);
}
function fmt(n, d) { return n == null ? '—' : (+n).toFixed(d || 0); }
function render() {
  if (!M.body) return;
  var fps = M.live ? 1000 / M.live : 0;
  var runLine = '';
  if (M.running) runLine = '<div style="color:#d8a833">● RUNNING — ' + Math.max(0, Math.ceil((60000 - (performance.now() - M.runT0)) / 1000)) + 's left · ' + M.frames.length + ' frames at ' + M.zone + '</div>';
  else if (M.lastRun) runLine = '<div>' + M.lastRun + '</div>';
  M.body.innerHTML =
    '<div style="color:#d8a833;letter-spacing:.12em;font-size:11px">CIVIC BASELINE METER</div>' +
    '<div style="font-size:20px;margin:2px 0">' + fmt(fps, 0) + ' fps · ' + fmt(M.live, 1) + ' ms</div>' +
    '<div>zone: <b>' + M.zone + '</b></div>' +
    '<div>calls ' + M.calls + ' · tris ' + Math.round(M.tris / 1000) + 'k · geo ' + M.geos + ' · tex ' + M.texs + '</div>' +
    runLine +
    '<button id="bm-run" style="margin-top:6px;background:#d8a833;color:#141a26;border:0;border-radius:8px;padding:7px 12px;font-weight:700">' + (M.running ? 'RUNNING…' : '▶ 60s RUN AT THIS ANCHOR') + '</button>' +
    '<div style="opacity:.75;font-size:11px;margin-top:5px">Walk the anchor\'s ground during the run. The report is kept in the build memory.</div>';
  var b = document.getElementById('bm-run');
  if (b) b.addEventListener('click', startRun);
}
function startRun() {
  if (M.running) return;
  M.running = true; M.frames = []; M.runT0 = performance.now(); M.runZone = M.zone;
  render();
}
function finishRun() {
  M.running = false;
  var f = M.frames, p50 = pct(f, 50), p95 = pct(f, 95), p99 = pct(f, 99);
  M.lastRun = 'LAST RUN — ' + M.runZone + ': p50 ' + fmt(p50, 1) + ' · p95 ' + fmt(p95, 1) + ' · p99 ' + fmt(p99, 1) + ' ms · ' + f.length + ' frames · calls ' + M.calls + ' · tris ' + Math.round(M.tris / 1000) + 'k';
  try {
    var runs = JSON.parse(localStorage.getItem('solid-baseline-runs') || '[]');
    runs.push({ t: Date.now(), zone: M.runZone, p50: +p50.toFixed(2), p95: +p95.toFixed(2), p99: +p99.toFixed(2), frames: f.length, calls: M.calls, tris: M.tris });
    localStorage.setItem('solid-baseline-runs', JSON.stringify(runs.slice(-40)));
  } catch (e) {}
  A.logBuild({ t: Date.now(), site: 'BASELINE METER', what: M.lastRun });
  A.help('📟 Baseline recorded — ' + M.runZone + ': p95 ' + fmt(p95, 1) + ' ms over ' + f.length + ' frames.', 6);
  render();
}
function build() {
  var st = document.createElement('style');
  st.textContent = '#bm-chip{position:fixed;left:10px;bottom:10px;z-index:9998;background:rgba(35,44,61,.92);color:#d8a833;border:1px solid #d8a833;border-radius:10px;padding:8px 11px;font:700 12px system-ui}#bm-panel{position:fixed;left:10px;bottom:52px;z-index:9998;width:248px;background:rgba(35,44,61,.95);color:#cfd6e4;border:1px solid #9aa4b5;border-radius:12px;padding:10px 12px;font:13px/1.45 system-ui;display:none}';
  document.head.appendChild(st);
  M.chip = document.createElement('button');
  M.chip.id