website: the laptop screen moves - six real apps behind the cut-out frame
gates / gates (push) Failing after 7m17s
gates / gates (push) Failing after 7m17s
The operator recorded the six scenes and ruled on their length: use them whole, "they had been cut as I liked them". Jellyfin, Tandoor, OnlyOffice, Immich, RomM, Paperless, 0.6s crossfades, 36.6s looping at 904x556. - hero-clip.mp4 (1 134 KB) + hero-clip.webm (1 295 KB) + hero-poster.webp (53 KB), one set for both languages: the recordings are the same on / and /en/. - The hero <video> sits in the frame's cut-out hole at z-index 1, the frame above it at 2. No autoplay attribute: the page's script starts it when the hero is on screen, pauses it when it leaves, and never starts it under prefers-reduced-motion. With JS off the poster stays. - make_hero_clip.py reads each scene's own length (ffprobe) instead of cutting every one to a fixed window, and the xfade offsets count from the running sum minus one fade per transition already made. - site.css ?v=26 on all 22 pages. NOT verified: playback in this workstation's Chrome, whose media pipeline loads nothing at all today (a 1 KB generated WAV blob stalls the same way, with no error) - which rules the clip out as the cause. Geometry, layering and the poster frame were verified in the browser. Jellyfin, Paperless and RomM were filmed on those projects' own public demo sites; the films in the Jellyfin scene are public-domain prints.
This commit is contained in:
@@ -81,3 +81,36 @@ sidebar's `position: fixed` on a phone with scripting off.
|
||||
website's own footer, so those rules are meant to apply.
|
||||
|
||||
Both halves have decoys that have been seen to fail.
|
||||
|
||||
---
|
||||
|
||||
# Third round, same day — the laptop screen moves
|
||||
|
||||
You recorded six scenes and said to use them whole: *„use the full length for all of them, they had
|
||||
been cut as I liked them”*. That is what shipped — Jellyfin, Tandoor, OnlyOffice, Immich, RomM,
|
||||
Paperless, in file order, 0.6 s crossfades between them, **36.6 seconds** in all, looping.
|
||||
|
||||
**What a visitor pays for it.** The page draws immediately: the laptop frame (59 KB) and the first
|
||||
frame of the clip as a still (53 KB) are all the hero needs to look finished. The clip itself
|
||||
(1.1 MB as mp4, 1.3 MB as webm — a browser takes one, not both) is fetched only when the hero is
|
||||
actually on screen, and it is dropped entirely for a visitor whose system asks for reduced motion,
|
||||
or whose JavaScript is off. **No „loading” graphic was needed:** the still *is* the loading state,
|
||||
and it is a real frame of the thing that is about to move.
|
||||
|
||||
**Why it is honest.** Jellyfin, Paperless and RomM were filmed on those projects' own public demo
|
||||
sites, so nothing in the clip is anybody's private library, and the films in the Jellyfin scene are
|
||||
public-domain prints. The caption under the laptop names the six apps and claims nothing about whose
|
||||
box they ran on.
|
||||
|
||||
**One thing I could not check, and will not pretend I did.** This workstation's Chrome will not play
|
||||
*any* media right now — not the clip, not a 1 KB silent sound file generated in the page itself,
|
||||
which stalls the same way with no error. That rules the clip out as the cause: the browser's media
|
||||
engine is down, not the file. What I did verify in the browser: the clip sits exactly in the cut-out
|
||||
screen with the bezel, the glare and the keyboard in front of it, at desktop size, and the first
|
||||
frame is what you see there. **Restart Chrome and the moving picture is the one thing left to look
|
||||
at** — on <https://felhom.eu> itself once this is pushed.
|
||||
|
||||
The builder changed with it: `scripts/make_hero_clip.py` reads each scene's length instead of
|
||||
cutting every one to the same window, and the crossfade offsets now count from the running total
|
||||
minus one fade per transition already made — with equal scenes the old arithmetic happened to
|
||||
give the same answer, with your unequal ones it would not have.
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
| Workspace installer | scripts/install_workspace.py | Lays down the workspace-root `CLAUDE.md` (canonical source: `documentation/runbooks/workspace-CLAUDE.md`) and the `InstructionsLoaded` hook from `workspace/hooks/`. **Unlike `install_skills.py` the targets are LIVE CONFIG, not disposable**: timestamped backup before every write, `settings.json` MERGED (owns exactly one key, `hooks.InstructionsLoaded`), a diverged CLAUDE.md reported not silently resolved, an unparseable settings.json REFUSED. Idempotent — a second run writes nothing. |
|
||||
| Rule-firing report | scripts/rules_report.py | Reads the `InstructionsLoaded` hook log and cross-references every `.claude/rules/*.md` in the workspace. **The column that matters is the empty one** — a rule that has never fired is mis-globbed or dead (built-but-never-wired, applied to instructions). Prints each silent rule's `paths:` so "wrong glob" is distinguishable from "quiet month". Caveat: the log only covers since the hook was armed. |
|
||||
| Gate script | scripts/site_gates.py | Byte-level mechanical gates (BOM, emoji codepoint ranges, nav/footer diff, analytics, banned tokens, cache-bust); run `python scripts/site_gates.py` after ANY website change; non-zero exit on failure. |
|
||||
| Hero clip builder | scripts/make_hero_clip.py | Assembles the home page's laptop clip from the operator's raw app recordings: scale+crop each to the screen's 1.626:1, crossfade, encode mp4 (h264) + webm (VP9) + a webp poster. **Runs on DooPlex** - Windows has no ffmpeg. NOT part of any build, gate or deploy: the site has no build step, so the output is committed like any other picture. Measured 2026-10-12: GIF is 11x the mp4 for the same seconds and dithers to 256 colours, hence mp4+webm. The frame it plays under is `website/assets/hero-laptop-frame.webp`, whose screen is cut out; the hole's position is in the CSS as percentages. |
|
||||
| Hero clip builder | scripts/make_hero_clip.py | Assembles the home page's laptop clip from the operator's raw app recordings: scale+crop each to the screen's 1.626:1, crossfade, encode mp4 (h264) + webm (VP9) + a webp poster. **Runs on DooPlex** - Windows has no ffmpeg. NOT part of any build, gate or deploy: the site has no build step, so the output is committed like any other picture. Measured 2026-10-12: GIF is 11x the mp4 for the same seconds and dithers to 256 colours, hence mp4+webm. The frame it plays under is `website/assets/hero-laptop-frame.webp`, whose screen is cut out; the hole's position is in the CSS as percentages. **Every scene plays at its full recorded length** (operator ruling 2026-10-12: the raw file IS the edit); `--seconds N` only caps. One output set (`hero-clip.mp4/.webm`, `hero-poster.webp`) serves both languages; `--lang hu` adds a suffix back if they ever differ. |
|
||||
| Fetch-validate-install (shell) | scripts/felhom-host-install.sh `step_agent_install` (~L1108) | `fetch_raw` to mktemp → syntax-check (`bash -n`) → `install -m0755 -o root -g root` → only then activate; guarded-mkfs wrapper installed BEFORE the sudoers that references it (ordering is the safety property). All mutations through `run()` (dry-run aware). |
|
||||
| Install-profile gate (shell) | scripts/felhom-host-install.sh `--mode appliance\|byo` (GL-2, v1.10.0) | Mandatory-flag profile (no default), refusals at argv time BEFORE any prompt/step, risky step gated at its CALL SITE (one auditable place — never a branch inside the step), mode persisted to state.json + resume-mismatch refusal, `FELHOM_INSTALL_STATE_DIR` override for harness isolation. Harness: scripts/hostinstall-mode-harness.sh (static refusal matrix + grep-invariants + PVE dry-transcript tier; red-proofs run against a mutated scratch copy). |
|
||||
| Disclosure↔uninstall parity (shell) | scripts/felhom-host-install.sh `_uninstall_statement` + harness GL4-D (v1.11.0) | Every host artifact the byo disclosure names must be removed OR explicitly listed KEPT by `run_uninstall`; the harness greps the parity (token list). New install-time artifact ⇒ add its removal + disclosure line + parity token in the SAME commit. Drive data rule: plain `umount` only, never `-l`/`-f`, never any format op under /mnt/felhom-drives. |
|
||||
|
||||
@@ -5,6 +5,19 @@
|
||||
**Updated 2026-10-11 (M3 live): hub 0.146.0; demo-hp, demo-felhom and Tester 1 run agent 0.154.0 and controller
|
||||
0.307.0. The open-items list is at 129 (was 127). Reports: `REPORT-m3-2026-10-11.md`, `REPORT-redesign-2026-10-10.md`, `REPORT-testers-2026-10-10.md`, `REPORT-register-shrink-2026-10-10.md`, `REPORT-new-apps-2026-10-10.md`, `REPORT-release-2026-10-10.md`, `REPORT-break-the-circle-2026-10-09.md`, `REPORT-dooplex-survival-2026-10-09.md`, `REPORT-day4-2026-10-09.md`.**
|
||||
|
||||
## Monday 2026-10-12 (night): the laptop on the home page is playing
|
||||
|
||||
- **Six apps, filmed by you, now run on the laptop's screen** on <https://felhom.eu> and the English
|
||||
home page: Jellyfin, Tandoor, OnlyOffice, Immich, RomM, Paperless, 36.6 seconds, looping,
|
||||
silently. Jellyfin, Paperless and RomM were filmed on those projects' public demo sites, so no
|
||||
private library is on show anywhere in it.
|
||||
- **It costs nothing up front.** The page draws with a still frame of the clip; the clip is fetched
|
||||
only once the laptop is on screen, and not at all for a visitor who asked their system for less
|
||||
movement.
|
||||
- **One check is still open:** this workstation's Chrome will not play any video at all today (a
|
||||
1 KB test sound fails the same way), so the moving picture itself has not been watched in a
|
||||
browser. Geometry and the still were verified. Restart Chrome and open the home page.
|
||||
|
||||
## Monday 2026-10-12: your seven notes are done, and one of them was broken on every page
|
||||
|
||||
- **The app category links in the menu never worked.** All five pointed at names that do not exist on
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
## 2026-10-12 (night) — make_hero_clip.py: scenes keep their own length
|
||||
|
||||
- **`--seconds` is no longer a fixed window.** Each scene now plays its full recorded length, read
|
||||
per file with `ffprobe` (operator ruling 2026-10-12: *„use the full length for all of them, they
|
||||
had been cut as I liked them”* — the raw file IS the edit). `--seconds N` stays as a cap, for
|
||||
trying a shorter loop; `0` (the default) means full length.
|
||||
- The xfade offsets are computed from the running sum of the scenes so far **minus one fade per
|
||||
transition already made** — xfade measures its offset on the chain it is handed, which is
|
||||
shorter than the raw sum. With equal-length scenes the old `i * (seconds - FADE)` was the same
|
||||
number; with unequal ones it is not.
|
||||
- **Output is `hero-clip.mp4` / `.webm` / `hero-poster.webp`** — no language suffix, because the
|
||||
same recordings serve `/` and `/en/`. `--lang hu` puts `-hu` back if the two ever differ.
|
||||
- Six real scenes, 36.6s after the fades: **mp4 1 134 KB, webm 1 295 KB, poster 53 KB.** Three
|
||||
times the 11.4s trial, as expected — the hero is the only page that loads it, and only once it
|
||||
is on screen.
|
||||
|
||||
## 2026-10-12 (evening) — make_hero_clip.py: the home page's laptop clip
|
||||
|
||||
- `scripts/make_hero_clip.py` assembles the hero clip from the operator's raw app recordings:
|
||||
|
||||
+43
-15
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""make_hero_clip.py — assemble the home page's laptop clip from raw app recordings.
|
||||
|
||||
python3 scripts/make_hero_clip.py <raw-dir> <out-dir> [--lang hu|en] [--seconds 4] [--dry-run]
|
||||
python3 scripts/make_hero_clip.py <raw-dir> <out-dir> [--lang hu|en] [--seconds N] [--dry-run]
|
||||
|
||||
WHAT IT IS FOR. The hero on / and /en/ is a laptop whose SCREEN is a short, silent, looping clip of
|
||||
real apps running on a real box. The operator records the raw clips; this turns them into the two
|
||||
@@ -18,15 +18,25 @@ INPUT. One file per scene in <raw-dir>, named `NN-appname.<ext>` so the order is
|
||||
Any container ffmpeg reads. Audio is dropped. Each clip is scaled to cover the laptop's screen and
|
||||
centre-cropped to 1.626:1 — record at 16:10 (1440x900 is ideal) and almost nothing is lost.
|
||||
|
||||
EACH SCENE PLAYS AT ITS FULL RECORDED LENGTH (operator ruling 2026-10-12: "use the full length for
|
||||
all of them, they had been cut as I liked them"). The raw file IS the edit; this script does not
|
||||
re-cut it. `--seconds N` caps every scene at N seconds, for trying a shorter loop.
|
||||
|
||||
OUTPUT (in <out-dir>, which is normally website/assets):
|
||||
|
||||
hero-clip-<lang>.mp4 h264, the one every browser plays
|
||||
hero-clip-<lang>.webm VP9, preferred where offered
|
||||
hero-poster-<lang>.webp the first frame, which the <video> shows before it loads
|
||||
hero-clip.mp4 h264, the one every browser plays
|
||||
hero-clip.webm VP9, preferred where offered
|
||||
hero-poster.webp the first frame, which the <video> shows before it loads
|
||||
|
||||
ONE set serves both languages, because the recordings are the same on / and /en/. `--lang hu` puts
|
||||
a `-hu` suffix back, for the day the two differ.
|
||||
|
||||
MEASURED, 2026-10-12, six scenes of 2.4s with 0.6s fades at 904x556:
|
||||
mp4 331 KB · webm 411 KB · animated webp 2.5 MB · GIF 2.7 MB for HALF of it.
|
||||
GIF is 11x the size of the mp4 and dithers to 256 colours on a dark UI. Hence mp4 + webm.
|
||||
|
||||
SHIPPED, same day, the six real recordings at FULL length (36.6s after the fades):
|
||||
hero-clip.mp4 1134 KB · hero-clip.webm 1295 KB · hero-poster.webp 53 KB.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
@@ -46,6 +56,15 @@ FPS = 20
|
||||
EXTS = (".mp4", ".mkv", ".webm", ".mov", ".m4v", ".avi")
|
||||
|
||||
|
||||
def duration(path):
|
||||
"""Seconds of a raw clip, read from the container."""
|
||||
p = subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration",
|
||||
"-of", "default=nw=1:nk=1", path], capture_output=True, text=True)
|
||||
if p.returncode or not p.stdout.strip():
|
||||
sys.exit("ffprobe could not read a duration from %s:\n%s" % (path, p.stdout + p.stderr))
|
||||
return float(p.stdout.strip())
|
||||
|
||||
|
||||
def ffmpeg(args, dry):
|
||||
print(" ffmpeg " + " ".join(a if " " not in a else '"%s"' % a for a in args[:6]) + " …")
|
||||
if dry:
|
||||
@@ -60,8 +79,9 @@ def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("raw_dir")
|
||||
ap.add_argument("out_dir")
|
||||
ap.add_argument("--lang", default="hu")
|
||||
ap.add_argument("--seconds", type=float, default=4.0, help="how much of each raw clip to keep")
|
||||
ap.add_argument("--lang", default="", help="suffix for a language-specific set; default: none")
|
||||
ap.add_argument("--seconds", type=float, default=0.0,
|
||||
help="cap every scene at N seconds; default 0 = each clip's full length")
|
||||
ap.add_argument("--dry-run", action="store_true")
|
||||
a = ap.parse_args()
|
||||
|
||||
@@ -72,19 +92,26 @@ def main():
|
||||
if unnumbered:
|
||||
sys.exit("name every clip NN-appname.ext so the order is explicit; these are not: %s"
|
||||
% ", ".join(unnumbered))
|
||||
print("%d scene(s), %.1fs each, %.1fs fades -> %.1fs loop"
|
||||
% (len(raws), a.seconds, FADE, len(raws) * (a.seconds - FADE) + FADE))
|
||||
secs = []
|
||||
for f in raws:
|
||||
print(" %s" % f)
|
||||
d = duration(os.path.join(a.raw_dir, f))
|
||||
secs.append(min(d, a.seconds) if a.seconds else d)
|
||||
n = len(raws)
|
||||
print("%d scene(s) at full length, %.1fs fades -> %.1fs loop"
|
||||
% (n, FADE, sum(secs) - (n - 1) * FADE))
|
||||
for f, d in zip(raws, secs):
|
||||
print(" %-24s %5.2fs" % (f, d))
|
||||
|
||||
ins, fc, n = [], "", len(raws)
|
||||
ins, fc = [], ""
|
||||
for i, f in enumerate(raws):
|
||||
ins += ["-t", "%.3f" % a.seconds, "-i", os.path.join(a.raw_dir, f)]
|
||||
ins += ["-t", "%.3f" % secs[i], "-i", os.path.join(a.raw_dir, f)]
|
||||
fc += ("[%d:v]scale=%d:%d:force_original_aspect_ratio=increase,crop=%d:%d,"
|
||||
"setsar=1,fps=%d,format=yuv420p[v%d];" % (i, W, H, W, H, FPS, i))
|
||||
prev = "[v0]"
|
||||
for i in range(1, n):
|
||||
off = round(i * (a.seconds - FADE), 3)
|
||||
# xfade measures its offset on the chain built SO FAR, which is shorter than the raw sum
|
||||
# by one FADE per transition already made.
|
||||
off = round(sum(secs[:i]) - i * FADE, 3)
|
||||
out = "[out]" if i == n - 1 else "[x%d]" % i
|
||||
fc += "%s[v%d]xfade=transition=fade:duration=%s:offset=%s%s;" % (prev, i, FADE, off, out)
|
||||
prev = "[x%d]" % i
|
||||
@@ -92,9 +119,10 @@ def main():
|
||||
if n == 1:
|
||||
fc = fc.replace("[v0];", "[out];").rstrip(";")
|
||||
|
||||
mp4 = os.path.join(a.out_dir, "hero-clip-%s.mp4" % a.lang)
|
||||
webm = os.path.join(a.out_dir, "hero-clip-%s.webm" % a.lang)
|
||||
poster = os.path.join(a.out_dir, "hero-poster-%s.webp" % a.lang)
|
||||
sfx = ("-" + a.lang) if a.lang else ""
|
||||
mp4 = os.path.join(a.out_dir, "hero-clip%s.mp4" % sfx)
|
||||
webm = os.path.join(a.out_dir, "hero-clip%s.webm" % sfx)
|
||||
poster = os.path.join(a.out_dir, "hero-poster%s.webp" % sfx)
|
||||
|
||||
ffmpeg(ins + ["-filter_complex", fc, "-map", "[out]", "-an",
|
||||
"-c:v", "libx264", "-crf", "30", "-preset", "slow",
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
+30
-1
@@ -1,4 +1,33 @@
|
||||
## 2026-10-12 (evening) — the laptop frame, cut open for a moving screen
|
||||
## 2026-10-12 (night) — the laptop screen moves: six real apps behind the cut-out frame
|
||||
|
||||
The operator recorded the six scenes himself and ruled on their length: *„use the full length for
|
||||
all of them, they had been cut as I liked them”*. Jellyfin, Paperless and RomM were filmed on those
|
||||
projects' own public demo sites, so nothing in the clip is anyone's private library; the films in
|
||||
the Jellyfin scene are public-domain prints.
|
||||
|
||||
- **`website/assets/hero-clip.mp4` (1 134 KB) + `hero-clip.webm` (1 295 KB) + `hero-poster.webp`
|
||||
(53 KB)** — Jellyfin, Tandoor, OnlyOffice, Immich, RomM, Paperless, in that order, 0.6s
|
||||
crossfades, **36.6s** in all at 904×556 (2× the rendered size). Built by
|
||||
`scripts/make_hero_clip.py`, which now plays each scene at its own length instead of a fixed
|
||||
window.
|
||||
- **The hero on `/` and `/en/` is the clip**, behind `hero-laptop-frame.webp`: `.hero-screen` sits
|
||||
in the cut-out hole (`left 11.041%` / `top 12.345%` / `79.817%×64.305%`) at `z-index: 1`, the
|
||||
frame above it at `z-index: 2`, so bezel, glare and keyboard stay in front of the moving picture.
|
||||
- **Nothing autoplays by attribute.** `preload="none"` + a poster; the page's own script starts the
|
||||
clip when it scrolls into view, pauses it when it leaves, and **never starts it at all under
|
||||
`prefers-reduced-motion: reduce`**. With JavaScript off the poster simply stays. First paint costs
|
||||
the frame (59 KB) + poster (53 KB); the clip is fetched after.
|
||||
- **One set serves both languages** — the recordings are the same on both pages, so there is no
|
||||
`-hu`/`-en` pair to keep in step. The still composites (`dashboard-laptop-{hu,en}.webp`) stay in
|
||||
the repo, now unreferenced, as the fallback if the clip is ever pulled.
|
||||
- Caption and `aria-label` name the six apps and claim nothing about whose box they ran on.
|
||||
- `site.css` — `?v=26` on all 22 pages.
|
||||
- **Not verified: playback in this workstation's Chrome.** Its media pipeline loads nothing at all
|
||||
today — a 1 KB generated WAV blob also stalls at `readyState 0` with no error, which rules the
|
||||
clip out as the cause. Geometry, layering and the poster were verified in the browser; the moving
|
||||
picture needs a Chrome restart to confirm.
|
||||
|
||||
## 2026-10-12 (evening) — the laptop frame, cut open for a moving screen
|
||||
|
||||
Prepared, **not yet on the page**: the live hero still shows the still. The clip itself is the
|
||||
operator's to record (his decision, 2026-10-12: he films the apps, the assembly is built here).
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="/assets/gallery.js?v=2"></script>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
+22
-9
@@ -2537,23 +2537,36 @@ html::-webkit-scrollbar-corner, .shell ::-webkit-scrollbar-corner { background:
|
||||
.shell .caps-teaser { grid-template-columns: 1fr; gap: 10px; }
|
||||
}
|
||||
|
||||
/* --- the hero's picture: the REAL dashboard, in a laptop ---------------------
|
||||
The frame is the operator's own supplied render (2026-10-12); the SCREEN is
|
||||
website/assets/dashboard-start-{hu,en}.webp composited into it, so what a
|
||||
visitor sees is a real screenshot of a real box and not a drawing of one.
|
||||
The file is named dashboard-laptop-<lang>.webp on purpose: gate 19 then holds
|
||||
it to the page's language like every other dashboard picture. */
|
||||
/* --- the hero's picture: a laptop whose SCREEN is a clip of real apps --------
|
||||
The frame is the operator's own supplied render with the screen cut to
|
||||
transparency (hero-laptop-frame.webp); behind it sits hero-clip.mp4/.webm,
|
||||
six apps recorded by the operator, assembled by scripts/make_hero_clip.py.
|
||||
What a visitor sees is real software, not a drawing of it.
|
||||
NO autoplay attribute on purpose: the page's own script starts the clip when
|
||||
it scrolls into view and never starts it under prefers-reduced-motion, so a
|
||||
visitor who asked for less movement is left with the poster frame.
|
||||
The still composite (dashboard-laptop-<lang>.webp) is kept but no longer
|
||||
referenced - it is the fallback if the clip is ever pulled. */
|
||||
.hero-shot { margin: 0; }
|
||||
.hero-laptop { position: relative; max-width: 620px; margin-inline: auto; }
|
||||
.hero-shot img {
|
||||
display: block; width: 100%; height: auto; max-width: 620px;
|
||||
margin-inline: auto;
|
||||
display: block; width: 100%; height: auto;
|
||||
position: relative; z-index: 2; pointer-events: none;
|
||||
}
|
||||
/* The hole in the frame, measured off its alpha channel: the clip is rendered to
|
||||
exactly this shape (904x556, 1.626:1) by make_hero_clip.py, so object-fit has
|
||||
nothing to crop. z-index keeps the bezel, the glare and the keyboard in FRONT. */
|
||||
.hero-screen {
|
||||
position: absolute; z-index: 1; display: block;
|
||||
left: 11.041%; top: 12.345%; width: 79.817%; height: 64.305%;
|
||||
object-fit: cover; background: var(--bg-2, #101826); border: 0;
|
||||
}
|
||||
.hero-shot figcaption {
|
||||
margin-top: .4rem; text-align: center;
|
||||
font-size: .85rem; color: var(--text-2);
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.hero-shot img { max-width: 520px; }
|
||||
.hero-laptop { max-width: 520px; }
|
||||
}
|
||||
|
||||
/* --- an anchored target must not land under the sticky phone top bar -------- */
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="/assets/gallery.js?v=2"></script>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
+1
-1
@@ -366,7 +366,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
+19
-3
@@ -57,7 +57,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="/assets/gallery.js?v=2"></script>
|
||||
@@ -117,8 +117,14 @@
|
||||
<a href="/en/testing" class="btn btn-primary btn-lg">Apply as a tester →</a>
|
||||
</div>
|
||||
<figure class="hero-shot">
|
||||
<img src="/assets/dashboard-laptop-en.webp?v=1" width="1200" height="916" alt="The box's Dashboard on a laptop screen: the Launcher with the installed applications" decoding="async">
|
||||
<figcaption>This is what the box's Dashboard looks like.</figcaption>
|
||||
<div class="hero-laptop">
|
||||
<video class="hero-screen" poster="/assets/hero-poster.webp?v=1" muted loop playsinline preload="none" disablepictureinpicture aria-label="Apps running: Jellyfin, Tandoor, OnlyOffice, Immich, RomM, Paperless">
|
||||
<source src="/assets/hero-clip.webm?v=1" type="video/webm">
|
||||
<source src="/assets/hero-clip.mp4?v=1" type="video/mp4">
|
||||
</video>
|
||||
<img src="/assets/hero-laptop-frame.webp?v=1" width="1200" height="916" alt="" decoding="async">
|
||||
</div>
|
||||
<figcaption>Apps from the catalogue: Jellyfin, Tandoor, OnlyOffice, Immich, RomM, Paperless.</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
@@ -291,6 +297,16 @@
|
||||
});
|
||||
if(willOpen){g.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
||||
});
|
||||
(function(){
|
||||
var v=document.querySelector('.hero-screen');
|
||||
if(!v||!v.play)return;
|
||||
if(window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches)return;
|
||||
var go=function(){var p=v.play();if(p&&p['catch'])p['catch'](function(){});};
|
||||
if(!window.IntersectionObserver){go();return;}
|
||||
new IntersectionObserver(function(es){
|
||||
es.forEach(function(e){if(e.isIntersecting){go();}else{v.pause();}});
|
||||
},{threshold:0.2}).observe(v);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="/assets/gallery.js?v=2"></script>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
+1
-1
@@ -366,7 +366,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
+19
-3
@@ -57,7 +57,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="/assets/gallery.js?v=2"></script>
|
||||
@@ -117,8 +117,14 @@
|
||||
<a href="/teszteles" class="btn btn-primary btn-lg">Jelentkezem tesztelőnek →</a>
|
||||
</div>
|
||||
<figure class="hero-shot">
|
||||
<img src="/assets/dashboard-laptop-hu.webp?v=1" width="1200" height="916" alt="A doboz vezérlőpultja egy laptop képernyőjén: az Indítópult a telepített alkalmazásokkal" decoding="async">
|
||||
<figcaption>Így néz ki a doboz vezérlőpultja.</figcaption>
|
||||
<div class="hero-laptop">
|
||||
<video class="hero-screen" poster="/assets/hero-poster.webp?v=1" muted loop playsinline preload="none" disablepictureinpicture aria-label="Alkalmazások futás közben: Jellyfin, Tandoor, OnlyOffice, Immich, RomM, Paperless">
|
||||
<source src="/assets/hero-clip.webm?v=1" type="video/webm">
|
||||
<source src="/assets/hero-clip.mp4?v=1" type="video/mp4">
|
||||
</video>
|
||||
<img src="/assets/hero-laptop-frame.webp?v=1" width="1200" height="916" alt="" decoding="async">
|
||||
</div>
|
||||
<figcaption>Alkalmazások a katalógusból: Jellyfin, Tandoor, OnlyOffice, Immich, RomM, Paperless.</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
@@ -291,6 +297,16 @@
|
||||
});
|
||||
if(willOpen){g.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
||||
});
|
||||
(function(){
|
||||
var v=document.querySelector('.hero-screen');
|
||||
if(!v||!v.play)return;
|
||||
if(window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches)return;
|
||||
var go=function(){var p=v.play();if(p&&p['catch'])p['catch'](function(){});};
|
||||
if(!window.IntersectionObserver){go();return;}
|
||||
new IntersectionObserver(function(es){
|
||||
es.forEach(function(e){if(e.isIntersecting){go();}else{v.pause();}});
|
||||
},{threshold:0.2}).observe(v);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<!-- analytics intentionally omitted for draft page -->
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="/assets/gallery.js?v=2"></script>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/pjs-latin-ext.woff2">
|
||||
<!-- shared assets: bump ?v= together on any site.css/icons.svg change -->
|
||||
<link rel="stylesheet" href="/assets/site.css?v=25">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=26">
|
||||
<link rel="stylesheet" href="/assets/shell.css?v=25">
|
||||
<link rel="icon" type="image/png" href="/assets/logo_favicon_2.svg">
|
||||
<script defer src="https://stats.felhom.eu/script.js" data-website-id="d419db57-5e1c-48df-bdb3-c29f25fb1661"></script>
|
||||
|
||||
Reference in New Issue
Block a user