mirror rev 4: evidence-verified rewrite, six-diagram artefact set, front-end and build-gate hardening
Content corrected against upstream docs, deployed-version source and the live config: retention/GC semantics (untagged manifests including digest-only pull-through entries are GC-eligible; keepUntagged is v2.1.19+, not deployed), warm-cache guarantees (tag pulls revalidate upstream per open issue; digest pulls serve locally), retry arithmetic (buildah --retry counts retries: 16 max, not 12), five-upstream flat-namespace routing stated as observed risk with a proposed prefix redesign, auth-canary design validated against longest-match policy precedence with the Docker-client caveat, coalescing reframed from deadlock to design with the restart as recovery-not-root-cause. Adds fact panel, contents, failure-mode and cache test matrices, decision table, open verification register and primary-source references. Front-end: grid full-bleed (no viewport units - no scrollbar overflow), shell-level skip link, real 1600x872 intrinsic dims injected from bytes, lazy below-fold plates, AA code-comment contrast, scene engine rewritten (IO-gated, rAF-coalesced, live reduced-motion, hidden-tab pause, zero guards). Assembler: per-page gates plus mirror-specific required/banned content assertions. README describes the multi-page site honestly.
This commit is contained in:
+96
-31
@@ -1,39 +1,104 @@
|
||||
(function(){
|
||||
var rm = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
var bar = document.getElementById('bar');
|
||||
var s20 = document.querySelector('#s20 img');
|
||||
var ring = document.getElementById('ring');
|
||||
var glows = [].slice.call(document.querySelectorAll('.s22-glow'));
|
||||
var s24 = document.querySelector('#s24 img');
|
||||
function prog(el){
|
||||
var r = el.getBoundingClientRect(), vh = innerHeight;
|
||||
return Math.max(0, Math.min(1, (vh - r.top) / (vh + r.height)));
|
||||
}
|
||||
function onScroll(){
|
||||
var d = document.documentElement;
|
||||
bar.style.width = (100 * d.scrollTop / (d.scrollHeight - innerHeight)) + '%';
|
||||
if (rm) return;
|
||||
var p20 = prog(s20); /* dock-and-stow: assembles inward */
|
||||
s20.style.transform = 'scale(' + (1.07 - 0.07 * Math.min(1, p20 * 1.6)) + ') translateY(' + (26 - 26 * Math.min(1, p20 * 1.6)) + 'px)';
|
||||
var p21 = prog(ring); /* ring-walk: the cycle turns */
|
||||
ring.style.transform = 'rotate(' + (p21 * 300) + 'deg)';
|
||||
glows.forEach(function(g, i){ /* checkpoint procession: three beats */
|
||||
var p = prog(g.parentElement);
|
||||
var band = 0.18 + i * 0.22;
|
||||
g.style.opacity = Math.max(0, Math.min(0.85, (p - band) * 6));
|
||||
// The Mirror - scroll effects. External file: script-src 'self' holds, no inline JS.
|
||||
// Design constraints: no perpetual work - scroll reads are coalesced into a single rAF,
|
||||
// per-scene work only runs while that scene intersects the viewport, the pulse loop
|
||||
// runs only while its scene is visible AND the tab is visible AND motion is allowed,
|
||||
// and a change to prefers-reduced-motion takes effect live without a reload.
|
||||
(function () {
|
||||
"use strict";
|
||||
var motionQuery = matchMedia("(prefers-reduced-motion: reduce)");
|
||||
var bar = document.getElementById("bar");
|
||||
var s20 = document.querySelector("#s20 img");
|
||||
var ring = document.getElementById("ring");
|
||||
var s22 = document.getElementById("s22");
|
||||
var glows = s22 ? [].slice.call(s22.querySelectorAll(".s22-glow")) : [];
|
||||
var s24 = document.querySelector("#s24 img");
|
||||
var pulse = document.getElementById("pulse");
|
||||
|
||||
// visibility ledger: IntersectionObserver marks which scenes are on screen
|
||||
var visible = {};
|
||||
var watched = [
|
||||
["s20", s20 && s20.closest(".scene")],
|
||||
["s21", ring && ring.closest(".scene")],
|
||||
["s22", s22 && s22.closest(".scene")],
|
||||
["s23", pulse && pulse.closest(".scene")],
|
||||
["s24", s24 && s24.closest(".scene")],
|
||||
].filter(function (w) { return w[1]; });
|
||||
var io = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (e) {
|
||||
var hit = watched.find(function (w) { return w[1] === e.target; });
|
||||
if (hit) visible[hit[0]] = e.isIntersecting;
|
||||
});
|
||||
var p24 = prog(s24); /* two-plane parallax */
|
||||
s24.style.transform = 'translateY(' + ((p24 - 0.5) * -26) + 'px) scale(1.03)';
|
||||
if (visible.s23) startPulse(); // (re)arm the breather when its scene returns
|
||||
}, { rootMargin: "10% 0px" });
|
||||
watched.forEach(function (w) { io.observe(w[1]); });
|
||||
|
||||
// progress of an element through the viewport, guarded against zero heights
|
||||
function prog(el) {
|
||||
var r = el.getBoundingClientRect();
|
||||
var span = innerHeight + r.height;
|
||||
if (!span) return 0;
|
||||
return Math.max(0, Math.min(1, (innerHeight - r.top) / span));
|
||||
}
|
||||
addEventListener('scroll', onScroll, {passive: true});
|
||||
addEventListener('resize', onScroll);
|
||||
onScroll();
|
||||
if (!rm){ /* the jam breathes; nothing else does */
|
||||
var pulse = document.getElementById('pulse'), t0 = null;
|
||||
requestAnimationFrame(function breathe(t){
|
||||
|
||||
// scroll work: one rAF per burst, only touching scenes that are on screen
|
||||
var ticking = false;
|
||||
function frame() {
|
||||
ticking = false;
|
||||
var d = document.documentElement;
|
||||
var denom = d.scrollHeight - innerHeight;
|
||||
if (bar) bar.style.width = (denom > 0 ? (100 * d.scrollTop / denom) : 0) + "%";
|
||||
if (motionQuery.matches) return;
|
||||
if (visible.s20 && s20) {
|
||||
var p20 = Math.min(1, prog(s20) * 1.6);
|
||||
s20.style.transform = "scale(" + (1.07 - 0.07 * p20) + ") translateY(" + (26 - 26 * p20) + "px)";
|
||||
}
|
||||
if (visible.s21 && ring) ring.style.transform = "rotate(" + (prog(ring) * 300) + "deg)";
|
||||
if (visible.s22 && glows.length) {
|
||||
var p22 = prog(s22);
|
||||
glows.forEach(function (g, i) {
|
||||
g.style.opacity = Math.max(0, Math.min(0.85, (p22 - (0.18 + i * 0.22)) * 6));
|
||||
});
|
||||
}
|
||||
if (visible.s24 && s24) s24.style.transform = "translateY(" + ((prog(s24) - 0.5) * -26) + "px) scale(1.03)";
|
||||
}
|
||||
function onScroll() {
|
||||
if (!ticking) { ticking = true; requestAnimationFrame(frame); }
|
||||
}
|
||||
addEventListener("scroll", onScroll, { passive: true });
|
||||
addEventListener("resize", onScroll);
|
||||
|
||||
// the jam's breathing glow: runs only while worth running, stops itself otherwise
|
||||
var breathing = false;
|
||||
function startPulse() {
|
||||
if (breathing || !pulse || motionQuery.matches || document.hidden || !visible.s23) return;
|
||||
breathing = true;
|
||||
var t0 = null;
|
||||
requestAnimationFrame(function breathe(t) {
|
||||
if (motionQuery.matches || document.hidden || !visible.s23) {
|
||||
breathing = false;
|
||||
pulse.style.opacity = 0.25;
|
||||
return;
|
||||
}
|
||||
if (t0 === null) t0 = t;
|
||||
pulse.style.opacity = 0.18 + 0.2 * (0.5 + 0.5 * Math.sin((t - t0) / 520));
|
||||
requestAnimationFrame(breathe);
|
||||
});
|
||||
}
|
||||
document.addEventListener("visibilitychange", function () { if (!document.hidden) { startPulse(); onScroll(); } });
|
||||
|
||||
// live preference change: clear transforms so the page settles to its static state
|
||||
function motionChanged() {
|
||||
if (motionQuery.matches) {
|
||||
[s20, ring, s24].forEach(function (el) { if (el) el.style.transform = ""; });
|
||||
glows.forEach(function (g) { g.style.opacity = 0.35; });
|
||||
if (pulse) pulse.style.opacity = 0.25;
|
||||
} else {
|
||||
startPulse();
|
||||
onScroll();
|
||||
}
|
||||
}
|
||||
if (motionQuery.addEventListener) motionQuery.addEventListener("change", motionChanged);
|
||||
|
||||
startPulse();
|
||||
onScroll();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user