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:
420
src/app/(frontend)/[locale]/about/page.tsx
Normal file
420
src/app/(frontend)/[locale]/about/page.tsx
Normal file
@@ -0,0 +1,420 @@
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import { Shield, PenTool, Users } from 'lucide-react';
|
||||
import ScrollReveal from '@/components/ui/ScrollReveal';
|
||||
import Button from '@/components/ui/Button';
|
||||
import CornerBracket from '@/components/icons/CornerBracket';
|
||||
import { routing } from '@/i18n/routing';
|
||||
|
||||
// ─── Static Generation ────────────────────────────────────────────────────────
|
||||
|
||||
export function generateStaticParams() {
|
||||
return routing.locales.map((locale) => ({ locale }));
|
||||
}
|
||||
|
||||
// ─── Data ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
const PILLARS = [
|
||||
{
|
||||
id: 'ownership',
|
||||
Icon: Shield,
|
||||
title: 'Ownership & Privacy',
|
||||
description:
|
||||
'We build on infrastructure you control. No vendor lock-in, no opaque SaaS dependencies quietly holding your data hostage. Your platform, your servers, your rules — backed by engineering that makes it maintainable.',
|
||||
},
|
||||
{
|
||||
id: 'craftsmanship',
|
||||
Icon: PenTool,
|
||||
title: 'Craftsmanship',
|
||||
description:
|
||||
'The gap between a website that works and one that endures is craft. We sweat the typography, the transitions, the query performance, the edge cases. Every interface we ship is something we would be proud to sign.',
|
||||
},
|
||||
{
|
||||
id: 'one-team',
|
||||
Icon: Users,
|
||||
title: 'One Team, Everything',
|
||||
description:
|
||||
'Strategy, design, engineering, infrastructure — under one roof, one point of contact, one shared standard of quality. No handoffs between agencies. No telephone-game briefs. Just people who care about the whole thing.',
|
||||
},
|
||||
];
|
||||
|
||||
// ─── Sub-components ───────────────────────────────────────────────────────────
|
||||
|
||||
function StoryGeometry() {
|
||||
return (
|
||||
<div
|
||||
className="absolute inset-0 overflow-hidden rounded-xl"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{/* Primary gradient circle — top right */}
|
||||
<div
|
||||
className="absolute rounded-full"
|
||||
style={{
|
||||
width: '70%',
|
||||
height: '70%',
|
||||
top: '-15%',
|
||||
right: '-15%',
|
||||
background:
|
||||
'radial-gradient(circle, rgba(91,164,217,0.10) 0%, rgba(0,100,148,0.05) 55%, transparent 100%)',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Secondary circle — bottom left */}
|
||||
<div
|
||||
className="absolute rounded-full"
|
||||
style={{
|
||||
width: '55%',
|
||||
height: '55%',
|
||||
bottom: '-10%',
|
||||
left: '-8%',
|
||||
background:
|
||||
'radial-gradient(circle, rgba(46,196,160,0.07) 0%, transparent 70%)',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Diagonal grid texture */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.025]"
|
||||
style={{
|
||||
backgroundImage:
|
||||
'repeating-linear-gradient(-45deg, var(--color-primary-dark) 0, var(--color-primary-dark) 1px, transparent 0, transparent 50%)',
|
||||
backgroundSize: '28px 28px',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Tilted rectangle — left center */}
|
||||
<div
|
||||
className="absolute rounded-lg"
|
||||
style={{
|
||||
width: '24%',
|
||||
height: '34%',
|
||||
top: '18%',
|
||||
left: '6%',
|
||||
border: '1.5px solid rgba(91,164,217,0.12)',
|
||||
transform: 'rotate(-6deg)',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Dot field — right center */}
|
||||
<div
|
||||
className="absolute"
|
||||
style={{
|
||||
width: '28%',
|
||||
height: '28%',
|
||||
top: '30%',
|
||||
right: '8%',
|
||||
backgroundImage:
|
||||
'radial-gradient(circle, rgba(91,164,217,0.22) 1.5px, transparent 1.5px)',
|
||||
backgroundSize: '10px 10px',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Dashed arc */}
|
||||
<svg
|
||||
className="absolute"
|
||||
style={{ top: '15%', left: '28%', opacity: 0.07 }}
|
||||
width="160"
|
||||
height="160"
|
||||
viewBox="0 0 160 160"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="80"
|
||||
cy="80"
|
||||
r="68"
|
||||
stroke="var(--color-primary-dark)"
|
||||
strokeWidth="1"
|
||||
strokeDasharray="7 5"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{/* Small accent square */}
|
||||
<div
|
||||
className="absolute rounded-sm"
|
||||
style={{
|
||||
width: '5%',
|
||||
height: '5%',
|
||||
bottom: '26%',
|
||||
right: '26%',
|
||||
background: 'rgba(91,164,217,0.18)',
|
||||
transform: 'rotate(14deg)',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Teal accent line — bottom */}
|
||||
<div
|
||||
className="absolute"
|
||||
style={{
|
||||
width: '30%',
|
||||
height: '2px',
|
||||
bottom: '14%',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
background:
|
||||
'linear-gradient(to right, transparent, rgba(46,196,160,0.35), transparent)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PillarCard({
|
||||
Icon,
|
||||
title,
|
||||
description,
|
||||
}: {
|
||||
Icon: React.ElementType;
|
||||
title: string;
|
||||
description: string;
|
||||
}) {
|
||||
return (
|
||||
<ScrollReveal variant="fadeUp">
|
||||
<div className="bg-surface-high rounded-xl p-8 flex flex-col gap-5 h-full shadow-subtle">
|
||||
{/* Icon */}
|
||||
<div
|
||||
className="w-11 h-11 rounded-xl flex items-center justify-center flex-shrink-0"
|
||||
style={{ background: 'rgba(91,164,217,0.10)' }}
|
||||
>
|
||||
<Icon
|
||||
size={20}
|
||||
strokeWidth={1.75}
|
||||
className="text-primary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Text */}
|
||||
<div className="flex flex-col gap-2.5">
|
||||
<h3 className="font-serif text-xl font-semibold text-on-surface leading-snug tracking-[-0.02em]">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-outline text-sm leading-relaxed">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Page ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ locale: string }>;
|
||||
};
|
||||
|
||||
export default async function AboutPage({ params }: Props) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
|
||||
return (
|
||||
<main>
|
||||
|
||||
{/* ── 1. Hero ── */}
|
||||
<section className="bg-surface py-32">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="max-w-4xl mx-auto flex flex-col items-center text-center gap-8">
|
||||
|
||||
<ScrollReveal variant="fadeIn">
|
||||
<span className="label-md text-primary">About LetsBe.</span>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal variant="fadeUp" delay={0.1}>
|
||||
<h1 className="font-serif font-semibold text-on-surface text-5xl md:text-6xl lg:text-[4rem] leading-[1.05] tracking-[-0.03em]">
|
||||
Digital Sovereignty
|
||||
<br />
|
||||
is not a luxury.
|
||||
</h1>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal variant="fadeUp" delay={0.2}>
|
||||
<p className="text-outline text-xl leading-relaxed max-w-2xl">
|
||||
We build digital platforms for businesses that refuse to compromise — on ownership,
|
||||
on quality, or on the partner they trust to build it with them.
|
||||
</p>
|
||||
</ScrollReveal>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── 2. Story ── */}
|
||||
<section className="bg-surface-low py-24">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 items-start">
|
||||
|
||||
{/* Left: Text */}
|
||||
<div className="lg:col-span-5 flex flex-col gap-10">
|
||||
|
||||
<ScrollReveal variant="slideLeft" className="flex flex-col gap-4">
|
||||
<span className="label-md text-primary">Our Story</span>
|
||||
<h2 className="font-serif text-4xl md:text-[2.75rem] font-semibold text-on-surface leading-[1.1] tracking-[-0.02em]">
|
||||
Born on the<br />
|
||||
Côte d’Azur.
|
||||
</h2>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal variant="fadeUp" delay={0.1} className="flex flex-col gap-5 text-outline leading-relaxed text-[0.9375rem]">
|
||||
<p>
|
||||
LetsBe. was founded on the French Riviera by a small team of engineers and
|
||||
designers who shared a single conviction: that ambitious businesses deserve digital
|
||||
infrastructure as carefully considered as the work they do.
|
||||
</p>
|
||||
<p>
|
||||
We started by building for founders and institutions along the coast — port
|
||||
authorities, conservation organisations, maritime operators — each one operating
|
||||
in a context where reliability, elegance, and discretion were not optional extras.
|
||||
Those early projects shaped everything we believe about what a digital partner
|
||||
should be.
|
||||
</p>
|
||||
<p>
|
||||
Today we work with clients across Europe and the Mediterranean on platforms that
|
||||
are built to be owned, not rented. We do not believe in locking clients into
|
||||
systems they cannot see, services they cannot leave, or vendors whose priorities
|
||||
will drift from theirs. We build on open infrastructure, we document everything,
|
||||
and we hand over codebases that outlast the engagement.
|
||||
</p>
|
||||
</ScrollReveal>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Right: Decorative canvas */}
|
||||
<div className="lg:col-span-7 relative">
|
||||
<ScrollReveal variant="slideRight">
|
||||
<div
|
||||
className="relative bg-surface rounded-xl overflow-hidden"
|
||||
style={{ minHeight: '460px' }}
|
||||
>
|
||||
<StoryGeometry />
|
||||
|
||||
{/* Inset rim */}
|
||||
<div
|
||||
className="absolute inset-0 rounded-xl pointer-events-none"
|
||||
style={{
|
||||
boxShadow: 'inset 0 0 0 1px rgba(194,199,206,0.22)',
|
||||
}}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* Floating pull quote */}
|
||||
<ScrollReveal
|
||||
variant="fadeUp"
|
||||
delay={0.4}
|
||||
className="absolute -bottom-8 -left-4 lg:-left-10 z-10 max-w-[320px] w-[calc(100%-2.5rem)] lg:max-w-[340px]"
|
||||
>
|
||||
<div className="bg-surface-high rounded-xl p-6 shadow-subtle relative">
|
||||
<div className="absolute top-4 right-4">
|
||||
<CornerBracket
|
||||
size={28}
|
||||
position="top-right"
|
||||
color="var(--color-primary)"
|
||||
/>
|
||||
</div>
|
||||
<blockquote className="font-serif italic text-lg text-on-surface leading-relaxed pr-8">
|
||||
“Build fewer things. Build them better. Build them to last.”
|
||||
</blockquote>
|
||||
<div
|
||||
className="w-8 h-px bg-primary/40 my-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p className="label-md text-outline">LetsBe. founding principle</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── 3. Three Pillars ── */}
|
||||
<section className="bg-surface py-24 mt-8">
|
||||
<div className="container mx-auto px-6">
|
||||
|
||||
<ScrollReveal variant="fadeUp" className="flex flex-col items-center text-center gap-4 mb-16">
|
||||
<span className="label-md text-primary">Our Beliefs</span>
|
||||
<h2 className="font-serif font-semibold text-on-surface text-4xl md:text-5xl leading-[1.1] tracking-[-0.02em] max-w-2xl">
|
||||
What We Believe
|
||||
</h2>
|
||||
<p className="text-outline text-lg leading-relaxed max-w-xl mt-1">
|
||||
Three principles that inform every decision we make, every line of code we write, and
|
||||
every client relationship we enter.
|
||||
</p>
|
||||
</ScrollReveal>
|
||||
|
||||
<ScrollReveal variant="fadeUp" stagger className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{PILLARS.map(({ id, Icon, title, description }) => (
|
||||
<PillarCard key={id} Icon={Icon} title={title} description={description} />
|
||||
))}
|
||||
</ScrollReveal>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── 4. Quote ── */}
|
||||
<section
|
||||
className="py-20"
|
||||
style={{ backgroundColor: 'var(--color-navy)' }}
|
||||
>
|
||||
<div className="container mx-auto px-6">
|
||||
<ScrollReveal variant="fadeUp">
|
||||
<div className="max-w-3xl mx-auto flex flex-col items-center text-center gap-6">
|
||||
|
||||
{/* Decorative line */}
|
||||
<div
|
||||
className="w-10 h-px"
|
||||
style={{ background: 'rgba(91,164,217,0.5)' }}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
<blockquote className="font-serif italic text-white text-3xl md:text-[2.25rem] leading-[1.3] tracking-[-0.02em]">
|
||||
“Our mission is to bring the precision of architecture to the fluidity of the
|
||||
web.”
|
||||
</blockquote>
|
||||
|
||||
<p className="label-md text-white/40">
|
||||
— LetsBe. founding philosophy
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── 5. CTA ── */}
|
||||
<section className="bg-surface py-16">
|
||||
<div className="container mx-auto px-6">
|
||||
<ScrollReveal variant="fadeUp">
|
||||
<div className="max-w-3xl mx-auto flex flex-col items-center text-center gap-8">
|
||||
|
||||
<div className="flex flex-col gap-3 items-center">
|
||||
<span className="label-md text-primary">Work With Us</span>
|
||||
<h2 className="font-serif font-semibold text-on-surface text-3xl md:text-4xl leading-[1.1] tracking-[-0.02em]">
|
||||
Let’s build something together.
|
||||
</h2>
|
||||
<p className="text-outline text-lg leading-relaxed max-w-lg">
|
||||
Whether you have a clear brief or an early-stage idea, we would be glad to talk
|
||||
through what is possible.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center gap-4">
|
||||
<Button variant="primary" size="lg" arrow href="/#configure">
|
||||
Start your project
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
href="mailto:hello@letsbe.biz"
|
||||
>
|
||||
Book a call
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user