Some checks are pending
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Waiting to run
Limit mainwhite pattern to the landing hero and a bottom footer band; remove it from sections and page headers. Add vertical water-flow animation and stronger hero particles with hover boost. Fix green text on white sections and card descriptions in green bands, including the two-days program cards. Co-authored-by: Cursor <cursoragent@cursor.com>
136 lines
4.9 KiB
TypeScript
136 lines
4.9 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useEffect, useState } from "react";
|
|
import Link from "next/link";
|
|
import { ArrowRight } from "lucide-react";
|
|
import { site } from "@/content/site";
|
|
import { HeroGrantLine } from "@/components/home/HeroGrantLine";
|
|
import { TopoCurvyExtend } from "@/components/brand/TopoCurvyExtend";
|
|
import { HeroTopographyBackground } from "@/components/home/HeroTopographyBackground";
|
|
import { HeroRiftParticles } from "@/components/home/HeroRiftParticles";
|
|
import { TopoProseSurface } from "@/components/layout/TopoProseSurface";
|
|
import { TopoSectionProvider } from "@/components/layout/TopoSectionContext";
|
|
import { ScrollReveal } from "@/components/motion/ScrollReveal";
|
|
import { Button } from "@/components/ui/button";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const INTRO_MS = 10000;
|
|
|
|
export function Hero() {
|
|
const [reduceMotion, setReduceMotion] = useState(false);
|
|
const [introPhase, setIntroPhase] = useState<"intro" | "settled" | "static">("intro");
|
|
const [heroHover, setHeroHover] = useState(false);
|
|
const [hoverPoint, setHoverPoint] = useState({ x: 50, y: 50 });
|
|
|
|
const handleHeroPointerMove = useCallback(
|
|
(e: React.MouseEvent<HTMLElement>) => {
|
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
setHoverPoint({
|
|
x: ((e.clientX - rect.left) / rect.width) * 100,
|
|
y: ((e.clientY - rect.top) / rect.height) * 100,
|
|
});
|
|
setHeroHover(true);
|
|
},
|
|
[]
|
|
);
|
|
|
|
const handleHeroPointerLeave = useCallback(() => {
|
|
setHeroHover(false);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
const apply = () => {
|
|
const reduced = mq.matches;
|
|
setReduceMotion(reduced);
|
|
if (reduced) setIntroPhase("static");
|
|
};
|
|
apply();
|
|
mq.addEventListener("change", apply);
|
|
|
|
if (!mq.matches) {
|
|
const t = window.setTimeout(() => setIntroPhase("settled"), INTRO_MS);
|
|
return () => {
|
|
clearTimeout(t);
|
|
mq.removeEventListener("change", apply);
|
|
};
|
|
}
|
|
return () => mq.removeEventListener("change", apply);
|
|
}, []);
|
|
|
|
return (
|
|
<section
|
|
className="section-white relative isolate min-h-[min(100svh,900px)] overflow-x-hidden overflow-y-visible bg-white"
|
|
onMouseMove={handleHeroPointerMove}
|
|
onMouseLeave={handleHeroPointerLeave}
|
|
>
|
|
<HeroTopographyBackground
|
|
introPhase={introPhase}
|
|
hoverActive={heroHover && !reduceMotion}
|
|
hoverX={hoverPoint.x}
|
|
hoverY={hoverPoint.y}
|
|
className="absolute inset-0"
|
|
/>
|
|
<TopoCurvyExtend />
|
|
<HeroRiftParticles
|
|
active={!reduceMotion}
|
|
className="pointer-events-none absolute inset-0 z-[15]"
|
|
/>
|
|
|
|
<TopoSectionProvider tone="light">
|
|
<div className="topo-content-layer topo-content-readable relative z-20 mx-auto flex min-h-[min(100svh,900px)] max-w-4xl flex-col items-center justify-center px-4 py-24 text-center md:px-6">
|
|
<TopoProseSurface className="flex w-full flex-col items-center text-center">
|
|
<ScrollReveal immediate variant="fade-up" delay={0}>
|
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-[#1a5c38]/80 md:text-sm">
|
|
{site.dates.label} · {site.venue.address}
|
|
</p>
|
|
</ScrollReveal>
|
|
|
|
<ScrollReveal immediate variant="fade-up" delay={200}>
|
|
<h1
|
|
className={cn(
|
|
"rift-hero-heading rift-hero-title mt-6 font-display text-3xl font-bold leading-[1.05] tracking-tight text-[#0d3d26] sm:text-4xl md:text-5xl lg:text-6xl"
|
|
)}
|
|
>
|
|
<span className="font-wordmark block uppercase tracking-wide">
|
|
Great Rift Valley
|
|
</span>
|
|
<span className="mt-1 block text-[#ffb300]">Innovation Summit</span>
|
|
</h1>
|
|
</ScrollReveal>
|
|
|
|
<ScrollReveal immediate variant="fade-up" delay={400}>
|
|
<p className="mx-auto mt-6 max-w-2xl text-lg text-[#1a5c38]/80 md:text-xl">
|
|
{site.tagline} Presented by {site.presentedBy}.
|
|
</p>
|
|
</ScrollReveal>
|
|
|
|
<ScrollReveal immediate variant="fade-up" delay={500}>
|
|
<div className="mt-8 flex flex-wrap items-center justify-center gap-3">
|
|
<Button
|
|
className="rounded-full bg-[#ffb300] px-8 text-[#0f0404] hover:bg-[#ffb300]/90"
|
|
asChild
|
|
>
|
|
<Link href={site.links.pitchApplyUrl}>
|
|
Register <ArrowRight className="size-4" />
|
|
</Link>
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
className="rounded-full border-[#1a5c38]/30 bg-white/80 text-[#1a5c38] backdrop-blur-sm"
|
|
asChild
|
|
>
|
|
<Link href="/payment">Get tickets</Link>
|
|
</Button>
|
|
</div>
|
|
<p className="mt-4 text-sm text-[#1a5c38]/70">
|
|
<HeroGrantLine />
|
|
</p>
|
|
</ScrollReveal>
|
|
</TopoProseSurface>
|
|
</div>
|
|
</TopoSectionProvider>
|
|
</section>
|
|
);
|
|
}
|