Wave 4: reveal settling + replay, 206/magic-byte media fix, hidden-geometry banding, bare-zero utility gating

Capture (4a):
- stabilize: dwell-scroll settling pass + known-library pre-reveal
  neutralization after lazy promotion, so every viewport snapshot records
  the post-reveal steady state (about-page sections no longer captured
  blanket-hidden: 24 base `invisible` wrappers -> 0)
- probeReveals: extended to the visibility + entrance-animation family;
  DittoMotion replays them (JS re-hide on mount, IntersectionObserver
  reveal, 4s force-reveal failsafe) so SSR/non-JS still shows content
- assets: 206 range-response bodies are no longer stored as full assets
  (full-fetch fallback) + video magic-byte validation - the about-page
  hero video is now a valid 6.0MB ftyp mp4 instead of 18.9KB of garbage

Generate (4b):
- css: hidden-node geometry policy - a visibility:hidden box with a 0x0
  captured bbox at a band becomes display:none there; an OCCUPYING hidden
  box gets the captured per-viewport geometry instead of the baked
  canonical one. Also covers the ancestor-hidden-at-base case (the
  cropin.com/cotton slider arrow: hide inherited from an elementor-widget
  ancestor at every width parked a desktop left:548px box inside a 375px
  viewport -> body.scrollWidth 585; now 375 on both verified pages)
- tailwind: bare-zero utility gating - font-size:0 -> text-[0px] and
  letter-spacing:0 -> tracking-[0px] (text-0/tracking-0 are silently
  invalid in Tailwind v4; fixes permanently-visible map labels on the
  cotton Global-presence section)

Verified: 87/87 compiler tests green, full-workspace suite green,
regenerated cropin.com + /cotton/ builds measure 375px scrollWidth at a
375px viewport, and a fresh /about/ clone renders all reveal sections
with scroll-replay against the live site.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samraaj Bath
2026-07-03 18:33:10 -07:00
co-authored by Claude Fable 5
parent 9aed2540aa
commit 199359d0a6
6 changed files with 494 additions and 15 deletions
+32 -15
View File
@@ -3012,24 +3012,41 @@ export function collectNodeRules(ir: IR, assetMap: Map<string, string>, includeN
// (declsForViewport emits `visibility:hidden`; parent-equality keeps it own-only).
} else if (ownNone || !node.visibleByVp[b.vp]) {
const shownAtBase = node.visibleByVp[baseVp] && (node.computedByVp[baseVp]?.display || "") !== "none";
if (ownNone) {
// Own display:none removes the box from layout entirely. Emit the hide even when the node
// is ALSO hidden at base — a visibility:hidden base still bakes an OCCUPYING box (see
// above), so without this band the canonical geometry would render at this width. Skip
// only when the base itself is display:none (the band would be redundant).
if ((node.computedByVp[baseVp]?.display || "") !== "none") {
// Hidden by an ANCESTOR's visibility here AND at base (the ancestor's own rule carries
// the hide at every width) — but a visibility:hidden box still PARTICIPATES in layout,
// and the base rule bakes CANONICAL geometry. Same policy as the own-hidden path above:
// a 0x0 box gets display:none; an occupying box falls through to the per-viewport delta
// so it sits where the capture measured it at THIS width — not parked at e.g. a desktop
// left:548px inside a 375px viewport (the cropin.com/cotton slider arrow, +210px of
// sideways scroll at 375 with the hide inherited from an elementor-widget ancestor).
const ancestorHiddenHere = !ownNone && /^(hidden|collapse)$/.test(vpCs.visibility || "");
if (ancestorHiddenHere && !shownAtBase) {
const bb = node.bboxByVp[b.vp];
if (!bb || bb.width <= 0 || bb.height <= 0) {
nr.bands.push({ media: b.media, decls: new Map([["display", "none"]]) });
continue;
}
} else if (shownAtBase) {
// Hidden by an ancestor (or zero-size / opacity:0): geometry overrides are breakpoint
// noise — the ancestor's own hide (or the reveal replay, for scroll-reveal opacity)
// covers it. Emit only the hide the node itself carries.
const hide = new Map<string, string>();
if (pf(vpCs.opacity) === 0 && !animOwned.has("opacity")) hide.set("opacity", "0");
if (/^(hidden|collapse)$/.test(vpCs.visibility || "")) hide.set("visibility", "hidden");
if (hide.size) nr.bands.push({ media: b.media, decls: hide });
// Occupying: fall through to the normal per-viewport delta below.
} else {
if (ownNone) {
// Own display:none removes the box from layout entirely. Emit the hide even when the node
// is ALSO hidden at base — a visibility:hidden base still bakes an OCCUPYING box (see
// above), so without this band the canonical geometry would render at this width. Skip
// only when the base itself is display:none (the band would be redundant).
if ((node.computedByVp[baseVp]?.display || "") !== "none") {
nr.bands.push({ media: b.media, decls: new Map([["display", "none"]]) });
}
} else if (shownAtBase) {
// Hidden by an ancestor (or zero-size / opacity:0) but visible at base: geometry
// overrides are breakpoint noise — the ancestor's own hide (or the reveal replay,
// for scroll-reveal opacity) covers it. Emit only the hide the node itself carries.
const hide = new Map<string, string>();
if (pf(vpCs.opacity) === 0 && !animOwned.has("opacity")) hide.set("opacity", "0");
if (/^(hidden|collapse)$/.test(vpCs.visibility || "")) hide.set("visibility", "hidden");
if (hide.size) nr.bands.push({ media: b.media, decls: hide });
}
continue;
}
continue;
}
const centeredVp = stableCenter || (layoutParent ? centeredAtVp(node, layoutParent, b.vp) : false);
const vpDecls = finalizeDecls(declsForViewport(node, parentNode?.computedByVp[b.vp], b.vp, assetMap, centeredVp, colorVar, ir.doc.perViewport[b.vp]?.scrollHeight, widthPlan, gridColsByVp?.get(b.vp), gridRowsByVp?.get(b.vp), flowH, dropInsets, leftPct, heightFill, geometry, dropGridRows, dropViewportMaxWidth), tokenResolver);