<!-- SOLID CITY · part 82 -->
' + f.fam + '";src:url(' + d + ');font-weight:' + f.weight + ';font-style:' + f.style + ';}' : ''; }); })).then(function (a) { return a.join(''); });
}
function drawSvg(ctx, L, scale, ox, oy, fonts) {
  var root = document.createElement('div'); root.setAttribute('xmlns', XHTML);
  root.setAttribute('style', 'position:relative;width:' + L.w + 'px;height:' + L.h + 'px;transform:scale(' + scale + ');transform-origin:0 0;overflow:hidden;margin:0;padding:0;font-family:system-ui,-apple-system,sans-serif;');
  root.appendChild(L.clone);
  var xml = new XMLSerializer().serializeToString(root);
  var W = Math.ceil(L.w * scale), H = Math.ceil(L.h * scale);
  var svg = '<svg xmlns="' + SVGNS + '" width="' + W + '" height="' + H + '" viewBox="0 0 ' + W + ' ' + H + '"><foreignObject x="0" y="0" width="' + W + '" height="' + H + '">' + (fonts ? '<style xmlns="' + XHTML + '">' + fonts + '</style>' : '') + xml + '</foreignObject></svg>';
  return new Promise(function (res, rej) {
    var img = new Image(); img.decoding = 'sync';
    img.onload = function () { try { ctx.drawImage(img, ox + L.x * scale, oy + L.y * scale, W, H); res(); } catch (e) { rej(e); } };
    img.onerror = function () { rej(new Error('layer image failed')); };
    img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
  });
}
function blurRegion(ctx, g, scale, ox, oy) {
  var x = Math.round(ox + g.x * scale), y = Math.round(oy + g.y * scale), w = Math.round(g.w * scale), h = Math.round(g.h * scale); if (w < 2 || h < 2) return;
  var rad = g.blur * scale, f = Math.max(2, Math.min(24, rad / 2)), sw = Math.max(1, Math.round(w / f)), sh = Math.max(1, Math.round(h / f)), m = Math.ceil(rad);
  var t = document.createElement('canvas'); t.width = sw + 4; t.height = sh + 4; var tx = t.getContext('2d'); tx.imageSmoothingEnabled = true; tx.imageSmoothingQuality = 'high';
  tx.drawImage(ctx.canvas, x - m, y - m, w + 2 * m, h + 2 * m, 0, 0, sw + 4, sh + 4);                // downscale = box blur
  var t2 = document.createElement('canvas'); t2.width = Math.max(1, sw >> 1) + 2; t2.height = Math.max(1, sh >> 1) + 2; var t2x = t2.getContext('2d'); t2x.imageSmoothingQuality = 'high'; t2x.drawImage(t, 0, 0, t2.width, t2.height);
  ctx.save(); ctx.beginPath(); var r = g.r * scale; if (ctx.roundRect) ctx.roundRect(x, y, w, h, r); else ctx.rect(x, y, w, h); ctx.clip();
  ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'high'; ctx.drawImage(t2, x - m, y - m, w + 2 * m, h + 2 * m); ctx.restore();
}
/* top-level layers: visible children of the HUD and the #sc overlays, in paint order */
function layers(exclude) {
  var sc = document.getElementById('sc') || document.body, out = [], idx = 0;
  function z(el) { var v = parseInt(getComputedStyle(el).zIndex, 10); return isNaN(v) ? 0 : v; }
  [].forEach.call(sc.children, function (top) {
    if (top.tagName === 'CANVAS' || exclude(top) || !top.getClientRects().length) return;
    if (top.id === 'sc-hud') { var hz = z(top); [].forEach.call(top.children, function (el) { idx++; if (!exclude(el) && shown(el)) out.push({ el: el, k: [hz, z(el), idx] }); }); }
    else { idx++; if (shown(top)) out.push({ el: top, k: [z(top), 0, idx] }); }
  });
  [].forEach.call(document.body.children, function (el) { if (el !== sc && !exclude(el) && /^(DIV|SECTION|ASIDE|NAV|DIALOG)$/.test(el.tagName) && getComputedStyle(el).position !== 'static' && shown(el)) out.push({ el: el, k: [z(el), 0, ++idx] }); });
  out.sort(function (a, b) { return a.k[0] - b.k[0] || a.k[1] - b.k[1] || a.k[2] - b.k[2]; });
  return out.map(function (o) { return o.el; });
}
/* compose the visible HUD + panels over a world canvas. box: where the viewport maps into the output (contain-fit). */
function compose(ctx, W, H, opts) {
  opts = opts || {}; var vw = innerWidth, vh = innerHeight, s = Math.min(W / vw, H / vh), ox = (W - vw * s) / 2, oy = (H - vh * s) / 2;
  var exclude = opts.exclude || function () { return false; }, issues = [], drawn = 0, t0 = performance.now();
  var list = layers(exclude), jobs = list.map(function (el) {
    var pend = [], job = { issues: issues, glass: [], exclude: exclude,
      img: function (src, node) { if (!src) return; pend.push(toDataURL(src).then(function (d) { if (d) node.setAttribute('src', d); else { node.removeAttribute('src'); issues.push('an image'); } })); },
      bg: function (cs, node) { var bi = cs.backgroundImage; if (!bi || bi.indexOf('url(') < 0) return; var urls = []; bi.replace(/url\(["']?([^"')]+)["']?\)/g, function (m, u) { urls.push(u); });
        pend.push(Promise.all(urls.map(toDataURL)).then(function (ds) { var i = 0, v = bi.replace(/url\(["']?([^"')]+)["']?\)/g, function () { var d = ds[i++]; return d ? 'url("' + d + '")' : 'none'; }); node.setAttribute('style', (node.getAttribute('style') || '').replace(/background-image:[^;]*;/, 'background-image:' + v + ';')); })); } };
    var L = null; try { L = layerSvg(el, s, job); } catch (e) { issues.push((el.id || 