This commit is contained in:
2026-01-29 22:11:28 +01:00
parent a2e048826f
commit 802d707487
8 changed files with 389 additions and 190 deletions

View File

@@ -0,0 +1,116 @@
'use client';
import * as React from 'react';
import { motion } from 'framer-motion';
interface LineProps {
className?: string;
delay?: number;
}
export const HeroLines: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 800 600" fill="none" xmlns="http://www.w3.org/2000/svg">
<motion.path
d="M-100 300 C 100 300, 200 100, 400 100 C 600 100, 700 500, 900 500"
stroke="currentColor"
strokeWidth="1"
strokeLinecap="round"
className="text-slate-200"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={{ duration: 2, delay: delay, ease: "easeInOut" }}
/>
<motion.path
d="M-100 350 C 100 350, 200 150, 400 150 C 600 150, 700 550, 900 550"
stroke="currentColor"
strokeWidth="1"
strokeLinecap="round"
className="text-slate-100"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={{ duration: 2.5, delay: delay + 0.2, ease: "easeInOut" }}
/>
{/* Nodes */}
<motion.circle cx="400" cy="100" r="4" className="fill-slate-200"
initial={{ scale: 0 }} animate={{ scale: 1 }} transition={{ delay: delay + 1, duration: 0.5 }} />
<motion.circle cx="400" cy="150" r="4" className="fill-slate-100"
initial={{ scale: 0 }} animate={{ scale: 1 }} transition={{ delay: delay + 1.2, duration: 0.5 }} />
</svg>
);
};
export const GridLines: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="currentColor" strokeWidth="0.5" className="text-slate-100" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
{/* Highlighted Path */}
<motion.path
d="M 40 40 L 120 40 L 120 120 L 200 120"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
className="text-slate-300"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay }}
/>
<motion.circle cx="200" cy="120" r="3" className="fill-slate-400"
initial={{ scale: 0 }} whileInView={{ scale: 1 }} viewport={{ once: true }} transition={{ delay: delay + 1.5 }} />
</svg>
);
};
export const FlowLines: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 600 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<motion.path
d="M 0 100 H 100 C 150 100, 150 50, 200 50 H 300"
stroke="currentColor"
strokeWidth="1"
className="text-slate-200"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay }}
/>
<motion.path
d="M 0 100 H 100 C 150 100, 150 150, 200 150 H 300"
stroke="currentColor"
strokeWidth="1"
className="text-slate-200"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay + 0.2 }}
/>
<motion.rect x="300" y="30" width="80" height="40" rx="8" className="stroke-slate-300 fill-white" strokeWidth="1"
initial={{ opacity: 0, x: 280 }} whileInView={{ opacity: 1, x: 300 }} viewport={{ once: true }} transition={{ delay: delay + 1 }} />
<motion.rect x="300" y="130" width="80" height="40" rx="8" className="stroke-slate-300 fill-white" strokeWidth="1"
initial={{ opacity: 0, x: 280 }} whileInView={{ opacity: 1, x: 300 }} viewport={{ once: true }} transition={{ delay: delay + 1.2 }} />
</svg>
);
};
export const CirclePattern: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<motion.circle cx="200" cy="200" r="100" stroke="currentColor" strokeWidth="1" className="text-slate-100"
initial={{ scale: 0.8, opacity: 0 }} whileInView={{ scale: 1, opacity: 1 }} viewport={{ once: true }} transition={{ duration: 1, delay: delay }} />
<motion.circle cx="200" cy="200" r="150" stroke="currentColor" strokeWidth="1" className="text-slate-50"
initial={{ scale: 0.8, opacity: 0 }} whileInView={{ scale: 1, opacity: 1 }} viewport={{ once: true }} transition={{ duration: 1, delay: delay + 0.2 }} />
<motion.circle cx="200" cy="200" r="50" stroke="currentColor" strokeWidth="1" className="text-slate-200"
initial={{ scale: 0.8, opacity: 0 }} whileInView={{ scale: 1, opacity: 1 }} viewport={{ once: true }} transition={{ duration: 1, delay: delay + 0.4 }} />
</svg>
)
}

View File

