How it behaves
Exactly what the prompt builds — every timing and easing is specified, so the result matches the demo.
- Section: height: 240svh with a sticky stage inside (position: sticky; top: 0; height: 100svh), ground #EDE7DF, ink #1B1916.
- Progress: p = -sectionTop / (sectionHeight - viewportHeight), clamped 0 to 1, updated on scroll with requestAnimationFrame. The motion finishes at 80% of it and holds for the last 20%: e = easeInOutCubic(min(1, p / 0.8)), written to the section as --e.
- Photo window: a full-stage layer clipped with clip-path: inset(…). At e = 0 the inset is 0 (full bleed). At e = 1 it is an arched card --cw = clamp(220px, 26vw, 380px) wide and 4 / 3 as tall, centered horizontally with its center 44% down the stage: top corners rounded to half the card width, bottom corners 16px. Every inset and radius is multiplied by e, so the edges close in smoothly.
- Photo inside the window: translateY(e × -6svh) scale(1 - e × 0.38), so the object itself drifts up and gets smaller while the window closes around it.
- Headline: two words (Instrument Serif, clamp(56px, 12vw, 210px), line-height 0.9, letter-spacing -0.03em) on the same line as the card's center, the first on the left edge and the second on the right edge. They sit behind the photo and slide outward from the middle: left translateX((1 - e) × 14vw), right translateX((1 - e) × -14vw), opacity e.
- Labels and details: a top row (brand, "Drop 04 / Lighting") and a bottom row (product name, price and an underlined "Add to bag" link that turns the accent #9A5B2E on hover) fade in with opacity: clamp(0, (e - 0.6) / 0.4, 1) and rise 16px while they do.
- Hint: a small "Scroll" label bottom center at the start, fading out as e reaches 0.2.
- Phones (under 768px): card --cw: 62vw, the headline stacks above and below the card instead of beside it (first word just above it, second just below, both centered, sliding apart vertically) and the bottom row wraps.
- Fallbacks: reduced motion skips the scroll story and shows the finished state (--e: 1) in a normal 100svh section. Without JavaScript the section shows the full-bleed photo with the headline hidden behind it, so nothing breaks.