"use client"; import { motion, useReducedMotion, useScroll, useTransform, } from "framer-motion"; import { Logo } from "../ui/Logo"; import { ButtonLink } from "../ui/ButtonLink"; import { HERO_VIDEO, STUDIO } from "@/lib/site"; import { EASE_OUT_EXPO } from "@/lib/motion"; /** * Full-height hero. * * The dark plate sits under the video, so the headline is readable from the * first paint — before the background has loaded, and for visitors who have * reduced motion enabled (they get no video at all). * * Layout note: everything is in normal flow — the scroll hint is NOT absolutely * positioned. On a laptop (wide but only ~900px tall) an absolute hint sat on * top of the buttons. Spacing and type are clamped against `vh` so the block * shrinks with the viewport height instead of overflowing it. */ export function HomeHero() { const reduced = useReducedMotion(); // Skip entrance animations entirely when reduced motion is requested. const fade = (delay: number) => reduced ? {} : { initial: { opacity: 0, y: 24 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.8, delay, ease: "easeOut" as const }, }; /** * Scroll response. The video drifts slower than the page and the copy * dissolves before the section ends, so the hero hands over to the next band * instead of sliding out of frame intact. Page scroll is read directly — * the hero is always at offset 0, so there is nothing to measure. */ const { scrollY } = useScroll(); const videoY = useTransform(scrollY, [0, 800], [0, 150], { clamp: true }); const contentY = useTransform(scrollY, [0, 600], [0, 70], { clamp: true }); // Holds at full strength for the first 100px: a visitor who nudges the wheel // to see whether the page moves should not find the headline already faded. const contentOpacity = useTransform(scrollY, [100, 560], [1, 0], { clamp: true, }); const hintOpacity = useTransform(scrollY, [0, 180], [1, 0], { clamp: true }); return (
{!reduced && ( // Taller than the section and pulled up, so the parallax never // uncovers the bottom edge of the frame. )} {/* Readability scrim: flat tint + vignette, in a single layer */}
HIGHLAND GAMES
STUDIO
{STUDIO.tagline} EXPLORE OUR GAMES MEET THE STUDIO
{!reduced && ( // Fades on the first flick of the wheel: an invitation to scroll that // is still there once you have is just decoration. Two layers, because // one element cannot hold both a load-time `animate` opacity and a // scroll-driven one. SCROLL )}
); }