<!-- SOLID CITY · part 1003 -->
 // elevation relative to the true horizon
    ' float space = smoothstep(9000.0, 60000.0, uAlt);',
    ' vec3 zen = vec3(0.20,0.50,0.92); vec3 mid = vec3(0.50,0.74,0.96); vec3 hor = vec3(0.86,0.92,0.98);',
    ' vec3 c = mix(hor, mid, smoothstep(0.0, 0.18, e)); c = mix(c, zen, smoothstep(0.12, 0.6, e));',
    ' c = mix(c, vec3(0.05,0.03,0.04), smoothstep(0.0,-0.25,e));',
    ' float s = max(dot(normalize(vD), normalize(uSun)), 0.0);',
    ' c += vec3(1.0,0.96,0.86) * pow(s, 9.0) * 0.45 + vec3(1.0,0.98,0.92) * smoothstep(0.9993, 0.9997, s);',
    ' vec3 spc = vec3(0.0) + vec3(1.0,0.85,0.55)*smoothstep(0.9993,0.9997,s);',
    ' float limb = exp(-abs(e)*55.0);',     // thin blue-gold limb seen from orbit
    ' spc += mix(vec3(0.2,0.45,1.0), vec3(1.0,0.5,0.2), 0.35) * limb * 0.9;',
    ' gl_FragColor = vec4(mix(c, spc, space), 1.0);',
    '}'].join('\n')
}));
sky.frustumCulled = false; sky.renderOrder = -10; scene.add(sky);
var stars = (function () {
  var N = MOBILE ? 2600 : 4200, G = MOBILE ? 4200 : 7000, pos = new Float32Array((N + G) * 3), col = new Float32Array((N + G) * 3), r2 = mulberry(77);
  var v = new T.Vector3(), c = new T.Color();
  for (var k = 0; k < N; k++) {
    v.set(r2() * 2 - 1, r2() * 2 - 1, r2() * 2 - 1).normalize(); pos.set([v.x, v.y, v.z], k * 3);
    var b = 0.45 + r2() * 0.55; c.setRGB(b, b * (0.85 + r2() * 0.15), b * (0.8 + r2() * 0.25)); col.set([c.r, c.g, c.b], k * 3);
  }
  // galaxy band: a tilted great circle with gaussian spread, red/gold/violet
  var ax = new T.Vector3(0.3, 0.85, 0.42).normalize(), u = new T.Vector3(1, 0, 0).cross(ax).normalize(), w = ax.clone().cross(u);
  for (k = 0; k < G; k++) {
    var a = r2() * Math.PI * 2, sp = (r2() + r2() + r2() - 1.5) * 0.16 * (1 + 0.6 * Math.sin(a * 3));
    v.copy(u).multiplyScalar(Math.cos(a)).addScaledVector(w, Math.sin(a)).addScaledVector(ax, sp).normalize();
    pos.set([v.x, v.y, v.z], (N + k) * 3);
    var h = r2(); c.setRGB(h < .4 ? 1 : h < .75 ? 0.95 : 0.7, h < .4 ? 0.55 : h < .75 ? 0.82 : 0.55, h < .4 ? 0.5 : h < .75 ? 0.5 : 1); c.multiplyScalar(0.35 + r2() * 0.5 * (1 - Math.abs(sp) * 3));
    col.set([c.r, c.g, c.b], (N + k) * 3);
  }
  var g = new T.BufferGeometry(); g.setAttribute('position', new T.BufferAttribute(pos, 3)); g.setAttribute('color', new T.BufferAttribute(col, 3));
  var m = new T.Points(g, new T.PointsMaterial({ size: MOBILE ? 1.6 : 1.4, sizeAttenuation: false, vertexColors: true, transparent: true, opacity: 0.4, depthWrite: false, fog: false }));
  m.frustumCulled = false; m.renderOrder = -9; scene.add(m);
  return m;
})();

/* ---------------- textures (canvas, no downloads) ---------------- */
function canvasTex(w, h, draw, repeat) {
  var c = document.createElement('canvas'); c.width = w; c.height = h; draw(c.getContext('2d'), w, h);
  var t = new T.CanvasTexture(c); t.encoding = T.sRGBEncoding; t.anisotropy = 4;
  if (repeat) { t.wrapS = t.wrapT = T.RepeatWrapping; t.repeat.set(repeat[0], repeat[1]); }
  return t;
}
var r3 = mulberry(4242);
// Solidivism motherboard floor
var TEX_BOARD = canvasTex(512, 512, function (g, w, h) {
  g.fillStyle = '#0a0809'; g.fillRect(0, 0, w, h);
  g.strokeStyle = 'rgba(120,20,30,.55)'; g.lineWidth = 2;
  for (var k = 0; k < 70; k++) { var x = r3() * w, y = r3() * h; g.beginPath(); g.moveTo(x, y); var hz = r3() < .5; var L = 40 + r3() * 160; g.lineTo(hz ? x + L : x, hz ? y : y + L); g.lineTo(hz ? x + L + 20 : x + 20, hz ? y + 20 : y + L + 20); g.stroke(); g.fillStyle = '#c99a3a'; g.fillRect(x - 3, y - 3, 6, 6); }
  g.strokeStyle = 'rgba(232,195,106,.18)'; g.lineWidth = 1; for (k = 0; k <= 8; k++) { g.beginPath(); g.moveTo(k * 64, 0); g.lineTo(k * 64, h); g.moveTo(0, k * 64); g.lineTo(w, k * 64); g.stroke(); }
  for (k = 0; k < 14; k++) { g.fillStyle = '#151214'; var cw = 30 + r3() * 50; g.fillRect(r3() * w, r3() * h, cw, cw * .6); }
}, [6, 6]);
// tower windows (emissive)
var TEX_WIN = canvasTex(128, 256, function (g, w, h) {
  g.fillStyle = '#000'; g.fillRect(0, 0, w, h);
  for (var y = 4; y < h; y += 10) for (var x = 4; x < w; x += 10) { var p = r3(); if (p < 0.3) { g.fillStyle = p < 0.08 ? '#ff3048' : p < 0.22 ? '#e8b04a' : '#5a3a20'; g.fillRect(x, y, 6, 5); } }
}, [1, 1]);
var TEX_BLDG = canvasTex(128, 256, function (g, w, h) {
  g.fillStyle = '#121014'; g.fillRect(0, 0, w, h);
  for (var y = 0; y < h; y += 10) { g.fillStyle = 'rgba(255,255,255,.035)'; g.fillRect(0, y, w, 1); }
  g.fillStyle = '#2a0a10'; g.fillRect(0, 0, 3, h); g.fillRect(w - 3, 0, 3, h);
}, [1, 1]);
function labelTex(text, sub, w, h) {
  return canvasTex(w || 512, h || 128, function (g, W, H) {
    g.fillStyle = 'rgba(8,4,8,.88)'; g.fillRect(0, 0, W, H);
    g.strokeStyle = '#e8c36a'; g.lineWidth = 6; g.strokeRect(4, 4, W - 8, H - 8);
    g.fillStyle = '#e8c36a'; g.textAlign = 'center'; g.font = '700 ' + Math.round(H * 0.36) + 'px Georgia, serif'; g.fillText(text, W / 2, sub ? H * 0.5 : H * 0.64);
    if (sub) { g.fillStyle = '#ff4d5e'; g.font = '800 ' + Math.roun