102 lines
3.8 KiB
TypeScript
102 lines
3.8 KiB
TypeScript
import React, { useRef, useState } from 'react';
|
|
import { Brain, LineChart, Mic, CalendarClock } from 'lucide-react';
|
|
import { FeatureItem } from '../types';
|
|
|
|
const FEATURES: FeatureItem[] = [
|
|
{
|
|
title: "The Performance Co-Pilot",
|
|
description: "Training-aware guidance. We tell you to eat more carbs on Leg Day and prioritize protein on Rest Days.",
|
|
icon: Brain,
|
|
className: "md:col-span-2 md:row-span-2"
|
|
},
|
|
{
|
|
title: "Day 1 Action Plan",
|
|
description: "No 'learning phase.' Get an intelligent, customized plan immediately.",
|
|
icon: CalendarClock,
|
|
className: "md:col-span-1 md:row-span-1"
|
|
},
|
|
{
|
|
title: "Purposeful Logging",
|
|
description: "Log via photo, voice, or text. Inputs act as commands to the Co-Pilot.",
|
|
icon: Mic,
|
|
className: "md:col-span-1 md:row-span-1"
|
|
},
|
|
{
|
|
title: "Forward-Looking Dashboard",
|
|
description: "See how 92% consistency translates to trend weight changes.",
|
|
icon: LineChart,
|
|
className: "md:col-span-3 lg:col-span-2"
|
|
}
|
|
];
|
|
|
|
const SpotlightCard: React.FC<{ feature: FeatureItem }> = ({ feature }) => {
|
|
const divRef = useRef<HTMLDivElement>(null);
|
|
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
const [opacity, setOpacity] = useState(0);
|
|
|
|
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
if (!divRef.current) return;
|
|
const rect = divRef.current.getBoundingClientRect();
|
|
setPosition({ x: e.clientX - rect.left, y: e.clientY - rect.top });
|
|
};
|
|
|
|
const handleMouseEnter = () => setOpacity(1);
|
|
const handleMouseLeave = () => setOpacity(0);
|
|
|
|
return (
|
|
<div
|
|
ref={divRef}
|
|
onMouseMove={handleMouseMove}
|
|
onMouseEnter={handleMouseEnter}
|
|
onMouseLeave={handleMouseLeave}
|
|
className={`relative rounded-3xl border border-white/10 bg-black overflow-hidden group ${feature.className}`}
|
|
>
|
|
{/* Spotlight Gradient */}
|
|
<div
|
|
className="pointer-events-none absolute -inset-px transition duration-300 opacity-0 group-hover:opacity-100"
|
|
style={{
|
|
background: `radial-gradient(600px circle at ${position.x}px ${position.y}px, rgba(255,255,255,0.1), transparent 40%)`,
|
|
}}
|
|
/>
|
|
|
|
{/* Content Container */}
|
|
<div className="relative h-full p-8 flex flex-col bg-surface/50 backdrop-blur-xl z-10 m-[1px] rounded-[23px]">
|
|
<div className="mb-6 w-12 h-12 rounded-2xl bg-white/5 border border-white/10 flex items-center justify-center text-primary-400 group-hover:scale-110 transition-transform duration-300">
|
|
<feature.icon size={24} />
|
|
</div>
|
|
|
|
<div className="mt-auto">
|
|
<h3 className="text-xl font-bold text-white mb-3 tracking-tight">
|
|
{feature.title}
|
|
</h3>
|
|
<p className="text-gray-400 text-sm leading-relaxed">
|
|
{feature.description}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const Features: React.FC = () => {
|
|
return (
|
|
<section id="features" className="py-32 bg-black relative scroll-mt-32">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="mb-20 text-center max-w-3xl mx-auto">
|
|
<h2 className="text-4xl md:text-6xl font-bold text-white mb-6 tracking-tighter">
|
|
Engineered for <span className="text-transparent bg-clip-text bg-gradient-to-r from-primary-400 to-blue-500">Results</span>
|
|
</h2>
|
|
<p className="text-gray-400 text-lg">
|
|
A suite of tools designed to minimize friction and maximize adherence.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4 lg:gap-6 auto-rows-[minmax(280px,auto)]">
|
|
{FEATURES.map((feature, idx) => (
|
|
<SpotlightCard key={idx} feature={feature} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}; |