How it behaves
Exactly what the prompt builds — every timing and easing is specified, so the result matches the demo.
- Ground: parchment #EFE4D3 with a subtle paper texture. The texture's background-position-y moves from -280px to 0 as the section crosses the viewport, so it falls behind the scroll.
- Header: 1440px frame with 48px side padding. "The Edit" (Marcellus 88px, line-height 0.86, letter-spacing -0.025em, uppercase) on the left. On the right a 427px column holds a justified paragraph and, 32px below, a 241 × 43 button (uppercase 16px, #3A2216 fill, parchment label, turns rust #A65A2E on hover over 300ms). The header sits 96px above the grid. Section padding is 120px top and 171px bottom.
- Staggered grid: two equal columns. Left column: card 1 at 80.5% width (image 541:426), then 566px lower card 2 at 97.6% width (wide image 656:426). Right column starts 229px lower, right aligned: card 3 at 97.6% (wide), then 108px lower card 4 at 80.5%.
- Three depths: the grid block publishes --sy = viewportHeight / 2 - blockCenter (0 when it is centered). Each card moves translateY(var(--sy) × rate) with rates 0.062, 0.038, 0.020 and 0.074 for cards 1 to 4.
- Card: photo, then 32px lower a row with the product name (Marcellus 24px uppercase) and the price, then 30px lower a 386px description in Hanken Grotesk Light 16px. Hovering a card scales its photo to 1.03 over 900ms cubic-bezier(0.22, 0.61, 0.36, 1).
- Photo wipe: clip-path: inset(0 0 calc((1 - var(--rv)) × 100%) 0). --rv goes 0 to 1 as the card's top travels from 72% to 25% of the viewport height, and keeps its highest value so photos only ever open. On touch screens it does not scrub: once 30% of the card is visible --rv jumps to 1 and the clip transitions over 1100ms.
- Cursor arrow (fine pointers only): an 84px circle with a 1px parchment border and a 45° arrow follows the pointer on a spring (stiffness 400, damping 32, mass 0.4). Over any card it fades in and scales from 0.55 to 1 while the native cursor hides. One listener on the grid, not one per card. The first move after entering jumps the spring to the pointer so it doesn't glide in from a corner.
- Blur-in text: the heading's letters and each card title's words resolve from opacity: 0; blur(10px) to sharp over 0.8s, 0.035s apart, once 35% of them is visible.
- Under 1200px: a single column with every card full width and 56px apart (40px on phones), the header stacks with a 32px gap, the heading shrinks to clamp(38px, 9vw, 64px). On phones the card drift is switched off (it caused overlaps) but the lagging texture stays.
- Fallbacks: without JavaScript everything is visible and photos are fully open (--rv defaults to 1). Reduced motion: no drift, no lag, no wipe, no blur.