first commit
ci / build (push) Failing after 23s
deploy / deploy (push) Failing after 10m12s

This commit is contained in:
soroush.asadi
2026-05-31 12:47:02 +03:30
commit add78d8460
100 changed files with 15221 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
'use client';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { useLocale } from '@/lib/i18n/locale-context';
import { SectionHeader } from '@/components/ui/SectionHeader';
import { Counter } from '@/components/ui/Counter';
import { cn } from '@/lib/utils';
const FA_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] as const;
const toFa = (s: string) =>
s.replace(/\d/g, (d) => FA_DIGITS[Number(d)]);
export function Expertise() {
const { t, locale } = useLocale();
const barsRef = useRef<HTMLDivElement>(null);
const inView = useInView(barsRef, { once: true, margin: '-80px' });
return (
<section id="expertise" className="relative px-5 py-28 sm:px-8">
<div className="mx-auto max-w-7xl">
<SectionHeader
eyebrow={t.expertise.eyebrow}
title={t.expertise.title}
sub={t.expertise.sub}
/>
<div className="mt-14 grid grid-cols-1 gap-10 lg:grid-cols-2">
{/* Metric tiles */}
<div className="grid grid-cols-2 gap-4 self-start">
{t.hero.metrics.map((m, i) => (
<motion.div
key={m.label}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }}
transition={{
duration: 0.5,
ease: [0.22, 1, 0.36, 1],
delay: 0.05 * i,
}}
className="glass relative overflow-hidden p-6"
>
<span
aria-hidden
className={cn(
'absolute inset-x-0 top-0 h-px',
'bg-gradient-to-r from-transparent via-electric/60 to-transparent',
)}
/>
<div
className={cn(
'font-display text-[clamp(1.8rem,3.5vw,2.6rem)] font-bold leading-none',
['text-electric', 'text-violet', 'text-magenta', 'text-emerald'][i % 4],
)}
>
<Counter value={m.value} locale={locale} />
</div>
<div className="mt-3 text-sm leading-snug text-slate-400">
{m.label}
</div>
</motion.div>
))}
</div>
{/* Skill bars */}
<div ref={barsRef} className="glass relative p-7 sm:p-8">
<span
aria-hidden
className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-magenta/60 to-transparent"
/>
<ul className="flex flex-col gap-6">
{t.expertise.bars.map((b, i) => (
<li key={b.label}>
<div className="mb-2 flex items-baseline justify-between text-sm">
<span className="text-slate-200">{b.label}</span>
<span className="font-mono text-xs text-slate-400">
{locale === 'fa' ? toFa(b.value.toString()) + '٪' : `${b.value}%`}
</span>
</div>
<div className="relative h-1.5 overflow-hidden rounded-full bg-white/[0.05]">
<motion.div
initial={{ width: 0 }}
animate={inView ? { width: `${b.value}%` } : { width: 0 }}
transition={{
duration: 1.2,
ease: [0.22, 1, 0.36, 1],
delay: 0.08 * i,
}}
className="absolute inset-y-0 start-0 rounded-full bg-brand-gradient"
style={{ backgroundSize: '200% 200%' }}
/>
</div>
</li>
))}
</ul>
</div>
</div>
</div>
</section>
);
}