website: the operator's seven notes - a services page, the hero, and 100 dead links
gates / gates (push) Failing after 6m51s

Reported from his own screens. Five defects, two design.

1. THE CATEGORY ANCHORS WERE DEAD - all 100 of them. The sidebar's five app-category
   links pointed at #fajlkezeles-irodai and friends; the ids are #fajlkezeles and
   friends. The sidebar is copied into every page, so one wrong fragment was wrong
   twenty times, and a dead fragment raises no error anywhere: the browser stays put.

   New gate 23 resolves every in-site #fragment against the page it names (360 today).
   It convicted two more on its first run: the home page's four hero tiles pointed at
   /alkalmazasok#immich, which no page had ever carried, and the FAQ's contact button
   pointed at a home-page section that had moved out. Every app card now carries its
   slug as an id - taken from the card's own logo file, which gate 21 already ties to
   the catalog, so the ids cannot drift from it silently.
   Decoy site/dead-fragment puts the operator's own bug back and the gate convicts.

2. SZOLGALTATASOK IS A PAGE: /szolgaltatasok + /en/services, registered in TWINS, URL
   and SHELL. The ten capability items moved VERBATIM out of the home page; the home
   keeps the promise sentence, the three group names as links and a button, and is 345
   Hungarian / 434 English words shorter. The .caps rules stopped being .page-index-
   scoped, because the block now lives on a page with a different body class.

3. THE HOME HEADLINE is back in the hero column beside the picture, two lines with the
   second in --blue-bright at clamp(2.1rem, 4.4vw, 3.2rem). M3 had moved it into
   .page-header - the INNER pages' heading - so it shrank to 2.1rem and the domain
   badge was stranded beside it.

4. THE HERO PICTURE is the real dashboard in a laptop. The frame is the operator's own
   supplied render; the screen is dashboard-start-{hu,en}.webp composited into it, so a
   visitor sees a real box's Launcher rather than a drawing of one - the supplied
   screens are AI-drawn and misspell app names. 57 KB, named dashboard-laptop-<lang>
   so gate 19 holds it to the page's language.

5. .page-lede HAD NO CSS AT ALL on 16 pages: a bare paragraph, full width of the
   content column, hard against the box beneath it. 760px measure, centred over the
   box, air beneath; left-aligned on /szolgaltatasok, which has no centred box.

6. THE SIDEBAR LOGO: the wordmark shipped at 0.67x the mark's width where the real
   lockup (assets/logo.png, measured: mark 492px wide, wordmark 631px) has it at 1.28x.
   Mark 112px, wordmark 25px tall, 28px less vertical padding.

7. SCROLLBARS are the page's own now. The first attempt missed the viewport's: it is
   painted from <html>, and a `body.shell ::-webkit-...` rule only reaches scrollers
   inside the page.

Also: the two Latin font subsets are preloaded. With font-display: swap the page
reflowed after first paint - measured at 1339px on /alkalmazasok#media.

