<!-- SOLID CITY · part 84 -->
erTarget) { rt = new T.WebGLMultisampleRenderTarget(w, h, opts); rt.samples = 4; ms = true; } else rt = new T.WebGLRenderTarget(w, h, opts);
  rt.texture.encoding = R.outputEncoding;                                     // same program cache key as on screen
  var prev = R.getRenderTarget(), buf = new Uint8Array(w * h * 4);
  try {
    cam.aspect = w / h; cam.updateProjectionMatrix();
    R.setRenderTarget(rt); R.clear(); R.render(A.scene, cam);
    R.readRenderTargetPixels(rt, 0, 0, w, h, buf);
  } finally { R.setRenderTarget(prev); cam.aspect = oldAspect; cam.updateProjectionMatrix(); rt.dispose(); }
  return { buf: buf, msaa: ms };
}
function toCanvas(px, w, h, wm) {
  var c = document.createElement('canvas'); c.width = w; c.height = h; var x = c.getContext('2d'), img = x.createImageData(w, h), d = img.data, row = w * 4, b = px.buf;
  for (var y = 0; y < h; y++) d.set(b.subarray((h - 1 - y) * row, (h - y) * row), y * row);  // GL rows are bottom-up
  for (var i = 3; i < d.length; i += 4) d[i] = 255;                                            // opaque, never a see-through sky
  x.putImageData(img, 0, 0); px.buf = null;
  if (wm) watermark(c);
  return c;
}
function watermark(c) {
  var w = c.width, h = c.height, x = c.getContext('2d');
  {
    var fs = Math.max(12, Math.round(Math.min(w, h) * 0.026)), pad = Math.round(fs * 0.9);
    x.save(); x.font = '800 ' + fs + 'px system-ui,-apple-system,Segoe UI,sans-serif'; x.textAlign = 'right'; x.textBaseline = 'alphabetic';
    var t = 'EPISTLES.XYZ'; try { x.letterSpacing = Math.round(fs * 0.18) + 'px'; } catch (e) {}
    x.shadowColor = 'rgba(0,0,0,.65)'; x.shadowBlur = Math.round(fs * 0.35); x.fillStyle = 'rgba(232,195,106,.62)'; x.fillText(t, w - pad, h - pad);
    x.shadowBlur = 0; x.fillStyle = 'rgba(227,18,44,.55)'; x.fillRect(w - pad - x.measureText(t).width, h - pad + Math.round(fs * 0.25), Math.round(fs * 1.6), Math.max(2, Math.round(fs * 0.1)));
    x.restore();
  }
}
function encode(c, mime) {
  return new Promise(function (res) {
    var q = mime === 'image/png' ? undefined : 0.92;
    if (c.toBlob) c.toBlob(function (b) { if (b) res(b); else fallback(); }, mime, q); else fallback();
    function fallback() { var u = c.toDataURL(mime, q), bin = atob(u.split(',')[1]), a = new Uint8Array(bin.length); for (var i = 0; i < bin.length; i++) a[i] = bin.charCodeAt(i); res(new Blob([a], { type: u.slice(5, u.indexOf(';')) })); }
  });
}
/* public: capture({type,size,wm}) -> Promise<{blob,w,h,type,requested,note,ms}> */
function capture(o) {
  o = o || {}; var type = TYPES[o.type || cfg.type] || TYPES.png, d = dims(o.size || cfg.size), wm = o.wm != null ? !!o.wm : cfg.wm, t0 = performance.now(), px;
  try { px = renderPixels(d.w, d.h); } catch (e) { return Promise.reject(e); }
  var what = o.what || curWhat(), menus = what === 'menus' && window.CityDomShot, c = toCanvas(px, d.w, d.h, wm && !menus), shot = null;
  var step = menus ? CityDomShot.compose(c.getContext('2d'), d.w, d.h, { exclude: excluded }).then(function (r) { shot = r; if (wm) watermark(c); })
    .catch(function (e) { shot = { failed: String(e && e.message || e) }; c = toCanvas(renderPixels(d.w, d.h), d.w, d.h, wm); }) : Promise.resolve();
  return step.then(function () { return encode(c, type[0]); }).catch(function (e) { if (!menus) throw e; shot = { failed: 'this browser blocked drawing the menus (' + (e && e.name || e) + ')' }; c = toCanvas(renderPixels(d.w, d.h), d.w, d.h, wm); return encode(c, type[0]); }).then(function (blob) {
    c.width = c.height = 1;
    var got = blob.type || type[0], note = d.note;
    if (shot && shot.failed) note = (note ? note + '; ' : '') + 'menus could not be drawn here, saved the world only (' + shot.failed + ')';
    else if (shot && shot.issues.length) note = (note ? note + '; ' : '') + 'skipped: ' + shot.issues.slice(0, 3).join(', ');
    if (got !== type[0]) note = (note ? note + '; ' : '') + type[1] + ' is not supported by this browser, saved as ' + (got.split('/')[1] || 'png').toUpperCase();
    return { blob: blob, w: d.w, h: d.h, type: got, requested: type[0], note: note, msaa: px.msaa, ms: Math.round(performance.now() - t0), what: menus && !(shot && shot.failed) ? 'menus' : 'world', layers: shot && !shot.failed ? shot.drawn + '/' + shot.layers : '' };
  });
}

var CAP_UI = { 'cap-panel': 1, 'cap-toast': 1, 'cap-frame': 1, 'cap-flash': 1, 'sc-boot': 1 };
function excluded(el) { return !!(el.id && CAP_UI[el.id]) || !!(el.closest && el.closest('#cap-panel')); }
var MENU_SEL = '#uni-panel,#pd-panel,#bk-reader,#sc-talk,#sp-talk,#cz-talk,#sc-portal,#w-pick,.mp-panel,#hc-card,#fg,#rf-pick,.rf-pick,.fg-sheet,#ai-panel';
function menuOpen() { var l = document.querySelectorAll(MENU_SEL); for (var i = 0; i < l.length; i++) { var e = l[i]; if (!e.hidden && e.getClientRects().length && getComputedStyle(e).display !== 'none' && getComputedStyle(e).visibility !== 'hidden') return e.id || e.className; } return ''; }
var whatNow = null