feat: complete agency site build (Phases 1-7)

Full Next.js 16 + Payload CMS 3.x agency site with:
- Homepage: Hero, TrustBar, Services, Configurator wizard, Process,
  Selected Works, Philosophy, CTA Banner
- Sub-pages: /services (3 pillars + AI Layer), /work/[slug] (case
  studies), /about (philosophy + story)
- Configurator: 3-step wizard with AI brief generation API
- i18n: Full EN/FR bilingual with next-intl
- Design system: Cormorant Garamond + Inter, celestial blue palette,
  glassmorphism nav, Framer Motion animations
- Payload CMS collections: Projects, Services, Submissions, Media

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 20:37:38 +01:00
commit a1f9eca76c
64 changed files with 15810 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
'use client';
import { motion } from 'framer-motion';
import { useTranslations } from 'next-intl';
import { Compass, Shield, Brain, MapPin } from 'lucide-react';
import { cn } from '@/lib/utils';
import {
revealVariants,
staggerContainerWide,
viewportOnce,
} from '@/lib/animations';
import type { LucideIcon } from 'lucide-react';
// Each item's icon scale-bounce on enter
const iconBounceVariants = {
hidden: { scale: 0.7, opacity: 0 },
visible: {
scale: 1,
opacity: 1,
transition: {
duration: 0.5,
ease: [0.34, 1.56, 0.64, 1] as [number, number, number, number], // spring-like overshoot
},
},
};
interface TrustItem {
key: string;
Icon: LucideIcon;
}
const ITEMS: TrustItem[] = [
{ key: 'customBuilt', Icon: Compass },
{ key: 'privateInfra', Icon: Shield },
{ key: 'aiPowered', Icon: Brain },
{ key: 'rivieraBased', Icon: MapPin },
];
interface TrustCardProps {
item: TrustItem;
index: number;
t: (key: string) => string;
}
function TrustCard({ item, index, t }: TrustCardProps) {
const { Icon, key } = item;
return (
<motion.div
variants={revealVariants}
className={cn(
'group flex flex-col items-start gap-4 p-6',
'rounded-2xl bg-surface-high shadow-subtle',
'transition-shadow duration-300 hover:shadow-card',
'cursor-default',
)}
>
{/* Icon with scale-bounce on scroll reveal */}
<motion.div
variants={iconBounceVariants}
className={cn(
'flex items-center justify-center w-12 h-12 rounded-xl',
'bg-primary/8',
'transition-transform duration-300 ease-out',
'group-hover:-translate-y-1',
)}
aria-hidden="true"
>
<Icon
size={28}
className="text-primary transition-colors duration-300"
strokeWidth={1.75}
/>
</motion.div>
{/* Title */}
<div>
<h3
className={cn(
'font-semibold text-on-surface text-base leading-snug mb-1',
'transition-colors duration-300 group-hover:text-primary-dark',
)}
>
{t(`${key}.title`)}
</h3>
{/* Description */}
<p className="text-sm text-outline leading-relaxed">
{t(`${key}.description`)}
</p>
</div>
</motion.div>
);
}
export default function TrustBar() {
const t = useTranslations('trustBar');
return (
<section
aria-label="Trust indicators"
className="bg-surface-low py-16"
>
<div className="max-w-6xl mx-auto px-6">
{/* Stagger wrapper — triggers children revealVariants on scroll */}
<motion.div
variants={staggerContainerWide}
initial="hidden"
whileInView="visible"
viewport={viewportOnce}
className="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-8"
>
{ITEMS.map((item, index) => (
<TrustCard
key={item.key}
item={item}
index={index}
t={t}
/>
))}
</motion.div>
</div>
</section>
);
}