first commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { Code2, Compass, type LucideIcon } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type ServiceKey = 'outsourcing' | 'squads' | 'consulting';
|
||||
|
||||
const SERVICES: { key: ServiceKey; Icon: LucideIcon }[] = [
|
||||
{ key: 'outsourcing', Icon: Code2 },
|
||||
// { key: 'squads', Icon: Users },
|
||||
{ key: 'consulting', Icon: Compass },
|
||||
];
|
||||
|
||||
export function Services() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<section id="services" className="section bg-bg-muted">
|
||||
<div className="container-rl">
|
||||
<SectionHeader
|
||||
eyebrow={t('services.eyebrow')}
|
||||
title={t('services.title')}
|
||||
subtitle={t('services.subtitle')}
|
||||
/>
|
||||
|
||||
<div className="mt-14 grid gap-6 md:grid-cols-3">
|
||||
{SERVICES.map(({ key, Icon }, index) => (
|
||||
<motion.article
|
||||
key={key}
|
||||
initial={{ opacity: 0, y: 24 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: '-80px' }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: index * 0.08,
|
||||
ease: [0.4, 0, 0.2, 1],
|
||||
}}
|
||||
className="card card-hover group"
|
||||
>
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-secondary text-secondary-fg transition-transform group-hover:scale-110">
|
||||
<Icon className="h-6 w-6" aria-hidden="true" />
|
||||
</div>
|
||||
<h3 className="heading mt-5 text-xl">
|
||||
{t(`services.items.${key}.title`)}
|
||||
</h3>
|
||||
<p className="mt-3 text-fg-muted leading-relaxed">
|
||||
{t(`services.items.${key}.description`)}
|
||||
</p>
|
||||
</motion.article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export function SectionHeader({
|
||||
eyebrow,
|
||||
title,
|
||||
subtitle,
|
||||
align = 'center',
|
||||
}: {
|
||||
eyebrow?: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
align?: 'center' | 'left';
|
||||
}) {
|
||||
const alignClass =
|
||||
align === 'center' ? 'mx-auto text-center' : 'text-left';
|
||||
return (
|
||||
<div className={`max-w-2xl ${alignClass}`}>
|
||||
{eyebrow && (
|
||||
<span className="text-xs font-bold uppercase tracking-[0.18em] text-primary">
|
||||
{eyebrow}
|
||||
</span>
|
||||
)}
|
||||
<h2 className="heading text-balance mt-3 text-3xl md:text-4xl">{title}</h2>
|
||||
{subtitle && (
|
||||
<p className="mt-4 text-fg-muted text-balance md:text-lg">{subtitle}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user