<!-- SOLID CITY · part 10 -->
              first-line replies cached 1h. Uses the SAME server-side Gemini pool as SABLE
//                            (deps.brain = sableBrainFetch); the key never reaches the browser. When a cap
//                            is hit or no brain is wired, it answers {fallback:true} and the client plays
//                            that citizen's scripted lines. NEVER relays anything to the real bots.
//   GET  /api/city/pilots -> 🎮 AI PILOTS (lib/city-pilots.js): public pilot states + anonymous presence; pilots steer via /city/see?as=&key=.
//   GET  /api/city/provolta/prophecies -> ✧ PROVOLTA's shared prophecy pool (public; AI lines via czBudget, max 1 per 20 min site-wide, fallback = her page's quotes).
//   GET  /api/city/citizens/chatter -> last few AI radio lines (shared by everyone); a logged-in
//                            visit may trigger at most ONE new line per 10 min site-wide.
// The MP3s themselves are served untouched by express.static from
// public/SolidBigIsland/mp3/ (Accept-Ranges/206); mediaCache below only adds
// a 7-day Cache-Control so replays and next-track preloads hit the cache.
const COVERS_PUBLIC = String(process.env.CITY_COVERS_PUBLIC || '') === '1';

module.exports = function mountCity(app, deps) {
  const { requireLogin, rateLimit } = deps;
  const songsPath = path.join(deps.rootDir || path.join(__dirname, '..'), 'public', 'SolidBigIsland', 'songs.json');
  let songsCache = null, songsMtime = 0;
  function loadSongs() {
    try {
      const st = fs.statSync(songsPath);
      if (!songsCache || st.mtimeMs !== songsMtime) {
        const j = JSON.parse(fs.readFileSync(songsPath, 'utf8'));
        songsCache = { artist: j.artist || 'SolidBigIsland', updated: j.updated || null, songs: Array.isArray(j.songs) ? j.songs : [] };
        songsMtime = st.mtimeMs;
      }
    } catch (e) { console.error('[city] songs.json:', e.message); if (!songsCache) songsCache = { artist: 'SolidBigIsland', songs: [] }; }
    return songsCache;
  }
  const canSeeCovers = (req) => COVERS_PUBLIC || !!(req.user && req.user.is_admin);
  const safeFile = (f) => /^[A-Za-z0-9_.-]+\.mp3$/.test(String(f || ''));

  app.get('/api/city/songs', (req, res) => {
    const data = loadSongs(), covers = canSeeCovers(req);
    const list = data.songs.filter(s => safeFile(s.file) && (covers || s.kind !== 'cover')).map(s => ({
      n: s.n, title: String(s.title || 'Untitled').slice(0, 140), kind: s.kind || 'lyric', dur: s.dur || 0,
      file: s.file, thumb: /^thumbs\/[A-Za-z0-9_.-]+\.webp$/.test(s.thumb || '') ? s.thumb : null,
      lyr: !!(s.lyrics && String(s.lyrics).trim()), url: /^https:\/\/www\.tiktok\.com\//.test(s.url || '') ? s.url : null,
    }));
    res.set('Cache-Control', 'private, max-age=300');
    res.json({ artist: data.artist, updated: data.updated, covers: covers, hiddenCovers: covers ? 0 : data.songs.filter(s => s.kind === 'cover').length, songs: list });
  });

  app.get('/api/city/songs/:n/lyrics', (req, res) => {
    const n = parseInt(req.params.n, 10), s = loadSongs().songs.find(x => x.n === n);
    if (!s || (s.kind === 'cover' && !canSeeCovers(req))) return res.status(404).json({ error: 'not found' });
    res.set('Cache-Control', 'private, max-age=3600');
    res.json({ n: s.n, title: s.title, lyrics: String(s.lyrics || '').slice(0, 20000) });
  });

  /* /3D — THE NEW MAP (TOP SECRET, the King's word, 2026-10-14 ~17:56
     HST). The seed lives OUTSIDE the web root (/opt/epistles-private/3d)
     and is served only through this gate: the King's account alone
     (royal.isKing — decided from the account record server-side, never
     a client claim). To everyone else the address does not exist:
     404, and no sign anywhere points to it. */
  const TD_DIR = '/opt/epistles-private/3d';
  const TD_TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.webp': 'image/webp', '.png': 'image/png', '.json': 'application/json; charset=utf-8' };
  app.get(['/3D', '/3D/*'], (req, res) => {
    if (!royal.isKing(req)) return res.status(404).type('text').send('not found\n');
    let rel = req.path === '/3D' ? '/index.html' : req.path.slice(3);
    if (rel.indexOf('..') !== -1 || !/^\/[A-Za-z0-9._\/-]+$/.test(rel)) return res.status(404).type('text').send('not found\n');
    const ext = rel.slice(rel.lastIndexOf('.'));
    fs.readFile(TD_DIR + rel, (err, buf) => {
      if (err) return res.status(404).type('text').send('not found\n');
      res.set('Cache-Control', 'no-store');
      res.type(TD_TYPES[ext] || 'application/octet-stream').send(buf);
    });
  });

  app.get('/api/city/me', (req, res) => {
    res.set('Cache-Control', 'no-store');
    if (!req.user) return res.json({ loggedIn: false });
    res.json({
      loggedIn: true,
      name: String(req.user.display_name || req.user.username || 'captain').slice(0, 60),
      csrf: req.user.csrfToken || null,
      admin: !!req.user.is_adm