@@ -13,7 +13,12 @@ export const FeatureCard: React.FC<FeatureCardProps> = ({ icon: Icon, title, des
return (
<Reveal delay={delay}>
<div className="p-8 md:p-10 border border-slate-100 rounded-2xl hover:border-slate-200 transition-all duration-500 group bg-white hover:shadow-xl hover:shadow-slate-100/50 relative overflow-hidden">
<div className="absolute top-0 right-0 w-24 h-24 bg-slate-50 rounded-full -mr-12 -mt-12 group-hover:bg-slate-100 transition-colors duration-500"></div>
{/* Animated Top Line */}
<div className="absolute top-0 left-0 w-full h-1 bg-slate-900 scale-x-0 group-hover:scale-x-100 transition-transform duration-500 origin-left z-20"></div>
{/* Decorative Ring */}
<div className="absolute top-0 right-0 w-32 h-32 border border-slate-100 rounded-full -mr-16 -mt-16 group-hover:scale-150 transition-transform duration-700"></div>
<Icon className="w-8 h-8 mb-6 text-slate-300 group-hover:text-slate-900 group-hover:scale-110 group-hover:rotate-3 transition-all duration-500 relative z-10" />
<h3 className="text-xl md:text-2xl font-bold text-slate-900 mb-3 tracking-tight relative z-10">
{title}

View File

@@ -12,6 +12,9 @@ export const HeroItem: React.FC<HeroItemProps> = ({ number, title, description,
return (
<Reveal delay={delay}>
<div className="group flex gap-6 md:gap-8 p-6 md:p-8 border border-transparent hover:border-slate-100 rounded-2xl transition-all duration-500 hover:bg-slate-50/50 relative overflow-hidden">
{/* Animated Bottom Line */}
<div className="absolute bottom-0 left-8 right-8 h-px bg-slate-200 scale-x-0 group-hover:scale-x-100 transition-transform duration-700 origin-left"></div>
<div className="absolute inset-0 bg-gradient-to-r from-slate-50/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
<span className="text-xs font-bold uppercase tracking-[0.2em] text-slate-300 group-hover:text-slate-900 transition-colors pt-1.5 relative z-10">
{number}

View File

@@ -20,6 +20,10 @@ export const ServiceCard: React.FC<ServiceCardProps> = ({
return (
<Reveal delay={delay}>
<div className="group relative p-10 md:p-12 border border-slate-100 rounded-3xl hover:border-slate-900 transition-all duration-700 bg-white hover:shadow-2xl hover:shadow-slate-100/50 overflow-hidden">
{/* Decorative Corner */}
<div className="absolute top-0 right-0 w-32 h-32 border-t border-r border-slate-100 rounded-tr-3xl opacity-0 group-hover:opacity-100 transition-opacity duration-700"></div>
<div className="absolute top-8 right-8 w-2 h-2 bg-slate-900 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-700 delay-100"></div>
{/* Decorative background text */}
<div className="absolute -right-4 -bottom-4 text-8xl md:text-9xl font-bold text-slate-50 select-none group-hover:text-slate-100 transition-colors duration-700 -z-10">
{title.charAt(0)}

View File

@@ -2,3 +2,4 @@ export * from './HeroItem';
export * from './FeatureCard';
export * from './ComparisonRow';
export * from './ServiceCard';
export * from './AbstractLines';

View File

@@ -6,7 +6,6 @@ interface SectionProps {
title?: string;
children: React.ReactNode;
className?: string;
numberPosition?: 'left' | 'right';
delay?: number;
variant?: 'white' | 'gray';
borderTop?: boolean;
@@ -17,32 +16,44 @@ export const Section: React.FC<SectionProps> = ({
title,
children,
className = "",
numberPosition = 'left',
delay = 0,
variant = 'white',
borderTop = false,
}) => {
const bgClass = variant === 'gray' ? 'bg-slate-50/50' : 'bg-white';
const bgClass = variant === 'gray' ? 'bg-slate-50' : 'bg-white';
const borderClass = borderTop ? 'border-t border-slate-100' : '';
return (
<section className={`relative py-24 md:py-32 ${bgClass} ${borderClass} ${className}`}>
<div className="narrow-container relative">
{number && (
<div className={`absolute ${numberPosition === 'left' ? '-left-24' : '-right-24'} -top-24 text-[15rem] md:text-[20rem] font-bold text-slate-100/50 select-none -z-10 pointer-events-none`}>
{number}
</div>
)}
{title && (
<Reveal delay={delay}>
<div className="flex items-center gap-4 mb-12 md:mb-16">
<div className="h-px w-8 bg-slate-200"></div>
<h2 className="text-[10px] font-bold uppercase tracking-[0.4em] text-slate-400">{title}</h2>
<div className="narrow-container">
<div className="grid grid-cols-1 md:grid-cols-12 gap-12 md:gap-16">
{/* Sidebar: Number & Title */}
<div className="md:col-span-3 relative">
<div className="md:sticky md:top-32 space-y-6">
{number && (
<Reveal delay={delay}>
<span className="block text-6xl md:text-8xl font-bold text-slate-100 leading-none select-none">
{number}
</span>
</Reveal>
)}
{title && (
<Reveal delay={delay + 0.1}>
<div className="flex items-center gap-3">
<div className="h-px w-6 bg-slate-900"></div>
<h2 className="text-xs font-bold uppercase tracking-[0.3em] text-slate-900">
{title}
</h2>
</div>
</Reveal>
)}
</div>
</Reveal>
)}
<div className="relative z-10">
{children}
</div>
{/* Main Content */}
<div className="md:col-span-9">
{children}
</div>
</div>
</div>
</section>