<!-- SAGA · part 76 -->
ElementById('buildloadbar');
  var ov = document.getElementById('buildloader');
  function paint(){
    if(msg) msg.textContent = done + ' / ' + total;
    if(bar) bar.style.width = (total ? (done / total * 100) : 100) + '%';
  }
  function step(){
    var t0 = Date.now();
    while(window._buildQueue.length){
      if(Date.now() - t0 > 40) break;
      var fn = window._buildQueue.shift();
      try{ fn(); }catch(e){}
      done++;
    }
    paint();
    if(window._buildQueue.length){ requestAnimationFrame(step); }
    else{
      /* v87 — ?runcode=xxx: the site meshing with the game. /playaspace
         branches fire codes straight into the frame after the world loads. */
      try{
        var m = location.search.match(/[?&]runcode=([^&]+)/);
        /* v89 — comma-separated codes run in order: ?runcode=playa2,freecam */
        if(m && typeof runCode === 'function'){
          var list = decodeURIComponent(m[1]).split(',');
          for(var ri = 0; ri < list.length; ri++){ try{ runCode(list[ri]); }catch(e){} }
        }
      }catch(e){}
      if(ov){
        ov.style.transition = 'opacity 0.6s'; ov.style.opacity = '0';
        setTimeout(function(){ if(ov.parentNode) ov.parentNode.removeChild(ov); }, 700);
      }
    }
  }
  paint();
  requestAnimationFrame(function(){ requestAnimationFrame(step); });
})();

/* ===== SOLID'S LOAD-SPEED MEMOS — faster ways available if the King ever needs them =====
   Studied 2026-10-05 (research_notes/tap-object-actions-mobile-3d-20261005-1628).
   [1] PROGRESSIVE / LAZY LANDMARKS — build only near-field at load; stream distant
       landmarks nearest-first as the player approaches; unload beyond view distance
       with hysteresis. Biggest perceived-load win after time-slicing.
   [2] DRAW-CALL DIET — merge static geometry, InstancedMesh for repeats, one texture
       atlas, shared materials, cap pixel ratio 1-1.5, shadows off, under 50 draw
       calls on mobile. Also stops iOS Safari silently killing the tab at its memory
       ceiling ('A problem repeatedly occurred').
   [3] SPLIT + MINIFY — separate stable Three.js from changing game code for
       long-term caching; minify game code (~30% smaller); dynamic-import world
       builders after first frame. Big repeat-visit win.
   [4] WEB WORKER GENERATION — heavy procedural generation off the main thread,
       zero-copy typed arrays. iOS caveats: lazy-init workers, max ~2, never spawn
       at module-eval time (documented iOS freeze).
   [5] INTERNAL CHUNKING — detailEverything/ultraDetail run long single passes;
       chunk their inner loops with the same deadline pattern if one builder janks.
   [6] LOD FOR DISTANT LANDMARKS — cheaper geometry far away. Revisit if the world
       grows 10x.
   [7] iOS GUARDRAILS — hard memory ceiling, no swap; dispose unused GPU resources;
       tier quality by measured frame times in the first ~2s (deviceMemory absent on
       Safari — use UA/hardwareConcurrency).
   ====================================================================================== */


===== FILE: public/saga/three.min.js =====
/**
 * @license
 * Copyright 2010-2021 Three.js Authors
 * SPDX-License-Identifier: MIT
 */
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).THREE={})}(this,(function(t){"use strict";const e="128",n=100,i=300,r=301,s=302,a=303,o=304,l=306,c=307,h=1e3,u=1001,d=1002,p=1003,m=1004,f=1005,g=1006,v=1007,y=1008,x=1009,_=1012,w=1014,b=1015,M=1016,S=1020,T=1022,E=1023,A=1026,L=1027,R=33776,C=33777,P=33778,D=33779,I=35840,N=35841,B=35842,z=35843,F=37492,O=37496,H=2300,G=2301,U=2302,k=2400,V=2401,W=2402,j=2500,q=2501,X=3e3,Y=3001,Z=3007,J=3002,Q=3004,K=3005,$=3006,tt=7680,et=35044,nt=35048,it="300 es";class rt{addEventListener(t,e){void 0===this._listeners&&(this._listeners={});const n=this._listeners;void 0===n[t]&&(n[t]=[]),-1===n[t].indexOf(e)&&n[t].push(e)}hasEventListener(t,e){if(void 0===this._listeners)return!1;const n=this._listeners;return void 0!==n[t]&&-1!==n[t].indexOf(e)}removeEventListener(t,e){if(void 0===this._listeners)return;const n=this._listeners[t];if(void 0!==n){const t=n.indexOf(e);-1!==t&&n.splice(t,1)}}dispatchEvent(t){if(void 0===this._listeners)return;const e=this._listeners[t.type];if(void 0!==e){t.target=this;const n=e.slice(0);for(let e=0,i=n.length;e<i;e++)n[e].call(this,t);t.target=null}}}const st=[];for(let t=0;t<256;t++)st[t]=(t<16?"0":"")+t.toString(16);let at=1234567;const ot=Math.PI/180,lt=180/Math.PI;function ct(){const t=4294967295*Math.random()|0,e=4294967295*Math.random()|0,n=4294967295*Math.random()|0,i=4294967295*Math.random()|0;return(st[255&t]+st[t>>8&255]+st[t>>16&255]+st[t>>24&255]+"-"+st[255&e]+st[e>>8&255]+"-"+st[e>>16&15|64]+st[e>>24&255]+"-"+st[63&n|128]+st[n>>8&255]+"-"+st[n>>16&255]+st[n>>24&255]+st[255&i]+st[i>>8&255]+st[i>>16&255]+st[i>>24&255]).toUpper