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.
This commit is contained in:
2026-10-11 12:26:46 +02:00
parent 3129d99b04
commit 7413e0dfab
11 changed files with 221 additions and 1 deletions
@@ -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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB