83 lines
2.5 KiB
TypeScript
83 lines
2.5 KiB
TypeScript
|
|
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>
|
||
|
|
);
|
||
|
|
}
|