diff --git a/README.md b/README.md
index c54385d..e44f108 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,92 @@
-# flexhub
+# FlexHub
-FlexHub — where I flex my projects, code, and stuff
\ No newline at end of file
+> Where I flex my projects, code, and stuff. A place to flex.
+
+FlexHub is the brand wrapped around the operator's self-hosted Forgejo
+instance. Same git host, same backing store — different paint job.
+
+- **Canonical URL:** (admin / push / clone)
+- **FlexHub-branded entry:** (public showcase)
+
+Both hostnames hit the same Forgejo container on `nullstone`. Repos,
+PATs, SSH keys, issues — all identical. Login sessions are independent
+(separate cookies per hostname).
+
+This repository holds the customisations that turn vanilla Forgejo into
+FlexHub: the homepage template, the theme CSS, the logo set, and the
+deploy notes that wire it all together.
+
+---
+
+## What's in this repo
+
+| Path | Purpose |
+| ------------------------------------------------ | ------------------------------------------------------------------------ |
+| `templates/home.tmpl` | FlexHub homepage. Wordmark + tagline + 4-card "agent grid" feature list. |
+| `public/assets/css/theme-flexhub.css` *(planned)* | The orange/black/white theme. `DEFAULT_THEME = flexhub` in `app.ini`. |
+| `public/assets/img/` *(planned)* | Logo + wordmark + favicons (`logo.svg`, `flexhub-wordmark.png`, etc). |
+| `docs/DEPLOY.md` | How to roll a change out to the live `nullstone` Forgejo container. |
+| `LICENSE` | MIT. |
+
+The repo is currently template-only; assets get checked in as the
+operator pulls them out of the live Forgejo `custom/` tree. The
+authoritative copies live on `nullstone` until then.
+
+---
+
+## How it's wired up
+
+Forgejo loads any file under `custom/` to override the equivalent
+built-in. On `nullstone` that directory is bind-mounted from the host:
+
+```
+/home/docker/forgejo/data/gitea/ → /var/lib/gitea/ (in-container)
+ └─ custom/
+ ├─ conf/app.ini
+ ├─ templates/home.tmpl ← from this repo
+ ├─ public/assets/css/... ← from this repo
+ └─ public/assets/img/... ← from this repo
+```
+
+The Forgejo container itself is described by the compose file at
+`/opt/docker/forgejo/docker-compose.yml`. Container data + customisations
+live under `/home/docker/forgejo/` because the root volume on nullstone
+is small.
+
+Key settings in `app.ini` that drive the rebrand:
+
+```ini
+APP_NAME = FlexHub
+DEFAULT_THEME = flexhub
+THEMES = forgejo-auto,forgejo-light,forgejo-dark,flexhub,veilor
+```
+
+The two hostnames are split at the Traefik layer, not Forgejo:
+
+- `git.s8n.ru` — docker-provider router, gated by `no-guest@file` ACL.
+- `flexhub.s8n.ru` — file-provider router, **public**, no `no-guest`,
+ rate-limited login. Service `flexhub-svc` points at `http://forgejo:3000`
+ directly (avoids the file→docker provider race; see DEPLOY.md).
+
+---
+
+## Contributing
+
+Don't. This is the operator's personal git host — accounts are
+invite-only, registration is disabled. If you've found a real bug in
+Forgejo itself, file it upstream at
+. If you've found a typo on the
+homepage, open an issue here and the operator will get to it.
+
+Public read access is intentional: browse repos, clone what's MIT or
+AGPL, mirror what you like. That's the "flex" part.
+
+---
+
+## See also
+
+- **Deploy + update workflow:** [`docs/DEPLOY.md`](docs/DEPLOY.md)
+- **Initial Forgejo bring-up runbook (operator-internal):**
+ `_github/infra/forgejo/DEPLOY.md` in the `ai-lab` repo.
+- **ARRFLIX project** references `flexhub.s8n.ru` as a mirror — that's
+ this instance.
diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md
new file mode 100644
index 0000000..1669334
--- /dev/null
+++ b/docs/DEPLOY.md
@@ -0,0 +1,192 @@
+# Deploying FlexHub customisations
+
+These notes cover **updating an already-running** FlexHub. If you're
+standing up Forgejo from scratch on a new host, follow the
+operator-internal runbook at `_github/infra/forgejo/DEPLOY.md` in the
+`ai-lab` repo first, then come back here for the rebrand layer.
+
+All paths below are on `nullstone` (the host running the Forgejo
+container). The container is named `forgejo`. Host UID 1000 owns the
+data tree.
+
+---
+
+## Layout on nullstone
+
+```
+/opt/docker/forgejo/
+└── docker-compose.yml ← image tag, env, traefik labels
+
+/home/docker/forgejo/
+├── data/
+│ └── gitea/
+│ └── custom/ ← Forgejo "override" tree
+│ ├── conf/app.ini ← APP_NAME, DEFAULT_THEME, THEMES
+│ ├── templates/
+│ │ └── home.tmpl ← homepage (mirror of this repo)
+│ └── public/assets/
+│ ├── css/theme-flexhub.css
+│ └── img/ ← logos + favicons
+└── config/ ← Forgejo-managed; don't hand-edit
+```
+
+`custom/` is the only path you ever edit by hand. Everything in this
+repo maps 1:1 into that tree.
+
+---
+
+## Update the homepage template
+
+Edit `templates/home.tmpl` here, then on `nullstone`:
+
+```bash
+ssh nullstone bash <<'EOF'
+sudo cp /tmp/home.tmpl /home/docker/forgejo/data/gitea/custom/templates/home.tmpl
+sudo chown 1000:1000 /home/docker/forgejo/data/gitea/custom/templates/home.tmpl
+EOF
+```
+
+Forgejo hot-reloads templates — no container restart needed. Hit
+ and hard-refresh (Ctrl+Shift+R) to confirm.
+
+If the page renders blank, check the Forgejo log for a template parse
+error:
+
+```bash
+ssh nullstone 'docker logs --tail 50 forgejo 2>&1 | grep -i template'
+```
+
+The wrapping `{{template "base/head" .}}` and `{{template "base/footer" .}}`
+calls are mandatory — drop them and you get a half-page.
+
+---
+
+## Update the theme CSS
+
+Edit `public/assets/css/theme-flexhub.css`, copy to nullstone:
+
+```bash
+ssh nullstone bash <<'EOF'
+sudo cp /tmp/theme-flexhub.css \
+ /home/docker/forgejo/data/gitea/custom/public/assets/css/theme-flexhub.css
+sudo chown 1000:1000 \
+ /home/docker/forgejo/data/gitea/custom/public/assets/css/theme-flexhub.css
+EOF
+```
+
+CSS is served static — no reload needed. The theme is selected
+per-user via `DEFAULT_THEME = flexhub` in `app.ini` (see next section)
+and is overridable in user settings.
+
+The brand palette is **orange `#FF9000` / black `#000` / white `#FFF`**.
+Don't drift.
+
+---
+
+## Bump APP_NAME or theme list
+
+`app.ini` lives at `/home/docker/forgejo/data/gitea/custom/conf/app.ini`
+on nullstone (Forgejo merges custom values over its built-in defaults).
+The relevant keys:
+
+```ini
+APP_NAME = FlexHub
+DEFAULT_THEME = flexhub
+THEMES = forgejo-auto,forgejo-light,forgejo-dark,flexhub,veilor
+```
+
+After editing `app.ini` you **must restart the container** — env-style
+INI is read at boot only:
+
+```bash
+ssh nullstone 'cd /opt/docker/forgejo && docker compose restart forgejo'
+```
+
+Watch the log come back clean:
+
+```bash
+ssh nullstone 'docker logs -f --tail 30 forgejo'
+```
+
+You're good when you see `Listen: http://0.0.0.0:3000`. The web UI
+should now show the new title bar / theme list.
+
+---
+
+## Add a new template override
+
+Forgejo template hierarchy:
+.
+The general flow:
+
+1. Find the upstream template in the Forgejo source tree
+ (`templates/...`).
+2. Copy its full content into `custom/templates/.tmpl` on
+ nullstone, owned `1000:1000`.
+3. Edit your override.
+4. Mirror the file into this repo under `templates/.tmpl`
+ so the repo stays the source of truth.
+
+Hot-reload applies — no container restart.
+
+If the override stops rendering after a Forgejo version bump, diff
+your override against the new upstream template — base layout slots
+sometimes get renamed across major versions.
+
+---
+
+## Add a new image asset
+
+```bash
+scp my-asset.png nullstone:/tmp/
+ssh nullstone bash <<'EOF'
+sudo cp /tmp/my-asset.png \
+ /home/docker/forgejo/data/gitea/custom/public/assets/img/my-asset.png
+sudo chown 1000:1000 \
+ /home/docker/forgejo/data/gitea/custom/public/assets/img/my-asset.png
+EOF
+```
+
+Reachable at `https://flexhub.s8n.ru/assets/img/my-asset.png`. No
+container restart.
+
+When replacing favicons specifically, also clear the browser cache —
+favicons are aggressively cached.
+
+---
+
+## Traefik routing for `flexhub.s8n.ru`
+
+The FlexHub-branded hostname is wired in via a **file-provider** router
+(`/opt/docker/traefik/config/dynamic.yml` on nullstone), not via the
+docker-provider labels on the Forgejo compose file. This is intentional:
+
+- `git.s8n.ru` — docker-provider router, `no-guest@file` middleware.
+- `flexhub.s8n.ru` — file-provider router, public, rate-limit only.
+
+The file-provider router defines its **own** service
+(`flexhub-svc`) pointing at `http://forgejo:3000` directly, rather than
+referencing `forgejo@docker`. This works around a startup race where
+file-provider routers can resolve before docker-provider services are
+populated, leaving the router permanently broken with
+`the service "forgejo@docker" does not exist`.
+
+If you change the Forgejo container name or move it off the `proxy`
+network, update `flexhub-svc.loadBalancer.servers[0].url` in
+`dynamic.yml` to match.
+
+---
+
+## Rollback
+
+The customisations are pure overlay files. To revert FlexHub branding:
+
+```bash
+ssh nullstone bash <<'EOF'
+sudo mv /home/docker/forgejo/data/gitea/custom /home/docker/forgejo/data/gitea/custom.disabled
+EOF
+ssh nullstone 'cd /opt/docker/forgejo && docker compose restart forgejo'
+```
+
+Forgejo falls back to vanilla theme + homepage. Nothing in the data
+store changes. Restore by renaming `custom.disabled` back to `custom`.