How it behaves
Exactly what the prompt builds — every timing and easing is specified, so the result matches the demo.
- Section: height: min(100svh, 977px); min-height: 620px, sage ground #8F9A8A, dark ink #1F1812. The top 69% is a full-width photo band (object-fit: cover). The bottom 31% is a 1440px-wide stage. A paper texture is blended over the lower 61% of the section at 14% opacity (mix-blend-mode: overlay).
- Stage layout: the title sits 48px from the left, 312px wide (Marcellus 32px, line-height 1.2, letter-spacing -0.025em, uppercase). The copy block sits 507px from the left, 541px wide: a 512px paragraph (Hanken Grotesk Light 16px, line-height 1.2), then 35px lower, the button. Both start 31.7% down the stage.
- Button at rest: display: inline-flex; gap: 16px; height: 38px; padding-right: 20px; width: fit-content. The ring is a 38 × 38 square with a 1px border in currentColor and no radius. The arrow inside is rotated -45° so it points up and to the right. The label is 16px uppercase.
- Button on hover or keyboard focus: a ::before fill (background: currentColor, z-index: -1 inside an isolation: isolate button) grows from width: 0 to 100% over 500ms cubic-bezier(0.4, 0, 0.2, 1). The label and arrow change to var(--tds-spill-on), the section's ground color, over 500ms. The arrow moves translateX(3px) after its rotation, so it travels along its own diagonal. The fill starts at 0 width, not 38px, or it would cover the ring and arrow at rest.
- Title: each word resolves from opacity: 0; blur(10px) to sharp over 0.8s, 0.035s apart, once 35% of it is visible.
- Under 1200px: the section grows with its content, the band keeps a 1440:674 ratio, the stage stacks (title then copy, 32px gap, 56px/24px/64px padding) and the title is 26px. Phones: 48px/16px/56px padding, 24px gap, title 20px.
- Fallbacks: reduced motion keeps the hover colors but skips the transitions and the blur. Without JavaScript the title is plain text.