Capture:
- Preserve real asset extensions (.lottie no longer truncated); extract the
default animation JSON from dotLottie ZIP containers at materialization
(minimal built-in ZIP reader, no new deps)
- Reset scroll + settle before every viewport snapshot so scroll-linked
styles can't bake into captured computed values
- Marquee detection now requires content overflow (>1.35x), sustained
velocity across separated windows, scroll independence (jiggle test), and
genuine content repetition — kills scroll-settle-lerp false positives
- Sizing probe trusts authored explicit heights (px/vh/calc) over circular
parent/child reflow verdicts, fixing dropped 100vh heroes
Normalize/generate:
- Canonicalize identity transforms to "none" per viewport; emit explicit
transform resets across bands when any band has a real transform
- DittoLottie keeps the captured placeholder until DOMLoaded; failed loads
no longer blank the container
- mx-auto only for true auto-margin centering; literal per-band margins are
preserved (fixes full-bleed regressions)
- Spacing-scale snap tightened to 0.25px (3.5px stays p-[3.5px]); emit
whitespace-nowrap for wrap-vulnerable single-line text leaves
- Grid recipes no longer override captured track counts or column spans;
responsive re-flow plans apply only when geometry agrees
Quality scoring:
- Rewritten 6-dimension rubric (payload, decomposition, duplication,
semantics, hygiene, runtime) with catastrophe caps and per-dimension
reporting; thresholds are documented calibration guides
218 tests pass (74 new), typecheck clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>