Go **Shape A**. Move Vite and FastAPI to the MacBook. 

Interactive creative review lives and dies on latency. A 30ms Tailscale tax on dozens of sequential API calls per minute turns flow state into sludge. The Studio is a workhorse; treat it like one. Let the MacBook handle the UI loop at `localhost` speed.

Here is how you execute it without shooting yourself in the foot.

### 1. The Mutation Question: "Mutex by Convention"
Do **not** build a complex read-replica routing layer where local reads are fast but local writes are proxied to the Studio. That is over-engineering for a single-user tool. 

Make the setup **symmetric** (both APIs can write to the JSON files), but rely on a strict human protocol: **MacBook owns the day, Studio owns the night.**
* When you are actively reviewing/rating on the MacBook, the Studio's API is idle. The MacBook FastAPI mutates the JSON. Dropbox syncs it back to the Studio.
* When you close the laptop, the Studio's overnight daemons wake up, mutate the JSON, and generate media. 
* Dropbox conflict files (`shot_1 (JT's Macbook conflict).json`) only happen if both machines write *concurrently*. Since you are the only user, you are the mutex. 

### 2. The `ttyd` Host Config
**The API should tell the frontend at runtime.** 
Do not bake `VITE_TTYD_HOST` into the frontend build. If you ever change Tailscale IPs, work offline, or test a different machine, you don't want to restart Vite to fix the chat iframe. 

Put `STUDIO_HOST=100.105.59.118` in your MacBook's FastAPI `.env`. Add a property to your `/api/system-status` or project metadata endpoint that returns the fully constructed `ttyd` URL. The frontend just blindly binds the iframe to whatever the API hands it.

### 3. The Unseen Gotchas

**The JSON-Media Race Condition**
This is your biggest incoming headache. The Studio finishes a generation. It writes a 50MB MP4 to Dropbox, then immediately writes a 1KB JSON file. Dropbox syncs the 1KB JSON to your MacBook almost instantly. The MacBook UI sees the new take, tries to load `take_42.mp4`, and **404s** because Dropbox is still downloading the 50MB file. 
* *Fix:* Your frontend needs a graceful fallback. If `<video src="...">` fails to load, show a "Syncing from Studio..." placeholder and poll, rather than treating it as a hard error.

**Python Environment Drift**
You now have two `recoil/.venv` directories. You will `pip install` a new dependency on the Studio, push the code, pull it on the MacBook, and the local FastAPI will crash on boot. Get in the habit of syncing `requirements.txt` religiously.

**Absolute Path Poisoning**
Comb through your backend for anything hardcoding `/Users/jt/...` vs `/Users/jt-studio/...`. If the JSON state files accidentally serialize absolute paths to media assets instead of relative paths, the MacBook API will fail to resolve them.

**Vite HMR Thrashing**
When the Studio runs an overnight batch, Dropbox will stream hundreds of file changes to the MacBook. Vite's file watcher will see this and might trigger massive, continuous Hot Module Reloads if it's watching the `projects/` directory. Ensure `projects/` is strictly excluded from Vite's `watch` config. 

### 4. The Concrete First Step
Don't rip up the Studio configuration yet. Leave the Studio exactly as it is. 

On the MacBook:
1. Boot the FastAPI locally on a *different* port (e.g., `:8432`).
2. Boot Vite locally, pointing its proxy to `localhost:8432`.
3. Hardcode the `ttyd` iframe to the Studio's IP temporarily.

Open `localhost:5173`. Click around. Feel the latency drop. Make a single rating mutation and watch Dropbox sync it to the Studio. Once you confirm the speed is worth it (it will be) and the Dropbox sync latency doesn't break your workflow, you can formally promote the MacBook to the primary interactive API and relegate the Studio to headless daemon duty.