<!-- SOLID CITY · part 2044 -->
od, url, body) {
  return getMe().then(function (me) {
    return fetch(url, { method: method, credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': (me && me.csrf) || '' }, body: body ? JSON.stringify(body) : undefined })
      .then(function (r) { return r.json().catch(function () { return {}; }).then(function (j) { j._s = r.status; return j; }); });
  });
}
function loadWorld() {
  return fetch('/api/city/builds', { credentials: 'same-origin', cache: 'no-store' }).then(function (r) { return r.ok ? r.json() : { builds: [] }; }).then(function (j) {
    F.world.slice().forEach(dropCreation);
    (j.builds || []).forEach(function (b) {
      var cr = { sid: b.id, name: b.name, owner: b.owner, mine: !!b.mine, status: b.status, local: false, items: [] };
      (b.pieces || []).forEach(function (p) { var d = C.sanitizePiece(p); if (d) addItem(cr, d); });
      if (cr.items.length) F.world.push(cr);
    });
    if (window.CityHyper && CityHyper.regrid) CityHyper.regrid();
    F.lastKey = ''; render();
  }).catch(function () { F.loadErr = 'Could not load world builds.'; });
}
function publish() {
  var cr = F.sel && F.sel.cr.local ? F.sel.cr : F.cur && F.cur.local ? F.cur : null;
  if (!cr || !cr.items.length) return say('Nothing to publish — build a creation first.', true);
  getMe().then(function (me) {
    if (!me.loggedIn) return say('Publishing needs an epistles login (your builds stay saved on this device).', true);
    var nm = $('fg-name') ? $('fg-name').value.trim() : ''; if (nm) cr.name = nm.slice(0, L.NAME);
    say('Publishing \u201c' + cr.name + '\u201d…');
    api('POST', '/api/city/builds', { name: cr.name, pieces: cr.items.map(function (it) { return it.d; }) }).then(function (j) {
      if (j._s === 403) { F.me = null; return say('Session check failed — reload the page and try again.', true); }
      if (!j.ok) return say('Not published: ' + (j.error || ('error ' + j._s)), true);
      var i = F.bench.indexOf(cr); if (i > -1) F.bench.splice(i, 1);
      cr.local = false; cr.sid = j.build.id; cr.status = j.build.status; cr.mine = true; cr.owner = j.build.owner; F.world.push(cr); if (F.cur === cr) F.cur = null;
      F.undo.length = 0; saveBench(); updateSel(); renderSheet(); render();
      say(cr.status === 'public' ? '\u201c' + cr.name + '\u201d is live in Solid City for everyone.' : '\u201c' + cr.name + '\u201d sent for approval — only you see it until the King approves.');
    }).catch(function () { say('Connection lost — not published.', true); });
  });
}
function removeWorld(cr) {
  if (!confirm((cr.mine ? 'Delete your build \u201c' : 'Delete \u201c') + cr.name + '\u201d from the world?')) return;
  api('POST', '/api/city/builds/' + cr.sid + '/delete').then(function (j) { if (!j.ok) return say('Not deleted: ' + (j.error || j._s), true); dropCreation(cr); F.sel = null; changed(); renderSheet(); say('Deleted from the world.'); });
}
function pickUp(cr) {
  if (!confirm('Pick up \u201c' + cr.name + '\u201d? It leaves the world and comes back to your workbench to edit; publish it again when done.')) return;
  api('POST', '/api/city/builds/' + cr.sid + '/delete').then(function (j) {
    if (!j.ok) return say('Could not pick it up: ' + (j.error || j._s), true);
    var i = F.world.indexOf(cr); if (i > -1) F.world.splice(i, 1);
    cr.local = true; cr.lid = 'c' + Date.now().toString(36); delete cr.sid; cr.status = 'local'; F.bench.push(cr); F.cur = cr; changed(); renderSheet(); say('\u201c' + cr.name + '\u201d is back on your workbench.');
  });
}
function review(cr, action) {
  api('POST', '/api/city/builds/' + cr.sid + '/review', { action: action }).then(function (j) {
    if (!j.ok) return say('Review failed: ' + (j.error || j._s), true);
    if (action === 'approve') { cr.status = 'public'; say('Approved \u201c' + cr.name + '\u201d — it is public now.'); }
    else { if (cr.mine) cr.status = 'rejected'; else { dropCreation(cr); changed(); } say('Rejected \u201c' + cr.name + '\u201d.'); }
    renderSheet();
  });
}
function goTo(cr) { var u = C.unionAABB(cr.items.map(function (it) { return it.box; })); closeSheet(); SC.walk((u.x0 + u.x1) / 2, u.z1 + 12, 0); F.lastKey = ''; }

/* ---------------- UI ---------------- */
var hud = $('sc-hud'), scRoot = $('sc');
var ui = document.createElement('div'); ui.id = 'fg'; ui.hidden = true;
function bt(act, label, title, cls, arg) { return '<button type="button" class="fg-b' + (cls ? ' ' + cls : '') + '" data-fg="' + act + '"' + (arg != null ? ' data-arg="' + arg + '"' : '') + ' title="' + title + '" aria-label="' + title + '">' + label + '</button>'; }
var SW = [0xc99a3a, 0xffd27a, 0xff2a40, 0x5a0410, 0x0d0b0e, 0x2a2a30, 0xf5f5f5, 0x8e8e88, 0x6b625a, 0x8b5a2b, 0x00ccff, 0x49a6ff, 0x2f8f4e, 0x7a2e9a];
ui.innerHTML =
  '<div class="fg-bar"><b>\uD83D\uDD28 FORGE</b><span id="fg-info"></span><button type="button" class="fg-x" data-fg="exit" aria-label="Leave build mode">DONE</button><div id=