# Add a city build (Ibadan Life, Osogbo Life) to this project

Goal: Turn this game into the ONE HTML file a city (Ibadan Life or Osogbo Life) takes for its arcade, without changing how the game normally builds.

You are a coding agent working inside my project. Do the work. Do not ask me anything you can find out by reading the project. Do not invent an endpoint, a field or an error code that is not written below.

## 1. Look at the project first

- Find the framework and language, how the project is run and tested, and how the game is built, which files it loads, every place it reaches the network (fetch, an SDK, a score server), every place it uses browser storage, and its web fonts and pictures.
- I am not a programmer. Do every step yourself, and explain what you found and what you changed in plain words, with no jargon.
- Find what the game is made with. A plain HTML/JavaScript game or a Vite project can be made into one file. A Unity or Godot web export cannot: say so plainly and stop.
- If the project already has an Arena54 client or key variable, reuse it.
- Tell me in two or three lines what you found and what you will change, then carry on.

## 2. The contract

What a city takes:

- ONE HTML file, at most 200,000 characters, with every script in <script> and every style in <style>.
- It runs in a locked frame with NO network: nothing may be loaded from another address (no scripts, stylesheets, fonts or pictures by URL; pictures as data: URLs only).
- The city's review refuses the file on sight if these words appear anywhere in it, even in code that never runs: fetch, XMLHttpRequest, WebSocket, EventSource, sendBeacon, importScripts, localStorage, sessionStorage, indexedDB, document.cookie, or a <form>.
- No pop-up boxes (alert, confirm, prompt). Sound only after the first tap. Designed for a screen about 360 px wide and 416 px tall; it must work with touch alone.

The city's bridge (window.ibadan, the same in every city; every call returns a promise):

- ibadan.player() gives { name } of the person playing.
- ibadan.storage.get(key) and ibadan.storage.set(key, value): a small save per player, in place of localStorage. Text only.
- ibadan.score(n): report a whole-number score from 0 to 1,000,000,000 to the city's board; it answers { best }. At most one score every 20 seconds. Tick the Report a score permission when you send the game.
- ibadan.toast(text) shows a short message; ibadan.close() closes the game.
- Outside a city, window.ibadan does not exist: the game must still run (no scores, no saves).
## 3. Do this, in order

1. Add a separate city build, keeping the normal build exactly as it is: for Vite, a `city` mode (`vite build --mode city`) and an npm script `npm run city`.
2. In the city build, swap every module that reaches the network or uses browser storage for a small stand-in that uses the bridge above instead (for Vite, resolve.alias in the city mode only). Anything that needs a server, such as Arena54 sign-in, leaderboards or a score server, is switched off; if the server checked scores, check them in the page instead and report the result with ibadan.score.
3. Leave web fonts out of the city build (the device's own fonts show instead), unless one font file still fits the size limit as a data: URL.
4. Turn off Vite's modulepreload polyfill in the city mode (build.modulePreload.polyfill = false): it contains the word fetch.
5. After the build, inline the one stylesheet and the one script into the HTML (escape any </script> inside the code) and write the result to `city/app.html`.
6. Make an icon, `city/icon.png`: PNG, JPEG or WebP, square, at most 40,000 bytes. Draw it with the game's own artwork if it has any.

## 4. Prove it works

Check the file the way the city's review will see it.

- Print the length of `city/app.html` as JavaScript counts it (the string's length): at most 200,000.
- Search it for each refused word in the list above, and for src=, href= or url( pointing at another address. There should be none.
- Write `city/harness.html`: an iframe with sandbox="allow-scripts" whose page starts with a Content-Security-Policy meta tag of "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:; font-src data:; media-src data:; connect-src 'none'" and a stand-in window.ibadan (player, storage, score, toast, close, each returning a promise), followed by app.html. Serve the folder (for example python3 -m http.server), open it, play once to the end, and check there are no errors in the console and the score reached the stand-in.
- Check the icon's size in bytes.

Show me the output of each check.

## 5. Rules you must not break

- Never change how the game normally builds or plays: the city build is extra.
- Never put any key, token or password in the file.
- Do not submit anything or sign in anywhere. I send it myself from the Arena54 dashboard.
- Keep what you tell me short and plain. I am not a programmer.

## 6. Done when

- `npm run city` makes `city/app.html`, under 200,000 characters, with none of the refused words.
- It plays to the end in the locked harness with no network and no console errors, and reports its score through ibadan.score.
- `city/icon.png` exists and is under 40,000 bytes.

Then give me a short list of the files you changed and anything I must do by hand.

For a person, not needed for this task: https://developers.arena54.com/docs · This prompt: https://developers.arena54.com/prompts/city-ready.web.md
