- Hero auto-rotates story backdrops every 5 seconds with 500ms fade transitions - ProductShowcase cards rotate with mint ring highlight on the active card - Inactive cards fade to 60% opacity on mobile, full opacity on desktop - New components: StoryBackdrop, StoryOnboarding, CountUp, lib/stories - Manual Another-story control still works Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
42 lines
1.3 KiB
TypeScript
42 lines
1.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import type { GoalStory } from "@/lib/stories";
|
|
|
|
/**
|
|
* Full-bleed background video for a goal story, with a legibility scrim and a
|
|
* graceful gradient fallback (used while loading or if the video fails).
|
|
* Reusable: fills whatever positioned container it's placed in.
|
|
*/
|
|
export function StoryBackdrop({ story, dim = "strong" }: { story: GoalStory; dim?: "strong" | "soft" }) {
|
|
const [failed, setFailed] = useState(false);
|
|
|
|
return (
|
|
<div className="absolute inset-0 overflow-hidden" aria-hidden>
|
|
{/* gradient fallback always behind the video */}
|
|
<div className={`absolute inset-0 bg-gradient-to-br ${story.fallback}`} />
|
|
{!failed && (
|
|
<video
|
|
key={story.id}
|
|
className="absolute inset-0 h-full w-full scale-105 object-cover"
|
|
src={story.video}
|
|
autoPlay
|
|
muted
|
|
loop
|
|
playsInline
|
|
preload="auto"
|
|
onError={() => setFailed(true)}
|
|
/>
|
|
)}
|
|
{/* scrim for text legibility */}
|
|
<div
|
|
className={
|
|
dim === "strong"
|
|
? "absolute inset-0 bg-gradient-to-r from-[#08130f]/85 via-[#08130f]/60 to-[#08130f]/30"
|
|
: "absolute inset-0 bg-gradient-to-t from-[#08130f]/80 via-[#08130f]/35 to-transparent"
|
|
}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|