audit: the mockup comparison, the shared-design answer, and two register moves
gates / gates (push) Failing after 11m45s
documentation/audits/mockups-2026-10-11/
COMPARE.md the three directions side by side, measured the same way:
A current preview 12 req / 270 KB
M1 dashboard 68 req / 1012 KB (front page 1 660 px)
M2 hybrid 16 req / 150 KB (phone 3 057 px, the shortest)
plus three sentences each (good / risk / cost) and the risk I
think is real: a non-technical visitor may read M1 as somebody
else's account and look for a login.
SHARED-DESIGN.md REPORT ONLY, nothing built. M1 needed 260 CSS rules: 188 (72%)
copied from the controller UNCHANGED, 8 adapted, 64 new. It used
188 of the controller's 826 rules (23%). The 18 tokens are
already identical - verified by parsing both :root blocks, zero
differing values.
Recommends (a) a canonical felhom-ui.css plus a copy-drift gate,
over (b) serving the controller's stylesheet to the website -
and recommends NOT building it until a direction is chosen.
shots/ desktop and phone captures of all three, from the deployed pages
The finding that outranks the percentage, and the reason for the recommendation: a
component-level copy silently drops the element-level rules those components depend
on. The extractor copied every .sidebar/.content/.mobile-topbar rule and missed
"@media(max-width:768px){ body { flex-direction: column } }". Everything LOOKED
copied; the phone layout was broken in four measurable ways. Invisible to a diff,
invisible to review. A second instance the same evening: the drawer is gated on a
'js' class set by an inline script that was not copied either.
Register 129 -> 129 (one closed, one opened):
R-929 CLOSED - NOT A DEFECT. Measured from the catalog: 60 templates, minus
plant-it (abandoned) and wger (hidden) = 58 offered, exactly what the prose
says; the page's 59th card is FileBrowser carrying the single "Beepitett"
badge. Set difference both ways is empty. The premise was wrong on BOTH
halves. Rule kept: before filing a count disagreement, check whether the
counts are counting the same set.
R-932 OPEN - 8 catalog strings across 5 apps carry an em dash, and they render in
the controller's app page on every real box. Found because the mockups are
the first thing to show catalog copy on a website page. Lives in a repo this
session did not have in scope, so it is a row, not a fix.
@@ -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** | <https://felhom.eu/preview/> |
|
||||
| **M1 — the site is the dashboard** | <https://felhom.eu/preview/m1/> |
|
||||
| **M2 — hybrid** | <https://felhom.eu/preview/m2/> |
|
||||
|
||||
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.
|
||||
@@ -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.
|
||||
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 107 KiB |
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||