Checked at 390px and 1440px on all 21 pages: no sideways scrolling, footer inside the
content column everywhere, drawer opens with all 13 items. site.css?v=24.
This commit is contained in:
2026-10-12 08:58:42 +02:00
parent a7480172ab
commit 0ef0b106f6
31 changed files with 1150 additions and 474 deletions
+13
View File
@@ -1,3 +1,16 @@
## 2026-10-12 — site gate 23: every in-site #fragment resolves
- `site_gates.py` **gate 23** reads every page's ids, maps each in-site href with a `#fragment` to the
page it names (via `URL`), and fails when that id is not there. 360 fragments today, all resolving.
Links to another site, or to a URL this gate does not own, are not its business.
- **Why.** The sidebar's five app-category links pointed at ids nobody had written — 100 dead links
across 20 pages, live, found by the operator clicking one. A dead fragment raises no error: the
browser stays put, so nothing mechanical and nothing in a log ever noticed. The gate also convicted
two more on its first run (the home page's four hero tiles, the FAQ's contact button).
- **Decoy** `site/dead-fragment`: a sidebar link moved back to `#media-szorakozas`, seen to convict
with gate 23's own line.
- `szolgaltatasok.html` / `en/services.html` registered in `TWINS`, `URL` and `SHELL`.
## 2026-10-11 (evening) — site gate 22: no CSS leak from site.css onto a shell page, MEDIA-AWARE
- `site_gates.py` **gate 22** parses both stylesheets into (media context, selector, declarations) and
+46 -1
View File
@@ -31,6 +31,10 @@ Gates (all must pass; non-zero exit on any failure):
19. dash-lang — a page shows dashboard pictures only in its own language (`dashboard-*-hu.webp` / `-en.webp`)
20. viewer — data-gallery openers are <a href> to existing assets; gallery.js (with ?v=) exactly where openers are;
no inline viewer copy
23. anchors — every in-site link carrying a #fragment finds that id on the page it names.
The sidebar's five category links pointed at #fajlkezeles-irodai and friends while
the page's ids are #fajlkezeles and friends: 100 dead links across 20 pages, live,
found by the operator clicking one
22. shell leak — MEDIA-AWARE: no declaration in site.css reaches a shell page on a selector that
shell.css also styles. site.css loads FIRST, so a property shell.css never sets
survives — „later wins” is per PROPERTY, not per rule. Earned live 2026-10-11:
@@ -73,10 +77,12 @@ TWINS = {
"kapcsolat.html": os.path.join("en", "contact.html"),
"letoltes.html": os.path.join("en", "download.html"), # R-559 (2026-09-18) — the first twin
"teszteles.html": os.path.join("en", "testing.html"), # 2026-10-10 — the tester recruitment page
"szolgaltatasok.html": os.path.join("en", "services.html"), # 2026-10-12 — was a section of the home page
}
URL = {"index.html": "/", "alkalmazasok.html": "/alkalmazasok", "technologiak.html": "/technologiak",
"biztonsagimentes.html": "/biztonsagimentes", "gyik.html": "/gyik", "kapcsolat.html": "/kapcsolat",
"letoltes.html": "/letoltes", "teszteles.html": "/teszteles",
"szolgaltatasok.html": "/szolgaltatasok", os.path.join("en", "services.html"): "/en/services",
os.path.join("en", "testing.html"): "/en/testing", os.path.join("en", "index.html"): "/en/", os.path.join("en", "apps.html"): "/en/apps",
os.path.join("en", "technology.html"): "/en/technology", os.path.join("en", "backups.html"): "/en/backups",
os.path.join("en", "faq.html"): "/en/faq", os.path.join("en", "contact.html"): "/en/contact",
@@ -105,6 +111,7 @@ MOCKUPS = set() # the design studies were deleted with website/preview/ at the
# duplicated into each page, and this is what keeps the duplication honest.
SHELL = {
'szolgaltatasok-nonpublic.html',
'szolgaltatasok.html',
'index.html',
'alkalmazasok.html',
'technologiak.html',
@@ -117,6 +124,7 @@ SHELL = {
'feltetelek.html',
'404.html',
os.path.join("en", 'index.html'),
os.path.join("en", 'services.html'),
os.path.join("en", 'apps.html'),
os.path.join("en", 'technology.html'),
os.path.join("en", 'backups.html'),
@@ -734,8 +742,45 @@ else:
"declarations (media-aware)" % (len(_site_r), len(_shell_r),
len(set(s for _, s, _ in _site_r) & _shell_sel)))
# gate 23 (2026-10-12): an in-site #fragment has to exist on the page it points at.
#
# The shell's sidebar is copied into every page, so ONE wrong fragment is wrong twenty times: the five
# app-category links shipped pointing at ids nobody had written (#fajlkezeles-irodai for a section
# whose id is #fajlkezeles). Nothing noticed, because a dead fragment is not an error anywhere - the
# browser simply stays put. Checked for every page the gate already walks; a link to a page outside
# PAGES, or to another site, is not this gate's business.
_ids = {}
for _p in PAGES:
_s = io.open(os.path.join(W, _p), encoding="utf-8-sig").read()
_ids[_p] = set(re.findall(r'\sid="([^"]+)"', _s))
_url_to_page = {}
for _p, _u in URL.items():
_url_to_page[_u] = _p
if _u.endswith("/"): # "/" and "/en/" are index pages
_url_to_page[_u + "index.html"] = _p
_anchor_n, _anchor_bad = 0, 0
for _p in PAGES:
_s = io.open(os.path.join(W, _p), encoding="utf-8-sig").read()
for _href in re.findall(r'href="([^"]*#[^"]+)"', _s):
if _href.startswith(("http://", "https://", "mailto:")):
continue
_page_part, _frag = _href.split("#", 1)
if _page_part == "":
_target = _p # same page
elif _page_part in _url_to_page:
_target = _url_to_page[_page_part]
else:
continue # a URL this gate does not own
_anchor_n += 1
if _frag not in _ids.get(_target, ()):
_anchor_bad += 1
fail("%s: link to %s finds no id=\"%s\" on %s — the browser stays where it is and the link "
"reads as broken" % (_p, _href, _frag, _target))
if not _anchor_bad:
print(" anchors: %d in-site #fragment(s), every one resolves" % _anchor_n)
if fails:
print("\nSITE GATES FAILED: %d problem(s)" % len(fails))
sys.exit(1)
print("site gates OK — BOM, emoji=0, nav/footer per language, analytics, no CDN, no legacy tokens, no <style>, "
"cache-busted assets, twins + hreflang + globe, lang, same apps, no Hungarian in English, FAQ JSON-LD, clean tail, dashboard pictures per language, one picture viewer, no shell CSS leak")
"cache-busted assets, twins + hreflang + globe, lang, same apps, no Hungarian in English, FAQ JSON-LD, clean tail, dashboard pictures per language, one picture viewer, no shell CSS leak, live anchors")
+9 -1
View File
@@ -91,7 +91,7 @@ COVERS = {
"switch at the wrong twin, lang=\"hu\" on an English page, an app missing from one apps page, "
"ASCII-only Hungarian in English text and in a script message, a 'please', an English "
"retrieval promise the Hungarian does not make, a FAQ JSON-LD drifting from the visible "
"answer, text after </html>, a Hungarian dashboard picture on an English page, and four picture-viewer faults (an opener without href, an opener to a missing file, openers with no gallery.js, the inline viewer put back), and (R-912) a catalog app's logo under another name than its slug — each must convict WITH its own failure line; and (2026-10-11, both of them reported from a real phone AFTER they went live) site.css's old mobile nav reaching the shell's sidebar from inside an @media block — the leak the STOP-1 check missed because it skipped @media entirely — and a <footer> left outside <main class=\"content\">, which on a shell page renders as a second column"),
"answer, text after </html>, a Hungarian dashboard picture on an English page, and four picture-viewer faults (an opener without href, an opener to a missing file, openers with no gallery.js, the inline viewer put back), and (R-912) a catalog app's logo under another name than its slug — each must convict WITH its own failure line; and (2026-10-11, both of them reported from a real phone AFTER they went live) site.css's old mobile nav reaching the shell's sidebar from inside an @media block — the leak the STOP-1 check missed because it skipped @media entirely — and a <footer> left outside <main class=\"content\">, which on a shell page renders as a second column; and (2026-10-12) a sidebar #fragment pointing at an id no page carries - the 100 dead category links the operator found by clicking one"),
"script-tests": ("R-885: FIVE cases in scripts/test_script_tests_gate.py, run from here: a suite that PRINTS "
"OK and exits 1 (label without fact), a failing suite three levels deep (scope is a walk), "
"an empty scripts/ tree (checked nothing), the genuine article (must pass), the nested mark"),
@@ -657,6 +657,14 @@ decoy("site/shell-css-leak-in-media", "site_gates.py",
# shell page, so a <footer> left outside <main class="content"> is a flex SIBLING of the sidebar
# and the content, not the bottom of the page. The decoy closes <main> early and leaves the footer
# outside it — the shape the insert produced on every page that had a <script> after </main>.
# 3. A DEAD #FRAGMENT IN THE SIDEBAR, which is copied into every page - so one wrong fragment is
# wrong twenty times. The five app-category links shipped pointing at ids nobody had written
# (#fajlkezeles-irodai for a section whose id is #fajlkezeles): 100 dead links, live, found by the
# operator clicking one. A dead fragment raises no error anywhere - the browser just stays put.
decoy("site/dead-fragment", "site_gates.py",
replace_bytes(os.path.join(_WEB, "technologiak.html"),
[('<li><a href="/alkalmazasok#media">', '<li><a href="/alkalmazasok#media-szorakozas">')]),
must='link to /alkalmazasok#media-szorakozas finds no id="media-szorakozas"')
decoy("site/footer-outside-main", "site_gates.py",
replace_bytes(os.path.join(_WEB, "gyik.html"),
[(" <footer>\n", " </main>\n <footer>\n"),