The three pages had each grown their own tokens, type scale and alignment, so the openings looked like three different sites. The document shell now owns the masthead family - tokens, centred masthead, eyebrow, title, lede, fact strip, scroll cue and the index component - and the pages carry only their content. The Mirror gains the section index the arc has, drops its duplicate technical footer, and its title now uses the same magenta accent instead of a one-off gradient.
The Teaching Lab (learn.bztmon.com)
A static learning site documenting infrastructure I operate in my homelab - the decisions,
the failures, and the validation still outstanding. Dark engineering-dossier aesthetic (void #070b14, cyan #3fbaf5,
magenta #e879f9), dependency-light by design, strict security posture.
Pages
| Route | Page | Source |
|---|---|---|
/ |
The Teaching Lab - chapter directory | pilot/index.tpl.html |
/cluster |
Chapter 01: The Exploded Cluster - eleven-course container-platform teardown | pilot/arc.tpl.html + pilot/manifest-*.json |
/mirror |
Chapter 02: The Mirror - a zot pull-through registry, its failure modes and operations | pilot/mirror.tpl.html + pilot/mirror.js |
Build
bash build.sh -> dist/. The assembler (pilot/assemble-arc.mjs, plain node, no
dependencies) wraps each page in a shared document shell (canonical/OG metadata, build
revision, skip link), content-fingerprints every file under /assets/ (immutable-cached
by nginx), splits page scripts into external fingerprinted files, injects intrinsic image
dimensions (WebP and JPEG parsed from the actual bytes) with deliberate lazy/eager loading,
and runs per-page preflight gates: no inline scripts (CSP script-src 'self' with no
exceptions), exactly one h1, unique ids, no dangling anchors, no unresolved placeholders,
and page-specific content assertions. A failed gate fails the build.
Ship: podman build (multi-stage: node builds dist/, nginx-unprivileged serves it,
both digest-pinned) -> push to the estate registry -> digest-pinned deploy via GitOps.
nginx/ carries the runtime config: strict CSP, HSTS, no-cache HTML with immutable
fingerprinted assets, security headers on every location.
Accessibility and performance
Skip link on every page (hidden until keyboard focus), one h1 per page, stable section
anchors, reduced-motion honoured live (effects also pause when the tab is hidden or the
scene is off screen), intrinsic image dimensions to prevent layout shift, below-fold images
lazy-loaded, system font stacks (CSP permits no third-party fonts).
Layout
| Path | What |
|---|---|
pilot/ |
page templates, page scripts, scene manifests, the assembler |
assets/dist/ |
production heroes and diagrams for /cluster |
assets/mirror/ |
plates for /mirror |
content/ |
course source notes |
nginx/ |
runtime config baked into the image |
tools/slicer.html |
slice editor: load a hero, tune cut lines, export a scene manifest |
prompts/ |
image prompt contract for the house visual style |
exploded-cluster.html, ec2-part*.html |
v2 (parked, superseded by /cluster) |
gsap.min.js / st.min.js |
GSAP 3.13 + ScrollTrigger, fingerprinted at assemble time |
The slicing rule (load-bearing - read before touching the arc engine)
Parts occlude each other in 2D, so every slice carries a hidden notch where the part in
front covers it. Animating collapsed -> hero only ever increases occlusion, so notches
stay buried. Never overshoot past the hero layout and never fan parts sideways - either
exposes the tears. Region boundaries overlap upward by EPS; expanding downward would ghost.