diff --git a/documentation/audits/mockups-2026-10-11/COMPARE.md b/documentation/audits/mockups-2026-10-11/COMPARE.md
new file mode 100644
index 00000000..99c8da15
--- /dev/null
+++ b/documentation/audits/mockups-2026-10-11/COMPARE.md
@@ -0,0 +1,91 @@
+# Three directions for the website, side by side
+
+Built 2026-10-11. **Nothing here is live.** All three are hidden under `/preview/`, carry
+`noindex`, are blocked by `robots.txt`, are in no sitemap and are linked from no live page.
+
+| | address |
+|---|---|
+| **A — the current redesign** | |
+| **M1 — the site is the dashboard** | |
+| **M2 — hybrid** | |
+
+Screenshots: `shots/`. Desktop at 1440 px, phone at 390 px, all from the deployed pages.
+
+## Measured, same method for all three
+
+| | A, current preview | M1, dashboard | M2, hybrid |
+|---|---:|---:|---:|
+| Requests, first load | 12 | **68** | 16 |
+| Weight, first load | 270 KB | **1 012 KB** | **150 KB** |
+| HTML document alone | 26 KB | **134 KB** | 13 KB |
+| Front page height, 1440 px | 6 385 px | **1 660 px** | 1 853 px |
+| Front page height, 390 px | 10 145 px | 6 563 px | **3 057 px** |
+| Third-party hosts | 0 | 0 | 0 |
+| Horizontal overflow at 390 px | none | none | none |
+
+M1's catalogue page (`alkalmazasok.html`) is **26 410 px tall on a phone** — 58 cards in one column.
+That is its worst measured number and it is not easily fixable while the page's job is "show the
+whole catalogue".
+
+## M1 — the site is the dashboard
+
+**What it does well.** A visitor sees the actual product in the first second, with 58 real apps, real
+logos, real Hungarian taglines and a real screenshot behind every tile. Nothing on the page is a
+promise about the product; the page *is* the product. It also makes the apps page genuinely useful
+rather than a wall of names: every tile opens what that app does for a family and how to start it.
+
+**What can go wrong.** A non-technical visitor can read a full-screen dashboard as *somebody else's
+account* and look for a login — the brief's own worry, and I think it is the real risk. The welcome
+card is the only thing saying "this is a demonstration, look around", and one card is thin defence.
+The second risk is weight: **1 MB and 68 requests** on a site that compresses nothing (R-931), four
+times the current preview, because the front page is 58 logos. The third is SEO: the front page's
+text is a welcome card and 58 app names; the sentences Google currently ranks would have to live
+somewhere else.
+
+**What it would cost to finish.** The largest of the three. Six pages exist as studies and all six
+need real design passes; the English twin set does not exist at all and this is the layout where
+twinning is most work (every app panel doubles). The catalogue page needs a different phone answer.
+The shared-design mechanism in `SHARED-DESIGN.md` §3(a) stops being optional. Call it several
+sessions, and it changes what the website *is*, so the SEO baseline needs deliberate re-planning
+rather than preservation.
+
+## M2 — hybrid
+
+**What it does well.** It is the lightest of the three by a clear margin — **150 KB over 16
+requests**, and the shortest phone page at 3 057 px — while still showing the real dashboard in the
+first screen, inside a window frame that reads unmistakably as "a picture of an application" rather
+than "you are logged in". It keeps the website's own voice and structure, so the SEO work already
+done survives, and it needs no new twin pattern.
+
+**What can go wrong.** The window is a smaller promise than M1's: four tiles, not the real thing, so
+it shows the product without quite letting a visitor *use* it. It is also the least distinctive of
+the three — a hero beside a product shot is a normal landing page, which is exactly the charge the
+current home page was redesigned to escape. And the window risks reading as a static image unless
+the "click a tile" invitation is obvious.
+
+**What it would cost to finish.** The smallest. It is one page plus its English twin, built out of
+components that now exist, and it can borrow the current preview's lower sections nearly as they are.
+One session, maybe two with the twin.
+
+## A — the current redesign, for reference
+
+Already written up in `audits/redesign-2026-10-10/`. It is the safest of the three, keeps every SEO
+string, and is the only one with its "Ki áll mögötte" question already settled. Its weakness is the
+one that started this: it still reads as *a website about a product* rather than the product.
+
+## What I would say if asked
+
+**M2 is the better bet, and M1 is the better idea.** M2 costs least, risks least, and keeps
+everything already decided. M1 is the only one of the three that answers the original complaint
+completely — but it is a different website, with a different SEO story, four times the weight, and a
+real chance a visitor mistakes it for a login page.
+
+If the appeal of M1 is "show the real thing immediately", **M2 already delivers most of that for a
+fifth of the cost**, and the honest way to find out whether M1's risk is real is to put M1 in front
+of two or three people who are not Viktor and watch whether they look for a password field.
+
+## The register
+
+Nothing here is filed yet. If a direction is chosen, the work to build it becomes rows; if M1 is
+rejected, `website/preview/m1/`, `website/preview/m2/`, `website/preview/mockup.css` and the
+`MOCKUPS` set in `scripts/site_gates.py` are deleted together.
diff --git a/documentation/audits/mockups-2026-10-11/SHARED-DESIGN.md b/documentation/audits/mockups-2026-10-11/SHARED-DESIGN.md
new file mode 100644
index 00000000..c54c72b9
--- /dev/null
+++ b/documentation/audits/mockups-2026-10-11/SHARED-DESIGN.md
@@ -0,0 +1,128 @@
+# Could the website and the controller share one design? — measured, then answered
+
+**Report only. Nothing in this document was built.** Written 2026-10-11 while building the M1/M2
+mockups, which is the only reason the numbers below are measurements rather than estimates.
+
+Baselines: `felhom.eu` `0c86e698b0`, `felhom-controller` `dcd89364a4` (**read only**),
+`app-catalog-felhom.eu` `8ea72aa`.
+
+---
+
+## 1. What M1 actually reused
+
+`website/preview/mockup.css` is deliberately three labelled blocks so this could be counted:
+
+| Block | Rules | Size | What it is |
+|---|---:|---:|---|
+| 1. BASE, **adapted** | 8 | 2.5 KB | The controller's reset, tokens and `@font-face`, with the font URLs repointed `/static/fonts/` → `/assets/fonts/` |
+| 2. CONTROLLER, **verbatim** | **188** | 19.4 KB | Copied unchanged out of `style.css` |
+| 3. MOCKUP-ONLY, **new** | 64 | 7.2 KB | What neither product has an answer for |
+| **Total** | **260** | **29.1 KB** | |
+
+So of the 260 rules M1 needs, **188 (72%) are an unchanged copy** and only **64 (25%) are new**.
+The controller's stylesheet has **826 rules**; M1 needed 188 of them, or **23%**. The rest is
+storage, deploy wizards, monitoring, restore flows — surfaces a marketing site has no use for.
+
+**The 18 design tokens are already identical.** Verified this session by parsing both `:root`
+blocks: 18 names in each, **zero differing values, none missing on either side**. The brief said so
+and it is true.
+
+What the 64 new rules are actually for: the welcome card, the `minta` badge, the disabled-menu-item
+treatment, the `:target` app panel, and M2's browser-window frame. Every one of them exists because
+a *website* needs something a *dashboard* does not — none is a restyling of a controller component.
+
+**Honest caveat on the 188.** Only **129 of the 186** class rules shipped (69%) match a class the
+mockup markup actually uses; 57 came along because my extractor matched on selector prefix. A
+hand-curated copy would be smaller. The 72% reuse figure is about *how much fits*, not about how
+tight the file is.
+
+## 2. The finding that matters more than the percentage
+
+**A component-level copy silently drops the element-level rules those components depend on.**
+
+The extractor keys on class names. It copied every `.sidebar`, `.content`, `.mobile-topbar` and
+`.nav-burger` rule, including the whole `@media(max-width: 768px)` block — and silently missed this:
+
+```css
+@media (max-width: 768px) { body { flex-direction: column; } }
+```
+
+One bare element rule. Without it, measured at 390 px: the sticky mobile topbar stayed a **row**
+flex item, pushed `.content` 135 px to the right, collapsed the launcher grid to **one column**, made
+the front page **15 597 px** tall on a phone, and gave two pages a `scrollWidth` of 467 in a 375 px
+viewport. Everything *looked* copied. Nothing said it was incomplete.
+
+A second instance in the same build: the controller's off-canvas drawer is gated on a `js` class that
+`layout.html` sets in an inline head script. The CSS was all there; the hook was not, so the drawer
+never engaged.
+
+**Both failures are invisible to a diff and invisible to review.** They are the argument for a
+mechanism over a convention, and they are why I would not recommend "copy the components when you
+need them" as the long-term answer.
+
+## 3. The two mechanisms, with cost and risk
+
+### (a) One `felhom-ui.css`, owned in `documentation/design/`, copied into both repos, with a gate
+
+The pattern this project already uses for things that must stay identical in two places.
+
+- **Cost.** Write the shared file once by pulling the common components out of both stylesheets
+ (realistically a day, because the two have drifted in ways nobody has catalogued). Then a gate in
+ each repo comparing its copy to the canonical one — the same shape as the existing copy-drift
+ gates, so roughly an afternoon including its decoy.
+- **Risk.** Low and *visible*. A drift turns a gate red on the next push, naming the file. The
+ failure mode is an annoying red gate, not a silently broken phone layout.
+- **What it does not solve.** Someone still has to decide what belongs in the shared file. Put too
+ little in and §2 happens again; put too much in and a controller-only change starts failing the
+ website's gate for no reason.
+
+### (b) Serve the controller's `style.css` to the website as the source
+
+- **Cost.** Near zero to start: one build step copying the file in.
+- **Risk.** High, and the risks are the interesting ones:
+ - **It inverts ownership.** The marketing site's appearance becomes a side effect of a controller
+ release. A controller change made for a deploy wizard can alter the home page of the public site
+ without anyone intending it, between two releases nobody reviewed for that.
+ - **Weight.** The controller's stylesheet is **826 rules**; the website would carry all of them to
+ use 23%. On a site that currently serves **nothing compressed at all** (R-931), that is a real
+ cost on every page, not a theoretical one.
+ - **Different release cadences.** The controller ships several times a day; the website is
+ git-synced from `main` in a minute. They would be coupled at the speed of the faster one.
+
+### Which I would pick
+
+**(a), and not yet.**
+
+(a) is right in shape: a canonical file plus a gate is the only one of the two where a divergence
+*announces itself*, and §2 is a measured demonstration that divergence does not announce itself
+otherwise.
+
+But I would **not build it until the operator has chosen a direction**, because the question (a)
+answers only exists if the website keeps wearing the dashboard's clothes. If M1 is rejected, the
+website needs about a dozen shared atoms (button, tag, card, alert) and a shared stylesheet would be
+a mechanism built for a problem that went away. If M1 is chosen, (a) stops being an improvement and
+becomes **necessary** — at that point the website *is* a second renderer of the controller's design,
+and keeping two hand-synced copies of 188 rules is exactly the arrangement that produced §2.
+
+**A third option worth naming, which I would pick over (b):** keep the tokens shared (they already
+are, and they are the half that never drifts) and let the components diverge on purpose. That is
+today's arrangement, and it has cost nothing so far — because until this week nothing tried to show
+the same component in both places.
+
+## 4. What breaks if the controller's look changes
+
+Under (a) or (b), the website follows the controller automatically. That cuts both ways:
+
+- **Good.** A customer who sees the website and then their own box sees one product. Today they see
+ two designs that merely share colours. That is the whole case for M1.
+- **Bad, and specific.** The controller's UI is driven by what a *household operating a box* needs,
+ and it changes for operational reasons several times a day. A denser `.launch-cell` for boxes with
+ 40 apps would silently re-flow the public front page. A new state colour added for a restore
+ wizard would appear in the website's palette. A controller release that narrows the sidebar
+ changes the public site's navigation width.
+- **The asymmetry to be explicit about:** the controller has a known audience of a handful of test
+ households and can be changed freely. The website is the public face and carries SEO and
+ first-impression weight. Coupling them means the *low-stakes* surface sets the pace for the
+ *high-stakes* one. Any shared-design mechanism needs a rule for that, and the simplest is that the
+ shared file holds only components both surfaces actually render — which is the discipline (a)
+ enforces and (b) does not.
diff --git a/documentation/audits/mockups-2026-10-11/shots/m1-1440-app-panel.png b/documentation/audits/mockups-2026-10-11/shots/m1-1440-app-panel.png
new file mode 100644
index 00000000..29cde115
Binary files /dev/null and b/documentation/audits/mockups-2026-10-11/shots/m1-1440-app-panel.png differ
diff --git a/documentation/audits/mockups-2026-10-11/shots/m1-1440-inditopult.png b/documentation/audits/mockups-2026-10-11/shots/m1-1440-inditopult.png
new file mode 100644
index 00000000..dcec0849
Binary files /dev/null and b/documentation/audits/mockups-2026-10-11/shots/m1-1440-inditopult.png differ
diff --git a/documentation/audits/mockups-2026-10-11/shots/m1-390-inditopult.png b/documentation/audits/mockups-2026-10-11/shots/m1-390-inditopult.png
new file mode 100644
index 00000000..ae97a5aa
Binary files /dev/null and b/documentation/audits/mockups-2026-10-11/shots/m1-390-inditopult.png differ
diff --git a/documentation/audits/mockups-2026-10-11/shots/m2-1440-hero.png b/documentation/audits/mockups-2026-10-11/shots/m2-1440-hero.png
new file mode 100644
index 00000000..a1c7b685
Binary files /dev/null and b/documentation/audits/mockups-2026-10-11/shots/m2-1440-hero.png differ
diff --git a/documentation/audits/mockups-2026-10-11/shots/m2-390-hero.png b/documentation/audits/mockups-2026-10-11/shots/m2-390-hero.png
new file mode 100644
index 00000000..7cca38dc
Binary files /dev/null and b/documentation/audits/mockups-2026-10-11/shots/m2-390-hero.png differ
diff --git a/documentation/audits/mockups-2026-10-11/shots/preview-1440-hero.png b/documentation/audits/mockups-2026-10-11/shots/preview-1440-hero.png
new file mode 100644
index 00000000..4f26e2e3
Binary files /dev/null and b/documentation/audits/mockups-2026-10-11/shots/preview-1440-hero.png differ
diff --git a/documentation/audits/mockups-2026-10-11/shots/preview-390-hero.png b/documentation/audits/mockups-2026-10-11/shots/preview-390-hero.png
new file mode 100644
index 00000000..fc9f0844
Binary files /dev/null and b/documentation/audits/mockups-2026-10-11/shots/preview-390-hero.png differ
diff --git a/documentation/backlog/CLOSED-ITEMS.md b/documentation/backlog/CLOSED-ITEMS.md
index 1fbc269b..d476c6bf 100644
--- a/documentation/backlog/CLOSED-ITEMS.md
+++ b/documentation/backlog/CLOSED-ITEMS.md
@@ -53,6 +53,7 @@ The full text of the row below: `git show 625d38645c:documentation/backlog/OPEN-
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-927** | **Monica was checked against the new-app checklist and STOPPED at 0.2 — upstream has not released anything in 17 months.** (P4) | CLOSED 2026-10-10 — **the operator ruled: keep it out.** The measurements that decided it: no release of any kind since 2025-04-21 and that one a prerelease; no stable release since v4.1.2 on 2024-05-04; the `4.x` branch its README calls "the stable and current version" untouched since 2024-05-04; a 13-month hole in the commit history ended by two commits that add a login-page notice saying the hosted instance and all its data will be deleted at the end of December 2026 "ahead of the new Monica version"; 790 issues open. Nothing in the catalog covers a family address book either way, and Radicale already carries contacts to a phone over CardDAV. **Reasoning kept:** an app is not added because a gap exists — it is added because the app can be kept. Look again when the rewrite ships. | `audits/new-apps-2026-10-10/FIT.md`; the operator's decision 2026-10-10 |
+| **R-929** | **The app count disagrees with itself in three places: website prose 58, apps page 59 cards, catalog 60 templates.** (P4) | CLOSED 2026-10-11 — **NOT A DEFECT. The three numbers count three different things, and all three are correct.** MEASURED by reading every `.felhom.yml` in `app-catalog-felhom.eu` `8ea72aa`: **60** template directories, of which `plant-it` is `lifecycle: abandoned` and `wger` is `lifecycle: hidden`, so the controller offers **58** for a new install — exactly what the site's prose says. The page's **59th** card is **FileBrowser**, which carries the page's single „Beépített" badge because it is a built-in, not a catalog app. Set difference both ways: nothing offered is missing from the page, and nothing on the page is unaccounted for. **The premise was wrong on both halves** — no card is uncounted and no template is missing. **Rule kept:** before filing a count disagreement, check whether the counts are counting the same set. | `audits/mockups-2026-10-11/` (the catalog read that measured it); `scripts/catalog.py` output in that session's transcript |
| **R-915** | **The Meta app `felhom.eu` was in development mode, so anything it posted was visible only to people with a role on the app.** (P4) | CLOSED 2026-10-09 — the app is **Published**. What it had been waiting for was R-813's Terms of Service URL, which did not exist until the closed-test legal set went live that morning. Filled in Basic settings: **Privacy policy URL** `https://felhom.eu/adatkezeles`, **Terms of Service URL** `https://felhom.eu/feltetelek`, **User data deletion → Data deletion instructions URL** `https://felhom.eu/adatkezeles` (the notice says how: write to `info@felhom.eu`), **Category** „Vállalkozások és oldalak". Meta then reported **„All required app settings are complete"** — the app **icon turned out NOT to be required**, which is worth recording because the original row listed it among the blockers. **Read back from a full page reload, not from the success toast** (Meta's toasts have lied on this Page before): the sidebar badge reads `Published`, the string `Unpublished` is gone, and the page now offers `Unpublish`. No permission was added and no use case changed — `business_management` et al. stay „Ready for testing", which is all a system user needs for the business's own Page. | `audits/facebook-page-details-2026-10-09/E1-meta-app-published.jpg` |
**Reasoning kept.** Going Live is about *who can see what the app posts*, not about new powers: the system user already had the Page scopes, and nothing was granted here. The app icon, display name and contact e-mail were listed as Live blockers in the original row; only the two URLs and the category actually were.
diff --git a/documentation/backlog/OPEN-ITEMS.md b/documentation/backlog/OPEN-ITEMS.md
index 29718164..ea451f59 100644
--- a/documentation/backlog/OPEN-ITEMS.md
+++ b/documentation/backlog/OPEN-ITEMS.md
@@ -135,7 +135,7 @@ stopping line that lies.
| **R-770** | Apps & catalog | P4 | **[P3-LOW] Invidious — fit check only; the recommendation is not to build it.** READ 2026-10-01: playback needs `invidious-companion` (rolling `latest`, no version tags); PostgreSQL 14 (EOL 2026-11); `registration_enabled: true` by default; upstream: a bot check means „your IP is blocked from YouTube”, a 429 can last 24 h, triggered by „someone on your network” — on our boxes that IP is the household's. One bad period in 2026 (March, ~2 weeks). No report found of a family's other devices being bot-checked (inference). **Needs:** the operator's go / no-go. `audits/new-apps-2026-10-01/FIT.md` | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator** **Re-ranked 2026-10-03: P3→P4: a new-app idea waiting on a decision.** | — | — | operator |
| **R-771** | Apps & catalog | P4 | **[P3-LOW] moonlight-web — fit check only; not buildable through an HTTP-only tunnel at usable latency.** READ 2026-10-01: two unrelated projects (MrCreativ3001/moonlight-web-stream, the original; linckosz/moonlight-web); both need Sunshine/Apollo/Wolf on a gaming PC on the LAN and WebRTC over UDP (40000-40100/udp; linckosz recommends host networking and sends telemetry by default); both have a WebSocket fallback (high latency, all video through the tunnel); a logged-in user controls the PC's desktop. **Needs:** the operator's go / no-go (LAN-only use would need a different publishing model). `audits/new-apps-2026-10-01/FIT.md` | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator** **Re-ranked 2026-10-03: P3→P4: a new-app idea waiting on a decision.** | — | — | operator |
| **R-905** | Apps & catalog | P4 | **wger's collected style files (283 MB) go into every wger backup, though the app rebuilds them at every start.** Since R-762's fix (`DJANGO_DEBUG=False` + `collectstatic`, catalog `cf1ed43`) the static files sit in a named volume that the backup legs copy like data (measured 283 MB, `audits/design-build-2026-10-06/`); `collectstatic` regenerates them, so they are rebuildable bytes in every Tier-1 unit, Tier-2 copy and off-site snapshot. wger is `lifecycle: hidden` and no box runs it (hub read 2026-10-08). Options to weigh: a non-backed-up volume class for regenerable data, or an anonymous volume. Found by the R-762 helpers 2026-10-08. | **OPEN — needs a design before wger is shown** | — | Design the „regenerable volume" exclusion (catalog + controller backup legs) | CC |
-| **R-929** | Apps & catalog | P4 | **The app count disagrees with itself in three places: the website's prose says 58, the apps page renders 59 cards, the catalog holds 60 template directories.** FOUND 2026-10-10 while writing the Facebook campaign, which had to quote a number. `site_gates.py` gate 15 prints „apps pages agree: 59 cards, 59 logos” (it compares the two language sets to EACH OTHER, so it cannot catch a count that is wrong on BOTH); the prose says 58 in 15 places per language set (`website/CHANGELOG.md` 2026-10-10 moved it 56→58 for Grocy + LubeLogger); `find templates -name .felhom.yml` in `app-catalog-felhom.eu` returns 60. So at least one card is not counted by the prose, and at least one template is not on the page — and the gate that looks at app counts is blind to both, because nothing compares the page to the CATALOG. **The campaign post says 58**, matching every prose claim on the site today; if 58 is wrong, a published post is wrong with it. **Fix shape:** reconcile the three (which templates are deliberately not shown — `lifecycle: hidden`, not installable — and which are missing from the page), then make the gate assert the page's card count against the catalog's shown-template count rather than against the other language. Needs the `felhom-app-catalog` skill's exclusion rules. | **OPEN — filed 2026-10-10 by the tester-recruitment session, which did NOT fix it (outside its brief)** | — | Reconcile page cards vs catalog templates vs the prose number; then point gate 15 at the catalog | CC |
+| **R-932** | Apps & catalog | P4 | **Eight catalog strings carry an em dash, and they are shown to customers in the controller's own app page.** MEASURED 2026-10-11 while reading every `.felhom.yml` for the website mockups: 8 strings across 5 apps (`recipe-importer` 4, `romm` 1, `calibre-web` 1, `paperless-ngx` 1, `vaultwarden` 1) contain `—` in a `tagline`, `use_case` or `first_step`. These render in the controller's app-info page on every real box, and they will render on any website page that shows catalog copy — the mockups are the first thing to surface them. The house rule (`skills/felhom-web-design` §4) bans the em dash in customer-facing Hungarian and uses the spaced en dash as the gondolatjel; most of these want a colon or a full stop instead, so it is a per-sentence edit, not a find-and-replace. Small, but it lives in a repo this session did not have in scope. | **OPEN** — filed 2026-10-11 by the mockup session | — | edit the 8 strings in `app-catalog-felhom.eu`, one sentence at a time | CC |
## App updates — 4 rows (P3 4)