feat: hero diagonal split with 3 dancer videos

- Three video panels with diagonal clip-paths and gold separator lines
- Each video centered in its own column for clear visibility
- Replaced nastya.mp4 with nastya-2.mp4

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 15:37:00 +03:00
parent b0c9a77474
commit 1c6462d340

View File

@@ -6,6 +6,8 @@ import { FloatingHearts } from "@/components/ui/FloatingHearts";
import { HeroLogo } from "@/components/ui/HeroLogo"; import { HeroLogo } from "@/components/ui/HeroLogo";
import type { SiteContent } from "@/types/content"; import type { SiteContent } from "@/types/content";
const VIDEOS = ["/video/ira.mp4", "/video/nadezda.mp4", "/video/nastya-2.mp4"];
interface HeroProps { interface HeroProps {
data: SiteContent["hero"]; data: SiteContent["hero"];
} }
@@ -15,10 +17,9 @@ export function Hero({ data: hero }: HeroProps) {
const scrolledRef = useRef(false); const scrolledRef = useRef(false);
const scrollToNext = useCallback(() => { const scrollToNext = useCallback(() => {
const hero = sectionRef.current; const el = sectionRef.current;
if (!hero) return; if (!el) return;
// Find the next sibling section let next = el.nextElementSibling;
let next = hero.nextElementSibling;
while (next && next.tagName !== "SECTION") { while (next && next.tagName !== "SECTION") {
next = next.nextElementSibling; next = next.nextElementSibling;
} }
@@ -26,76 +27,82 @@ export function Hero({ data: hero }: HeroProps) {
}, []); }, []);
useEffect(() => { useEffect(() => {
const hero = sectionRef.current; const el = sectionRef.current;
if (!hero) return; if (!el) return;
function handleWheel(e: WheelEvent) { function handleWheel(e: WheelEvent) {
// Only trigger when scrolling down and still inside hero
if (e.deltaY <= 0 || scrolledRef.current) return; if (e.deltaY <= 0 || scrolledRef.current) return;
if (window.scrollY > 10) return; // already scrolled past hero top if (window.scrollY > 10) return;
scrolledRef.current = true; scrolledRef.current = true;
scrollToNext(); scrollToNext();
// Reset after animation completes
setTimeout(() => { scrolledRef.current = false; }, 1000); setTimeout(() => { scrolledRef.current = false; }, 1000);
} }
function handleTouchStart(e: TouchEvent) { function handleTouchStart(e: TouchEvent) {
(hero as HTMLElement).dataset.touchY = String(e.touches[0].clientY); (el as HTMLElement).dataset.touchY = String(e.touches[0].clientY);
} }
function handleTouchEnd(e: TouchEvent) { function handleTouchEnd(e: TouchEvent) {
const startY = Number((hero as HTMLElement).dataset.touchY); const startY = Number((el as HTMLElement).dataset.touchY);
const endY = e.changedTouches[0].clientY; const endY = e.changedTouches[0].clientY;
const diff = startY - endY; if (startY - endY > 50 && !scrolledRef.current && window.scrollY < 10) {
// Swipe down (finger moves up) with enough distance
if (diff > 50 && !scrolledRef.current && window.scrollY < 10) {
scrolledRef.current = true; scrolledRef.current = true;
scrollToNext(); scrollToNext();
setTimeout(() => { scrolledRef.current = false; }, 1000); setTimeout(() => { scrolledRef.current = false; }, 1000);
} }
} }
hero.addEventListener("wheel", handleWheel, { passive: true }); el.addEventListener("wheel", handleWheel, { passive: true });
hero.addEventListener("touchstart", handleTouchStart, { passive: true }); el.addEventListener("touchstart", handleTouchStart, { passive: true });
hero.addEventListener("touchend", handleTouchEnd, { passive: true }); el.addEventListener("touchend", handleTouchEnd, { passive: true });
return () => { return () => {
hero.removeEventListener("wheel", handleWheel); el.removeEventListener("wheel", handleWheel);
hero.removeEventListener("touchstart", handleTouchStart); el.removeEventListener("touchstart", handleTouchStart);
hero.removeEventListener("touchend", handleTouchEnd); el.removeEventListener("touchend", handleTouchEnd);
}; };
}, [scrollToNext]); }, [scrollToNext]);
return ( return (
<section ref={sectionRef} className="relative flex min-h-svh items-center justify-center overflow-hidden bg-[#050505]"> <section ref={sectionRef} className="relative flex min-h-svh items-center justify-center overflow-hidden bg-[#050505]">
{/* Animated gradient background */} {/* Diagonal split background — 3 dancer videos */}
<div className="hero-bg-gradient absolute inset-0" /> <div className="absolute inset-0">
{VIDEOS.map((src, i) => {
{/* Glow orbs */} const positions = [
<div { left: "0%", width: "38%" },
className="hero-glow-orb" { left: "31%", width: "38%" },
style={{ { left: "62%", width: "38%" },
width: "500px", ];
height: "500px", const clips = [
top: "-10%", "polygon(0 0, 100% 0, 86% 100%, 0 100%)",
left: "50%", "polygon(14% 0, 100% 0, 86% 100%, 0 100%)",
transform: "translateX(-50%)", "polygon(14% 0, 100% 0, 100% 100%, 0 100%)",
background: "radial-gradient(circle, rgba(201, 169, 110, 0.12), transparent 70%)", ];
}} return (
/> <div
<div key={src}
className="hero-glow-orb" className="absolute top-0 bottom-0 overflow-hidden"
style={{ style={{
width: "300px", left: positions[i].left,
height: "300px", width: positions[i].width,
bottom: "10%", clipPath: clips[i],
right: "10%", }}
background: "radial-gradient(circle, rgba(201, 169, 110, 0.08), transparent 70%)", >
animationDelay: "3s", <video
}} autoPlay muted loop playsInline
/> className="absolute inset-0 h-full w-full object-cover object-center"
>
<source src={src} type="video/mp4" />
</video>
<div className="absolute inset-0 bg-black/50" />
</div>
);
})}
{/* Gold diagonal lines between panels */}
<svg className="absolute inset-0 h-full w-full z-10 pointer-events-none" preserveAspectRatio="none" viewBox="0 0 100 100">
<line x1="38" y1="0" x2="33" y2="100" stroke="rgba(201,169,110,0.25)" strokeWidth="0.15" />
<line x1="69" y1="0" x2="64" y2="100" stroke="rgba(201,169,110,0.25)" strokeWidth="0.15" />
</svg>
</div>
{/* Floating hearts */} {/* Floating hearts */}
<FloatingHearts /> <FloatingHearts />
@@ -103,7 +110,6 @@ export function Hero({ data: hero }: HeroProps) {
{/* Content */} {/* Content */}
<div className="section-container relative z-10 text-center"> <div className="section-container relative z-10 text-center">
<div className="hero-logo relative mx-auto mb-10 flex items-center justify-center" style={{ width: 220, height: 181 }}> <div className="hero-logo relative mx-auto mb-10 flex items-center justify-center" style={{ width: 220, height: 181 }}>
{/* Soft ambient glow behind heart */}
<div className="absolute -inset-10 rounded-full blur-[80px]" style={{ background: "radial-gradient(circle, rgba(201,169,110,0.25), transparent 70%)" }} /> <div className="absolute -inset-10 rounded-full blur-[80px]" style={{ background: "radial-gradient(circle, rgba(201,169,110,0.25), transparent 70%)" }} />
<div className="hero-logo-heartbeat relative"> <div className="hero-logo-heartbeat relative">
<HeroLogo <HeroLogo
@@ -127,7 +133,6 @@ export function Hero({ data: hero }: HeroProps) {
</Button> </Button>
</div> </div>
</div> </div>
</section> </section>
); );
} }