polish: deep visual pass across all homepage sections
All checks were successful
Build & Push / build-and-push (push) Successful in 1m26s
All checks were successful
Build & Push / build-and-push (push) Successful in 1m26s
Hero: - Removed fake trust proof avatars - Added decorative gradient separator line - Pushed content down for better vertical rhythm - More visible secondary CTA button TrustBar: - Icon circles with primary tint backdrops - Gradient top accent bar on hover - Gradient fade transition from hero section - Increased card padding Services: - Subtle grid texture background - Larger primary-colored feature dots - Enhanced AI narrative with horizontal decorative rules - Hover shadow elevation on pillars Configurator: - Step numbers in tinted circles with vertical rail - ShieldCheck trust icon - Radial gradient glow on wizard panel - Sparkles icon on AI toggle - Empty-state hint message - Service card icons in circular primary backdrops Process: - Gradient top accent strips on cards - Icon containers with primary tint - Vertical accent bar on section heading - Dashed SVG connector lines between cards SelectedWorks: - Distinct geometric compositions per card (Monaco, Nimara, Amador) - Bottom gradient fade on placeholder images - Dot separators between tags - Animated background pulse on coming-soon cards - Enhanced CTA hover animations Philosophy: - Enriched decorative panel (circle rings, diagonal lines, navy gradient) - Primary-colored pillar numbers - Left border accent on pull-quote card CTABanner: - Grid pattern + circle ring overlays - Email link underline-on-hover animation Footer: - Full logo image replacing text wordmark - Larger social icons - Gradient divider line - Larger nav logo (h-14 desktop, h-11 mobile) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -100,10 +100,10 @@ function ServiceCard({ option, selected, onToggle }: ServiceCardProps) {
|
|||||||
{/* Icon */}
|
{/* Icon */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex-shrink-0 w-10 h-10 rounded-xl flex items-center justify-center transition-colors duration-200',
|
'flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center transition-colors duration-200',
|
||||||
selected
|
selected
|
||||||
? 'bg-primary/15 text-primary-dark'
|
? 'bg-primary/15 text-primary-dark'
|
||||||
: 'bg-surface-low text-outline group-hover:bg-primary/10 group-hover:text-primary',
|
: 'bg-primary/8 text-primary group-hover:bg-primary/15 group-hover:text-primary-dark',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Icon size={20} strokeWidth={1.5} />
|
<Icon size={20} strokeWidth={1.5} />
|
||||||
@@ -176,21 +176,22 @@ function AIToggle({ enabled, onToggle }: AIToggleProps) {
|
|||||||
: 'bg-surface-high shadow-subtle hover:shadow-card',
|
: 'bg-surface-high shadow-subtle hover:shadow-card',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Sparkles
|
|
||||||
size={16}
|
|
||||||
strokeWidth={1.5}
|
|
||||||
className={cn(
|
|
||||||
'flex-shrink-0 transition-colors duration-200',
|
|
||||||
enabled ? 'text-primary' : 'text-outline',
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-sm font-medium transition-colors duration-200',
|
'text-sm font-semibold transition-colors duration-200 flex items-center gap-1.5',
|
||||||
enabled ? 'text-primary-dark' : 'text-on-surface',
|
enabled ? 'text-primary-dark' : 'text-on-surface',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
<Sparkles
|
||||||
|
size={14}
|
||||||
|
strokeWidth={1.75}
|
||||||
|
className={cn(
|
||||||
|
'flex-shrink-0 transition-colors duration-200',
|
||||||
|
enabled ? 'text-primary' : 'text-outline',
|
||||||
|
)}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
Enhance with AI
|
Enhance with AI
|
||||||
</span>
|
</span>
|
||||||
<p className="text-xs text-outline mt-0.5">
|
<p className="text-xs text-outline mt-0.5">
|
||||||
@@ -270,6 +271,20 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
|
|||||||
onToggle={() => toggleService(option.id)}
|
onToggle={() => toggleService(option.id)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
{/* Empty-state hint */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{formData.services.length === 0 && (
|
||||||
|
<motion.p
|
||||||
|
initial={{ opacity: 0, y: 4 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, y: -4 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="text-xs text-outline/60 text-center pt-1 pb-0.5 select-none"
|
||||||
|
>
|
||||||
|
Select at least one service to continue
|
||||||
|
</motion.p>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* AI Toggle */}
|
{/* AI Toggle */}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useTranslations } from 'next-intl'
|
import { useTranslations } from 'next-intl'
|
||||||
|
import Image from 'next/image'
|
||||||
import { Link } from '@/i18n/navigation'
|
import { Link } from '@/i18n/navigation'
|
||||||
import CalButton from '@/components/ui/CalButton'
|
import CalButton from '@/components/ui/CalButton'
|
||||||
|
|
||||||
@@ -111,12 +112,13 @@ export default function Footer() {
|
|||||||
|
|
||||||
{/* Col 1 — Brand */}
|
{/* Col 1 — Brand */}
|
||||||
<div className="sm:col-span-2 lg:col-span-1 flex flex-col gap-5">
|
<div className="sm:col-span-2 lg:col-span-1 flex flex-col gap-5">
|
||||||
<p
|
<Image
|
||||||
className="font-serif text-3xl text-on-surface tracking-tight leading-none"
|
src="/images/LogoLetsBe_Biz_celesBlue.png"
|
||||||
aria-label="LetsBe."
|
alt="LetsBe."
|
||||||
>
|
width={180}
|
||||||
LetsBe.
|
height={60}
|
||||||
</p>
|
className="h-12 w-auto object-contain"
|
||||||
|
/>
|
||||||
<p className="text-sm text-on-surface/55 leading-relaxed max-w-[260px]">
|
<p className="text-sm text-on-surface/55 leading-relaxed max-w-[260px]">
|
||||||
{t('tagline')}
|
{t('tagline')}
|
||||||
</p>
|
</p>
|
||||||
@@ -175,7 +177,7 @@ export default function Footer() {
|
|||||||
className="inline-flex items-center justify-center w-9 h-9 rounded-full text-on-surface/40 hover:text-primary transition-all duration-200 hover:-translate-y-0.5"
|
className="inline-flex items-center justify-center w-9 h-9 rounded-full text-on-surface/40 hover:text-primary transition-all duration-200 hover:-translate-y-0.5"
|
||||||
style={{ willChange: 'transform' }}
|
style={{ willChange: 'transform' }}
|
||||||
>
|
>
|
||||||
<Icon className="w-[18px] h-[18px]" />
|
<Icon className="w-5 h-5" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@@ -192,10 +194,13 @@ export default function Footer() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tonal divider */}
|
{/* Gradient separator line */}
|
||||||
<div
|
<div
|
||||||
className="mx-6 lg:mx-8 h-px"
|
className="mx-6 lg:mx-8 h-px"
|
||||||
style={{ backgroundColor: 'rgba(25,28,29,0.07)' }}
|
style={{
|
||||||
|
background:
|
||||||
|
'linear-gradient(90deg, transparent 0%, rgba(25,28,29,0.12) 25%, rgba(25,28,29,0.16) 50%, rgba(25,28,29,0.12) 75%, transparent 100%)',
|
||||||
|
}}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -161,9 +161,9 @@ export default function Nav() {
|
|||||||
<Image
|
<Image
|
||||||
src="/images/letsbe-logo-short.png"
|
src="/images/letsbe-logo-short.png"
|
||||||
alt="LetsBe."
|
alt="LetsBe."
|
||||||
width={132}
|
width={168}
|
||||||
height={44}
|
height={56}
|
||||||
className="h-11 w-auto object-contain"
|
className="h-14 w-auto object-contain"
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -256,9 +256,9 @@ export default function Nav() {
|
|||||||
<Image
|
<Image
|
||||||
src="/images/letsbe-logo-short.png"
|
src="/images/letsbe-logo-short.png"
|
||||||
alt="LetsBe."
|
alt="LetsBe."
|
||||||
width={100}
|
width={132}
|
||||||
height={34}
|
height={44}
|
||||||
className="h-8 w-auto object-contain"
|
className="h-11 w-auto object-contain"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -109,6 +109,50 @@ function DecorativeBand() {
|
|||||||
strokeDasharray="4 10"
|
strokeDasharray="4 10"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
{/* Fine geometric line pattern — white at 3-4% opacity */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0"
|
||||||
|
style={{
|
||||||
|
backgroundImage:
|
||||||
|
'linear-gradient(rgba(255,255,255,0.035) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.035) 1px, transparent 1px)',
|
||||||
|
backgroundSize: '48px 48px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Diagonal accent lines crossing the grid */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 opacity-[0.025]"
|
||||||
|
style={{
|
||||||
|
backgroundImage:
|
||||||
|
'repeating-linear-gradient(-45deg, #fff 0, #fff 1px, transparent 0, transparent 50%)',
|
||||||
|
backgroundSize: '40px 40px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Circle ring — large, right side */}
|
||||||
|
<div
|
||||||
|
className="absolute rounded-full"
|
||||||
|
style={{
|
||||||
|
width: '400px',
|
||||||
|
height: '400px',
|
||||||
|
top: '-100px',
|
||||||
|
right: '-80px',
|
||||||
|
border: '1px solid rgba(255,255,255,0.04)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Circle ring — smaller, left side */}
|
||||||
|
<div
|
||||||
|
className="absolute rounded-full"
|
||||||
|
style={{
|
||||||
|
width: '260px',
|
||||||
|
height: '260px',
|
||||||
|
bottom: '-60px',
|
||||||
|
left: '-40px',
|
||||||
|
border: '1px solid rgba(255,255,255,0.04)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -198,6 +242,7 @@ export default function CTABanner() {
|
|||||||
'transition-all duration-200',
|
'transition-all duration-200',
|
||||||
'hover:text-white hover:bg-white/8',
|
'hover:text-white hover:bg-white/8',
|
||||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-[#006494]',
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-[#006494]',
|
||||||
|
'group/email',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Mail
|
<Mail
|
||||||
@@ -206,7 +251,9 @@ export default function CTABanner() {
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
/>
|
/>
|
||||||
hello@letsbe.biz
|
<span className="relative after:absolute after:bottom-0 after:left-0 after:h-px after:w-full after:bg-white/70 after:origin-left after:scale-x-0 after:transition-transform after:duration-200 group-hover/email:after:scale-x-100">
|
||||||
|
hello@letsbe.biz
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
import { ShieldCheck } from 'lucide-react';
|
||||||
import { revealVariants, staggerContainer, viewportOnce } from '@/lib/animations';
|
import { revealVariants, staggerContainer, viewportOnce } from '@/lib/animations';
|
||||||
import WizardContainer from '@/components/configurator/WizardContainer';
|
import WizardContainer from '@/components/configurator/WizardContainer';
|
||||||
|
|
||||||
@@ -15,8 +16,8 @@ interface StepDotProps {
|
|||||||
function StepDot({ index, label }: StepDotProps) {
|
function StepDot({ index, label }: StepDotProps) {
|
||||||
return (
|
return (
|
||||||
<motion.div variants={revealVariants} className="flex items-center gap-3">
|
<motion.div variants={revealVariants} className="flex items-center gap-3">
|
||||||
<div className="w-5 h-5 rounded-full border border-outline-variant/60 bg-surface-high flex items-center justify-center flex-shrink-0">
|
<div className="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0">
|
||||||
<span className="text-[10px] font-semibold text-outline">{index}</span>
|
<span className="text-xs font-semibold text-primary-dark leading-none">{index}</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm text-outline">{label}</span>
|
<span className="text-sm text-outline">{label}</span>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -81,21 +82,24 @@ export default function Configurator() {
|
|||||||
<p className="text-xs font-semibold uppercase tracking-label text-outline/70">
|
<p className="text-xs font-semibold uppercase tracking-label text-outline/70">
|
||||||
How it works
|
How it works
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col gap-2.5">
|
{/* Vertical accent line + steps */}
|
||||||
{steps.map((step, i) => (
|
<div className="flex gap-4">
|
||||||
<StepDot key={i} index={i + 1} label={step} />
|
<div className="flex-shrink-0 w-px bg-primary/20 ml-3 rounded-full" aria-hidden="true" />
|
||||||
))}
|
<div className="flex flex-col gap-2.5 flex-1">
|
||||||
|
{steps.map((step, i) => (
|
||||||
|
<StepDot key={i} index={i + 1} label={step} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Trust signal */}
|
{/* Trust signal */}
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={revealVariants}
|
variants={revealVariants}
|
||||||
className="pt-2 flex items-center gap-2.5"
|
className="pt-1 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<div className="h-px flex-1 bg-outline-variant/40" />
|
<ShieldCheck size={14} strokeWidth={1.75} className="text-primary flex-shrink-0" aria-hidden="true" />
|
||||||
<p className="text-xs text-outline">No commitment required</p>
|
<p className="text-xs text-outline">No commitment required</p>
|
||||||
<div className="h-px flex-1 bg-outline-variant/40" />
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
@@ -103,7 +107,15 @@ export default function Configurator() {
|
|||||||
|
|
||||||
{/* ── Right: Wizard ───────────────────────────────────────────── */}
|
{/* ── Right: Wizard ───────────────────────────────────────────── */}
|
||||||
<div className="lg:col-span-7">
|
<div className="lg:col-span-7">
|
||||||
<div className="rounded-2xl bg-surface-high shadow-subtle p-6 sm:p-8">
|
<div className="relative rounded-2xl bg-surface-high shadow-subtle p-6 sm:p-8 overflow-hidden">
|
||||||
|
{/* Radial gradient glow — top-left warmth */}
|
||||||
|
<div
|
||||||
|
className="pointer-events-none absolute -top-16 -left-16 w-72 h-72 rounded-full"
|
||||||
|
style={{
|
||||||
|
background: 'radial-gradient(circle, rgba(91,164,217,0.07) 0%, transparent 70%)',
|
||||||
|
}}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
<WizardContainer />
|
<WizardContainer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { viewportOnce } from '@/lib/animations';
|
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
import HeroGeometric from '@/components/icons/HeroGeometric';
|
import HeroGeometric from '@/components/icons/HeroGeometric';
|
||||||
|
|
||||||
@@ -100,40 +100,20 @@ const ctaVariant = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Trust strip slide in from left
|
// Decorative separator line fade-in — after subtitle
|
||||||
const trustVariant = {
|
const separatorVariant = {
|
||||||
hidden: { opacity: 0, x: -32 },
|
hidden: { opacity: 0, scaleX: 0 },
|
||||||
visible: {
|
visible: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
x: 0,
|
scaleX: 1,
|
||||||
transition: {
|
transition: {
|
||||||
duration: 0.55,
|
duration: 0.6,
|
||||||
delay: 0.9,
|
delay: 0.65,
|
||||||
ease: [0.16, 1, 0.3, 1] as [number, number, number, number],
|
ease: [0.16, 1, 0.3, 1] as [number, number, number, number],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Gradient stops for each avatar circle — subtle variations on the primary palette
|
|
||||||
const AVATAR_GRADIENTS = [
|
|
||||||
'linear-gradient(135deg, rgba(0,100,148,0.35), rgba(91,164,217,0.45))',
|
|
||||||
'linear-gradient(135deg, rgba(91,164,217,0.40), rgba(0,100,148,0.30))',
|
|
||||||
'linear-gradient(135deg, rgba(0,100,148,0.28), rgba(91,164,217,0.38))',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
function AvatarCircle({ index }: { index: number }) {
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className={cn(
|
|
||||||
'inline-block w-8 h-8 rounded-full ring-2 ring-white',
|
|
||||||
index > 0 && '-ml-2',
|
|
||||||
)}
|
|
||||||
style={{ background: AVATAR_GRADIENTS[index] }}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Hero() {
|
export default function Hero() {
|
||||||
const t = useTranslations('hero');
|
const t = useTranslations('hero');
|
||||||
|
|
||||||
@@ -187,7 +167,7 @@ export default function Hero() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* ─── Content ──────────────────────────────────────────────────── */}
|
{/* ─── Content ──────────────────────────────────────────────────── */}
|
||||||
<div className="relative z-10 w-full max-w-4xl mx-auto px-6 py-24 flex flex-col items-center text-center">
|
<div className="relative z-10 w-full max-w-4xl mx-auto px-6 pt-32 md:pt-40 pb-24 flex flex-col items-center text-center">
|
||||||
|
|
||||||
{/* Eyebrow */}
|
{/* Eyebrow */}
|
||||||
<motion.span
|
<motion.span
|
||||||
@@ -238,7 +218,7 @@ export default function Hero() {
|
|||||||
|
|
||||||
{/* Subtitle */}
|
{/* Subtitle */}
|
||||||
<motion.p
|
<motion.p
|
||||||
className="text-lg text-outline leading-relaxed max-w-xl mb-10"
|
className="text-lg text-outline leading-relaxed max-w-xl mb-8"
|
||||||
variants={subtitleVariant}
|
variants={subtitleVariant}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
animate="visible"
|
animate="visible"
|
||||||
@@ -246,9 +226,22 @@ export default function Hero() {
|
|||||||
{t('subtitle')}
|
{t('subtitle')}
|
||||||
</motion.p>
|
</motion.p>
|
||||||
|
|
||||||
|
{/* Decorative gradient separator */}
|
||||||
|
<motion.span
|
||||||
|
className="block w-20 h-px mb-10 origin-center"
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
'linear-gradient(to right, rgba(0,100,148,0.3), rgba(91,164,217,0.3))',
|
||||||
|
}}
|
||||||
|
variants={separatorVariant}
|
||||||
|
initial="hidden"
|
||||||
|
animate="visible"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* CTA row */}
|
{/* CTA row */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className="flex flex-col sm:flex-row items-center gap-4 mb-12"
|
className="flex flex-col sm:flex-row items-center gap-4"
|
||||||
variants={ctaVariant}
|
variants={ctaVariant}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
animate="visible"
|
animate="visible"
|
||||||
@@ -256,28 +249,15 @@ export default function Hero() {
|
|||||||
<Button variant="primary" size="lg" arrow href="#configure">
|
<Button variant="primary" size="lg" arrow href="#configure">
|
||||||
{t('cta')}
|
{t('cta')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="secondary" size="lg" href="#work">
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="lg"
|
||||||
|
href="#work"
|
||||||
|
className="bg-on-surface/5 hover:bg-on-surface/10"
|
||||||
|
>
|
||||||
{t('ctaSecondary')}
|
{t('ctaSecondary')}
|
||||||
</Button>
|
</Button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Trust proof strip */}
|
|
||||||
<motion.div
|
|
||||||
className="flex items-center gap-3"
|
|
||||||
variants={trustVariant}
|
|
||||||
initial="hidden"
|
|
||||||
animate="visible"
|
|
||||||
>
|
|
||||||
{/* Overlapping avatar circles */}
|
|
||||||
<div className="flex items-center" aria-hidden="true">
|
|
||||||
<AvatarCircle index={0} />
|
|
||||||
<AvatarCircle index={1} />
|
|
||||||
<AvatarCircle index={2} />
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-outline">
|
|
||||||
{t('trust')}
|
|
||||||
</p>
|
|
||||||
</motion.div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom fade-out to next section */}
|
{/* Bottom fade-out to next section */}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ function PhilosophyPillar({
|
|||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="flex flex-col gap-1.5 py-4">
|
<div className="flex flex-col gap-1.5 py-4">
|
||||||
<p
|
<p
|
||||||
className="label-md text-outline/60 mb-1"
|
className="font-serif text-sm font-semibold text-primary mb-1"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
{String(index + 1).padStart(2, '0')}
|
{String(index + 1).padStart(2, '0')}
|
||||||
@@ -132,7 +132,7 @@ function AbstractGeometry() {
|
|||||||
className="absolute inset-0 overflow-hidden rounded-xl"
|
className="absolute inset-0 overflow-hidden rounded-xl"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
{/* Primary large circle */}
|
{/* Primary large circle — filled radial glow */}
|
||||||
<div
|
<div
|
||||||
className="absolute rounded-full"
|
className="absolute rounded-full"
|
||||||
style={{
|
style={{
|
||||||
@@ -145,6 +145,19 @@ function AbstractGeometry() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Large circle ring — border only, ~40% opacity */}
|
||||||
|
<div
|
||||||
|
className="absolute rounded-full"
|
||||||
|
style={{
|
||||||
|
width: '72%',
|
||||||
|
height: '72%',
|
||||||
|
top: '-18%',
|
||||||
|
right: '-20%',
|
||||||
|
border: '1px solid rgba(91,164,217,0.20)',
|
||||||
|
opacity: 0.4,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Secondary circle, bottom-left */}
|
{/* Secondary circle, bottom-left */}
|
||||||
<div
|
<div
|
||||||
className="absolute rounded-full"
|
className="absolute rounded-full"
|
||||||
@@ -158,6 +171,19 @@ function AbstractGeometry() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Second smaller ring — bottom-left */}
|
||||||
|
<div
|
||||||
|
className="absolute rounded-full"
|
||||||
|
style={{
|
||||||
|
width: '38%',
|
||||||
|
height: '38%',
|
||||||
|
bottom: '-4%',
|
||||||
|
left: '4%',
|
||||||
|
border: '1px solid rgba(0,100,148,0.18)',
|
||||||
|
opacity: 0.45,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Diagonal grid */}
|
{/* Diagonal grid */}
|
||||||
<div
|
<div
|
||||||
className="absolute inset-0 opacity-[0.03]"
|
className="absolute inset-0 opacity-[0.03]"
|
||||||
@@ -168,7 +194,22 @@ function AbstractGeometry() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Accent rectangle top-left */}
|
{/* Diagonal line crossing the composition */}
|
||||||
|
<div
|
||||||
|
className="absolute"
|
||||||
|
style={{
|
||||||
|
width: '130%',
|
||||||
|
height: '1px',
|
||||||
|
top: '42%',
|
||||||
|
left: '-15%',
|
||||||
|
background:
|
||||||
|
'linear-gradient(90deg, transparent 0%, rgba(91,164,217,0.18) 30%, rgba(0,100,148,0.22) 60%, transparent 100%)',
|
||||||
|
transform: 'rotate(-12deg)',
|
||||||
|
transformOrigin: 'left center',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Accent rectangle top-left, rotated */}
|
||||||
<div
|
<div
|
||||||
className="absolute rounded-md"
|
className="absolute rounded-md"
|
||||||
style={{
|
style={{
|
||||||
@@ -181,6 +222,19 @@ function AbstractGeometry() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Second rotated rectangle outline — center */}
|
||||||
|
<div
|
||||||
|
className="absolute rounded-sm"
|
||||||
|
style={{
|
||||||
|
width: '16%',
|
||||||
|
height: '22%',
|
||||||
|
top: '38%',
|
||||||
|
left: '28%',
|
||||||
|
border: '1px solid rgba(91,164,217,0.12)',
|
||||||
|
transform: 'rotate(14deg)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Floating dot cluster center-right */}
|
{/* Floating dot cluster center-right */}
|
||||||
<div
|
<div
|
||||||
className="absolute"
|
className="absolute"
|
||||||
@@ -190,15 +244,29 @@ function AbstractGeometry() {
|
|||||||
top: '35%',
|
top: '35%',
|
||||||
right: '10%',
|
right: '10%',
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
'radial-gradient(circle, rgba(91,164,217,0.25) 1.5px, transparent 1.5px)',
|
'radial-gradient(circle, rgba(91,164,217,0.28) 1.5px, transparent 1.5px)',
|
||||||
backgroundSize: '10px 10px',
|
backgroundSize: '10px 10px',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Thin arc line */}
|
{/* Small dot cluster upper-left */}
|
||||||
|
<div
|
||||||
|
className="absolute"
|
||||||
|
style={{
|
||||||
|
width: '14%',
|
||||||
|
height: '16%',
|
||||||
|
top: '12%',
|
||||||
|
right: '32%',
|
||||||
|
backgroundImage:
|
||||||
|
'radial-gradient(circle, rgba(0,100,148,0.20) 1.5px, transparent 1.5px)',
|
||||||
|
backgroundSize: '8px 8px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Thin dashed arc */}
|
||||||
<svg
|
<svg
|
||||||
className="absolute"
|
className="absolute"
|
||||||
style={{ top: '20%', left: '30%', opacity: 0.08 }}
|
style={{ top: '20%', left: '30%', opacity: 0.09 }}
|
||||||
width="140"
|
width="140"
|
||||||
height="140"
|
height="140"
|
||||||
viewBox="0 0 140 140"
|
viewBox="0 0 140 140"
|
||||||
@@ -223,10 +291,20 @@ function AbstractGeometry() {
|
|||||||
height: '6%',
|
height: '6%',
|
||||||
bottom: '28%',
|
bottom: '28%',
|
||||||
right: '28%',
|
right: '28%',
|
||||||
background: 'rgba(91,164,217,0.20)',
|
background: 'rgba(91,164,217,0.22)',
|
||||||
transform: 'rotate(12deg)',
|
transform: 'rotate(12deg)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Navy bottom overlay that fades upward — gives depth to pull-quote */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 bottom-0 rounded-b-xl"
|
||||||
|
style={{
|
||||||
|
height: '45%',
|
||||||
|
background:
|
||||||
|
'linear-gradient(to top, rgba(28,43,58,0.55) 0%, rgba(28,43,58,0.18) 55%, transparent 100%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -319,6 +397,7 @@ export default function Philosophy() {
|
|||||||
'bg-surface-high rounded-xl p-6 shadow-subtle',
|
'bg-surface-high rounded-xl p-6 shadow-subtle',
|
||||||
'max-w-[320px] w-[calc(100%-2.5rem)] lg:max-w-[340px]',
|
'max-w-[320px] w-[calc(100%-2.5rem)] lg:max-w-[340px]',
|
||||||
'z-10',
|
'z-10',
|
||||||
|
'border-l-[3px] border-primary',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* CornerBracket top-right decoration */}
|
{/* CornerBracket top-right decoration */}
|
||||||
|
|||||||
@@ -54,27 +54,36 @@ function StepCard({ numeral, stepKey, Icon }: { numeral: string; stepKey: string
|
|||||||
<motion.div
|
<motion.div
|
||||||
variants={revealVariants}
|
variants={revealVariants}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative flex flex-col bg-surface-high rounded-xl p-6',
|
'relative flex flex-col bg-surface-high rounded-xl pt-0 px-6 pb-6 overflow-hidden',
|
||||||
'shadow-subtle',
|
'shadow-subtle',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
{/* Top accent strip — gradient from primary-dark to primary */}
|
||||||
|
<div
|
||||||
|
className="absolute top-0 left-0 right-0 h-[3px] rounded-t-xl"
|
||||||
|
style={{ background: 'linear-gradient(90deg, #006494, #5BA4D9)' }}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Ghosted numeral — scales up on scroll */}
|
{/* Ghosted numeral — scales up on scroll */}
|
||||||
<motion.span
|
<motion.span
|
||||||
variants={numeralScaleVariants}
|
variants={numeralScaleVariants}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="font-serif text-6xl font-light leading-none text-on-surface/[0.06] select-none -ml-0.5 mb-3"
|
className="font-serif text-6xl font-light leading-none text-on-surface/[0.06] select-none -ml-0.5 mb-3 mt-6"
|
||||||
>
|
>
|
||||||
{numeral}
|
{numeral}
|
||||||
</motion.span>
|
</motion.span>
|
||||||
|
|
||||||
{/* Icon */}
|
{/* Icon container */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<Icon
|
<div className="w-12 h-12 rounded-xl bg-primary/8 flex items-center justify-center">
|
||||||
size={24}
|
<Icon
|
||||||
strokeWidth={1.5}
|
size={22}
|
||||||
className="text-primary"
|
strokeWidth={1.5}
|
||||||
aria-hidden="true"
|
className="text-primary"
|
||||||
/>
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
@@ -111,26 +120,71 @@ export default function Process() {
|
|||||||
|
|
||||||
{/* ── Header column ── */}
|
{/* ── Header column ── */}
|
||||||
<div className="lg:col-span-1 lg:sticky lg:top-32">
|
<div className="lg:col-span-1 lg:sticky lg:top-32">
|
||||||
<SectionHeader
|
{/* Vertical accent bar left of heading */}
|
||||||
eyebrow={t('process.eyebrow')}
|
<div className="relative pl-4">
|
||||||
title={t('process.title')}
|
<div
|
||||||
align="left"
|
className="absolute left-0 top-[3.25rem] w-0.5 h-10 rounded-full bg-primary"
|
||||||
/>
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<SectionHeader
|
||||||
|
eyebrow={t('process.eyebrow')}
|
||||||
|
title={t('process.title')}
|
||||||
|
align="left"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Steps column ── */}
|
{/* ── Steps column ── */}
|
||||||
<div className="lg:col-span-3">
|
<div className="lg:col-span-3">
|
||||||
<motion.div
|
<div className="relative">
|
||||||
variants={staggerContainerWide}
|
{/* Dashed connector line — visible on sm+ grid layouts only */}
|
||||||
initial="hidden"
|
<div
|
||||||
whileInView="visible"
|
className="hidden sm:block absolute inset-0 pointer-events-none"
|
||||||
viewport={viewportOnce}
|
aria-hidden="true"
|
||||||
className="grid grid-cols-1 sm:grid-cols-2 gap-5"
|
>
|
||||||
>
|
<svg
|
||||||
{STEPS.map((step) => (
|
className="w-full h-full"
|
||||||
<StepCard key={step.key} numeral={step.numeral} stepKey={step.key} Icon={step.Icon} />
|
preserveAspectRatio="none"
|
||||||
))}
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</motion.div>
|
>
|
||||||
|
{/* Horizontal dashes across the middle gap */}
|
||||||
|
<line
|
||||||
|
x1="50%" y1="50%"
|
||||||
|
x2="50%" y2="50%"
|
||||||
|
stroke="rgba(91,164,217,0.18)"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeDasharray="4 6"
|
||||||
|
/>
|
||||||
|
{/* Vertical dashes down the centre gap */}
|
||||||
|
<line
|
||||||
|
x1="0%" y1="50%"
|
||||||
|
x2="100%" y2="50%"
|
||||||
|
stroke="rgba(91,164,217,0.18)"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeDasharray="4 6"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
x1="50%" y1="0%"
|
||||||
|
x2="50%" y2="100%"
|
||||||
|
stroke="rgba(91,164,217,0.18)"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeDasharray="4 6"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
variants={staggerContainerWide}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewportOnce}
|
||||||
|
className="grid grid-cols-1 sm:grid-cols-2 gap-5"
|
||||||
|
>
|
||||||
|
{STEPS.map((step) => (
|
||||||
|
<StepCard key={step.key} numeral={step.numeral} stepKey={step.key} Icon={step.Icon} />
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -92,97 +92,278 @@ const comingSoonVariants = {
|
|||||||
|
|
||||||
function GeometricPlaceholder({
|
function GeometricPlaceholder({
|
||||||
variant = 'featured',
|
variant = 'featured',
|
||||||
|
cardVariant = 'default',
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
variant?: 'featured' | 'small';
|
variant?: 'featured' | 'small';
|
||||||
|
cardVariant?: 'default' | 'nimara' | 'amador';
|
||||||
className?: string;
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
const isFeatured = variant === 'featured';
|
const isFeatured = variant === 'featured';
|
||||||
|
|
||||||
|
// Different gradient bases per card so secondary cards look distinct
|
||||||
|
const gradientMap = {
|
||||||
|
default: 'from-primary-dark/90 to-primary/70',
|
||||||
|
nimara: 'from-navy/95 to-primary-dark/75',
|
||||||
|
amador: 'from-[#1a3a4a]/95 to-primary/60',
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative overflow-hidden bg-gradient-to-br from-primary-dark/90 to-primary/70',
|
'relative overflow-hidden bg-gradient-to-br',
|
||||||
|
gradientMap[cardVariant],
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
{/* Abstract geometric shapes */}
|
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
{/* Large circle top-right */}
|
|
||||||
<div
|
{isFeatured ? (
|
||||||
className="absolute rounded-full bg-white/[0.06]"
|
/* ── Featured (Monaco): blueprint feel ── */
|
||||||
style={{
|
<>
|
||||||
width: isFeatured ? '55%' : '70%',
|
{/* Large filled circle top-right */}
|
||||||
height: isFeatured ? '55%' : '70%',
|
<div
|
||||||
top: '-15%',
|
className="absolute rounded-full bg-white/[0.06]"
|
||||||
right: '-10%',
|
style={{ width: '55%', height: '55%', top: '-15%', right: '-10%' }}
|
||||||
}}
|
/>
|
||||||
/>
|
{/* Large circle ring — blueprint layer */}
|
||||||
{/* Medium circle bottom-left */}
|
<div
|
||||||
<div
|
className="absolute rounded-full"
|
||||||
className="absolute rounded-full bg-white/[0.08]"
|
style={{
|
||||||
style={{
|
width: '45%',
|
||||||
width: isFeatured ? '40%' : '50%',
|
height: '45%',
|
||||||
height: isFeatured ? '40%' : '50%',
|
top: '10%',
|
||||||
bottom: '-20%',
|
left: '5%',
|
||||||
left: '-5%',
|
border: '1px solid rgba(255,255,255,0.12)',
|
||||||
}}
|
opacity: 0.4,
|
||||||
/>
|
}}
|
||||||
{/* Diagonal stripes overlay */}
|
/>
|
||||||
<div
|
{/* Medium circle bottom-left */}
|
||||||
className="absolute inset-0 opacity-[0.04]"
|
<div
|
||||||
style={{
|
className="absolute rounded-full bg-white/[0.08]"
|
||||||
backgroundImage:
|
style={{ width: '40%', height: '40%', bottom: '-20%', left: '-5%' }}
|
||||||
'repeating-linear-gradient(-45deg, #fff 0, #fff 1px, transparent 0, transparent 50%)',
|
/>
|
||||||
backgroundSize: isFeatured ? '28px 28px' : '20px 20px',
|
{/* Subtle grid overlay (blueprint feel) */}
|
||||||
}}
|
<div
|
||||||
/>
|
className="absolute inset-0 opacity-[0.035]"
|
||||||
{/* Small accent rectangle */}
|
style={{
|
||||||
<div
|
backgroundImage:
|
||||||
className="absolute bg-white/[0.12] rounded-sm"
|
'linear-gradient(rgba(255,255,255,1) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,1) 1px, transparent 1px)',
|
||||||
style={{
|
backgroundSize: '32px 32px',
|
||||||
width: isFeatured ? '18%' : '24%',
|
}}
|
||||||
height: isFeatured ? '28%' : '36%',
|
/>
|
||||||
bottom: '18%',
|
{/* Diagonal stripes very subtle */}
|
||||||
right: '15%',
|
<div
|
||||||
transform: 'rotate(-6deg)',
|
className="absolute inset-0 opacity-[0.025]"
|
||||||
}}
|
style={{
|
||||||
/>
|
backgroundImage:
|
||||||
{/* Thin horizontal line accent */}
|
'repeating-linear-gradient(-45deg, #fff 0, #fff 1px, transparent 0, transparent 50%)',
|
||||||
<div
|
backgroundSize: '28px 28px',
|
||||||
className="absolute bg-white/20 rounded-full"
|
}}
|
||||||
style={{
|
/>
|
||||||
width: isFeatured ? '30%' : '40%',
|
{/* Small dot cluster */}
|
||||||
height: '1px',
|
<div
|
||||||
top: '38%',
|
className="absolute opacity-[0.12]"
|
||||||
left: '10%',
|
style={{
|
||||||
}}
|
width: '20%',
|
||||||
/>
|
height: '20%',
|
||||||
{/* Grid-dot accent */}
|
top: '55%',
|
||||||
<div
|
right: '18%',
|
||||||
className="absolute opacity-[0.07]"
|
backgroundImage: 'radial-gradient(circle, #fff 1.5px, transparent 1.5px)',
|
||||||
style={{
|
backgroundSize: '8px 8px',
|
||||||
width: isFeatured ? '25%' : '30%',
|
}}
|
||||||
height: isFeatured ? '25%' : '30%',
|
/>
|
||||||
top: '50%',
|
{/* Blueprint horizontal line — wide, white/20 */}
|
||||||
right: '22%',
|
<div
|
||||||
backgroundImage: 'radial-gradient(circle, #fff 1px, transparent 1px)',
|
className="absolute bg-white/[0.22] rounded-full"
|
||||||
backgroundSize: '8px 8px',
|
style={{ width: '60%', height: '1px', top: '50%', left: '5%' }}
|
||||||
}}
|
/>
|
||||||
/>
|
{/* Second thinner line below */}
|
||||||
|
<div
|
||||||
|
className="absolute bg-white/[0.10] rounded-full"
|
||||||
|
style={{ width: '40%', height: '1px', top: 'calc(50% + 10px)', left: '5%' }}
|
||||||
|
/>
|
||||||
|
{/* Accent rotated rectangle */}
|
||||||
|
<div
|
||||||
|
className="absolute bg-white/[0.10] rounded-sm"
|
||||||
|
style={{
|
||||||
|
width: '18%',
|
||||||
|
height: '28%',
|
||||||
|
bottom: '18%',
|
||||||
|
right: '15%',
|
||||||
|
transform: 'rotate(-6deg)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Small diagonal accent line */}
|
||||||
|
<div
|
||||||
|
className="absolute bg-white/[0.15]"
|
||||||
|
style={{
|
||||||
|
width: '25%',
|
||||||
|
height: '1px',
|
||||||
|
top: '28%',
|
||||||
|
right: '8%',
|
||||||
|
transform: 'rotate(-30deg)',
|
||||||
|
transformOrigin: 'left center',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : cardVariant === 'nimara' ? (
|
||||||
|
/* ── Port Nimara: horizontal bands + arc ── */
|
||||||
|
<>
|
||||||
|
{/* Large arc ring top-left */}
|
||||||
|
<div
|
||||||
|
className="absolute rounded-full"
|
||||||
|
style={{
|
||||||
|
width: '80%',
|
||||||
|
height: '80%',
|
||||||
|
top: '-30%',
|
||||||
|
left: '-20%',
|
||||||
|
border: '1px solid rgba(255,255,255,0.10)',
|
||||||
|
opacity: 0.5,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Smaller ring center */}
|
||||||
|
<div
|
||||||
|
className="absolute rounded-full"
|
||||||
|
style={{
|
||||||
|
width: '45%',
|
||||||
|
height: '70%',
|
||||||
|
bottom: '-15%',
|
||||||
|
right: '-10%',
|
||||||
|
border: '1px solid rgba(91,164,217,0.25)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Horizontal rule band */}
|
||||||
|
<div
|
||||||
|
className="absolute bg-white/[0.06]"
|
||||||
|
style={{ height: '28%', bottom: 0, left: 0, right: 0 }}
|
||||||
|
/>
|
||||||
|
{/* Fine horizontal lines */}
|
||||||
|
<div
|
||||||
|
className="absolute bg-white/20 rounded-full"
|
||||||
|
style={{ width: '50%', height: '1px', top: '35%', right: '8%' }}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="absolute bg-white/10 rounded-full"
|
||||||
|
style={{ width: '30%', height: '1px', top: '45%', right: '8%' }}
|
||||||
|
/>
|
||||||
|
{/* Dot cluster top-right */}
|
||||||
|
<div
|
||||||
|
className="absolute opacity-[0.14]"
|
||||||
|
style={{
|
||||||
|
width: '25%',
|
||||||
|
height: '30%',
|
||||||
|
top: '5%',
|
||||||
|
right: '5%',
|
||||||
|
backgroundImage: 'radial-gradient(circle, #fff 1.5px, transparent 1.5px)',
|
||||||
|
backgroundSize: '8px 8px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Diagonal line */}
|
||||||
|
<div
|
||||||
|
className="absolute bg-white/[0.12]"
|
||||||
|
style={{
|
||||||
|
width: '40%',
|
||||||
|
height: '1px',
|
||||||
|
top: '60%',
|
||||||
|
left: '5%',
|
||||||
|
transform: 'rotate(-20deg)',
|
||||||
|
transformOrigin: 'left center',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
/* ── Port Amador: triangular/radial composition ── */
|
||||||
|
<>
|
||||||
|
{/* Central radial glow */}
|
||||||
|
<div
|
||||||
|
className="absolute"
|
||||||
|
style={{
|
||||||
|
width: '70%',
|
||||||
|
height: '70%',
|
||||||
|
top: '-10%',
|
||||||
|
right: '-15%',
|
||||||
|
background:
|
||||||
|
'radial-gradient(circle, rgba(91,164,217,0.18) 0%, transparent 70%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Diamond/rotated square accent */}
|
||||||
|
<div
|
||||||
|
className="absolute"
|
||||||
|
style={{
|
||||||
|
width: '30%',
|
||||||
|
height: '50%',
|
||||||
|
bottom: '-10%',
|
||||||
|
left: '10%',
|
||||||
|
border: '1px solid rgba(255,255,255,0.12)',
|
||||||
|
transform: 'rotate(45deg)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Diagonal stripes (different angle) */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 opacity-[0.03]"
|
||||||
|
style={{
|
||||||
|
backgroundImage:
|
||||||
|
'repeating-linear-gradient(30deg, #fff 0, #fff 1px, transparent 0, transparent 50%)',
|
||||||
|
backgroundSize: '20px 20px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Top horizontal line */}
|
||||||
|
<div
|
||||||
|
className="absolute bg-white/20 rounded-full"
|
||||||
|
style={{ width: '40%', height: '1px', top: '22%', left: '8%' }}
|
||||||
|
/>
|
||||||
|
{/* Small filled rectangle */}
|
||||||
|
<div
|
||||||
|
className="absolute bg-white/[0.12] rounded-sm"
|
||||||
|
style={{
|
||||||
|
width: '20%',
|
||||||
|
height: '32%',
|
||||||
|
top: '15%',
|
||||||
|
right: '12%',
|
||||||
|
transform: 'rotate(8deg)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* Dot accent bottom-right */}
|
||||||
|
<div
|
||||||
|
className="absolute opacity-[0.16]"
|
||||||
|
style={{
|
||||||
|
width: '22%',
|
||||||
|
height: '22%',
|
||||||
|
bottom: '8%',
|
||||||
|
right: '8%',
|
||||||
|
backgroundImage: 'radial-gradient(circle, #fff 1.5px, transparent 1.5px)',
|
||||||
|
backgroundSize: '7px 7px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Bottom gradient fade */}
|
|
||||||
<div className="absolute inset-x-0 bottom-0 h-1/3 bg-gradient-to-t from-primary-dark/50 to-transparent" />
|
{/* Bottom gradient fade — card bg color bleed for text readability */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 bottom-0 h-2/5"
|
||||||
|
style={{
|
||||||
|
background: isFeatured
|
||||||
|
? 'linear-gradient(to top, rgba(0,100,148,0.55) 0%, transparent 100%)'
|
||||||
|
: cardVariant === 'nimara'
|
||||||
|
? 'linear-gradient(to top, rgba(28,43,58,0.60) 0%, transparent 100%)'
|
||||||
|
: 'linear-gradient(to top, rgba(26,58,74,0.55) 0%, transparent 100%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Tag Chip ─────────────────────────────────────────────────────────────────
|
// ─── Tag Chip ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function TagChip({ label }: { label: string }) {
|
function TagChip({ label, showDot = false }: { label: string; showDot?: boolean }) {
|
||||||
return (
|
return (
|
||||||
<span className="inline-flex items-center bg-primary/10 text-primary-dark text-xs font-medium px-2.5 py-1 rounded-full leading-none">
|
<span className="inline-flex items-center gap-1.5 bg-primary/10 text-primary-dark text-[0.75rem] font-semibold px-3 py-1 rounded-full leading-none tracking-wide">
|
||||||
|
{showDot && (
|
||||||
|
<span className="w-1 h-1 rounded-full bg-primary/50 shrink-0" aria-hidden="true" />
|
||||||
|
)}
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -203,6 +384,7 @@ function FeaturedCard({ project, readLabel }: { project: Project; readLabel: str
|
|||||||
{/* Geometric image placeholder */}
|
{/* Geometric image placeholder */}
|
||||||
<GeometricPlaceholder
|
<GeometricPlaceholder
|
||||||
variant="featured"
|
variant="featured"
|
||||||
|
cardVariant="default"
|
||||||
className="w-full aspect-[16/9] md:aspect-[2/1]"
|
className="w-full aspect-[16/9] md:aspect-[2/1]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -210,8 +392,8 @@ function FeaturedCard({ project, readLabel }: { project: Project; readLabel: str
|
|||||||
<div className="flex flex-col flex-1 p-7 gap-4">
|
<div className="flex flex-col flex-1 p-7 gap-4">
|
||||||
{/* Tags */}
|
{/* Tags */}
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{project.tags.map((tag) => (
|
{project.tags.map((tag, i) => (
|
||||||
<TagChip key={tag} label={tag} />
|
<TagChip key={tag} label={tag} showDot={i > 0} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -230,16 +412,15 @@ function FeaturedCard({ project, readLabel }: { project: Project; readLabel: str
|
|||||||
href={`/work/${project.slug}`}
|
href={`/work/${project.slug}`}
|
||||||
className={cn(
|
className={cn(
|
||||||
'inline-flex items-center gap-2 text-sm font-medium text-primary-dark',
|
'inline-flex items-center gap-2 text-sm font-medium text-primary-dark',
|
||||||
'transition-gap duration-200 group/link',
|
'mt-1 group/link',
|
||||||
'mt-1',
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="underline underline-offset-4 decoration-primary/40 group-hover/link:decoration-primary-dark transition-colors duration-200">
|
<span className="relative after:absolute after:bottom-0 after:left-0 after:h-px after:w-full after:bg-primary-dark after:origin-left after:scale-x-100 after:transition-transform after:duration-200">
|
||||||
{readLabel}
|
{readLabel}
|
||||||
</span>
|
</span>
|
||||||
<ArrowRight
|
<ArrowRight
|
||||||
size={14}
|
size={15}
|
||||||
className="transition-transform duration-200 group-hover/link:translate-x-1"
|
className="transition-transform duration-200 group-hover/link:translate-x-1.5"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -249,7 +430,14 @@ function FeaturedCard({ project, readLabel }: { project: Project; readLabel: str
|
|||||||
|
|
||||||
// ─── Small Card ───────────────────────────────────────────────────────────────
|
// ─── Small Card ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const SLUG_TO_VARIANT: Record<string, 'nimara' | 'amador'> = {
|
||||||
|
'port-nimara': 'nimara',
|
||||||
|
'port-amador': 'amador',
|
||||||
|
};
|
||||||
|
|
||||||
function SmallCard({ project, readLabel }: { project: Project; readLabel: string }) {
|
function SmallCard({ project, readLabel }: { project: Project; readLabel: string }) {
|
||||||
|
const cardVariant = SLUG_TO_VARIANT[project.slug] ?? 'nimara';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.article
|
<motion.article
|
||||||
variants={smallCardVariants}
|
variants={smallCardVariants}
|
||||||
@@ -271,6 +459,7 @@ function SmallCard({ project, readLabel }: { project: Project; readLabel: string
|
|||||||
>
|
>
|
||||||
<GeometricPlaceholder
|
<GeometricPlaceholder
|
||||||
variant="small"
|
variant="small"
|
||||||
|
cardVariant={cardVariant}
|
||||||
className="w-full aspect-[16/7]"
|
className="w-full aspect-[16/7]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -280,8 +469,8 @@ function SmallCard({ project, readLabel }: { project: Project; readLabel: string
|
|||||||
<div className="flex flex-col flex-1 p-5 gap-3">
|
<div className="flex flex-col flex-1 p-5 gap-3">
|
||||||
{/* Tags */}
|
{/* Tags */}
|
||||||
<div className="flex flex-wrap gap-1.5">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
{project.tags.map((tag) => (
|
{project.tags.map((tag, i) => (
|
||||||
<TagChip key={tag} label={tag} />
|
<TagChip key={tag} label={tag} showDot={i > 0} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -300,12 +489,12 @@ function SmallCard({ project, readLabel }: { project: Project; readLabel: string
|
|||||||
href={`/work/${project.slug}`}
|
href={`/work/${project.slug}`}
|
||||||
className="inline-flex items-center gap-1.5 text-xs font-medium text-primary-dark group/link"
|
className="inline-flex items-center gap-1.5 text-xs font-medium text-primary-dark group/link"
|
||||||
>
|
>
|
||||||
<span className="underline underline-offset-4 decoration-primary/40 group-hover/link:decoration-primary-dark transition-colors duration-200">
|
<span className="relative after:absolute after:bottom-0 after:left-0 after:h-px after:w-full after:bg-primary-dark after:origin-left after:scale-x-100 after:transition-transform after:duration-200">
|
||||||
{readLabel}
|
{readLabel}
|
||||||
</span>
|
</span>
|
||||||
<ArrowRight
|
<ArrowRight
|
||||||
size={12}
|
size={13}
|
||||||
className="transition-transform duration-200 group-hover/link:translate-x-0.5"
|
className="transition-transform duration-200 group-hover/link:translate-x-1"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -368,14 +557,21 @@ export default function SelectedWorks() {
|
|||||||
0% { background-position: 0 0, 100% 0, 100% 100%, 0 100%; }
|
0% { background-position: 0 0, 100% 0, 100% 100%, 0 100%; }
|
||||||
100% { background-position: 100% 0, 100% 100%, 0 100%, 0 0; }
|
100% { background-position: 100% 0, 100% 100%, 0 100%, 0 0; }
|
||||||
}
|
}
|
||||||
|
@keyframes coming-soon-bg-pulse {
|
||||||
|
0%, 100% { background-color: var(--color-surface-low); }
|
||||||
|
50% { background-color: var(--color-surface); }
|
||||||
|
}
|
||||||
@keyframes coming-soon-fade {
|
@keyframes coming-soon-fade {
|
||||||
0%, 100% { opacity: 0; }
|
0%, 100% { opacity: 0; }
|
||||||
50% { opacity: 1; }
|
50% { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
.coming-soon-card {
|
||||||
|
animation: coming-soon-bg-pulse 4s ease-in-out infinite;
|
||||||
|
}
|
||||||
.coming-soon-pulse {
|
.coming-soon-pulse {
|
||||||
background: radial-gradient(
|
background: radial-gradient(
|
||||||
ellipse at center,
|
ellipse at center,
|
||||||
rgba(91, 164, 217, 0.04) 0%,
|
rgba(91, 164, 217, 0.06) 0%,
|
||||||
transparent 70%
|
transparent 70%
|
||||||
);
|
);
|
||||||
animation: coming-soon-fade 4s ease-in-out infinite;
|
animation: coming-soon-fade 4s ease-in-out infinite;
|
||||||
|
|||||||
@@ -44,7 +44,16 @@ export default function ServicesOverview() {
|
|||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="services" className="bg-surface py-24">
|
<section
|
||||||
|
id="services"
|
||||||
|
className="relative bg-surface py-24"
|
||||||
|
style={{
|
||||||
|
backgroundImage: [
|
||||||
|
'repeating-linear-gradient(0deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
'repeating-linear-gradient(90deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
].join(', '),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="container mx-auto px-6">
|
<div className="container mx-auto px-6">
|
||||||
|
|
||||||
{/* Section header */}
|
{/* Section header */}
|
||||||
@@ -75,8 +84,8 @@ export default function ServicesOverview() {
|
|||||||
variants={revealVariants}
|
variants={revealVariants}
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative flex flex-col p-8 bg-surface',
|
'relative flex flex-col p-8 bg-surface',
|
||||||
'transition-colors duration-200 ease-out',
|
'transition-all duration-200 ease-out',
|
||||||
'hover:bg-surface-high',
|
'hover:bg-surface-high hover:shadow-subtle',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* Ghosted numeral */}
|
{/* Ghosted numeral */}
|
||||||
@@ -102,7 +111,7 @@ export default function ServicesOverview() {
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="mt-[0.35em] shrink-0 w-1 h-1 rounded-full bg-primary/50"
|
className="mt-[0.4em] shrink-0 w-1.5 h-1.5 rounded-full bg-primary"
|
||||||
/>
|
/>
|
||||||
{feature}
|
{feature}
|
||||||
</li>
|
</li>
|
||||||
@@ -120,18 +129,33 @@ export default function ServicesOverview() {
|
|||||||
initial="hidden"
|
initial="hidden"
|
||||||
whileInView="visible"
|
whileInView="visible"
|
||||||
viewport={viewportOnce}
|
viewport={viewportOnce}
|
||||||
className="mt-16 flex flex-col items-center gap-4"
|
className="mt-16 flex items-center gap-6"
|
||||||
>
|
>
|
||||||
{/* Decorative rule */}
|
{/* Left horizontal rule */}
|
||||||
<motion.span
|
<motion.span
|
||||||
variants={scaleVariants}
|
variants={scaleVariants}
|
||||||
className="block w-px h-10 bg-primary/30"
|
className="flex-1 h-px max-w-[120px] ml-auto"
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
'linear-gradient(to left, rgba(0,100,148,0.25), transparent)',
|
||||||
|
}}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p className="font-serif italic text-xl text-primary-dark text-center max-w-xl leading-relaxed">
|
<p className="font-serif italic text-2xl text-primary-dark text-center max-w-xl leading-relaxed">
|
||||||
{t('services.aiNarrative')}
|
{t('services.aiNarrative')}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* Right horizontal rule */}
|
||||||
|
<motion.span
|
||||||
|
variants={scaleVariants}
|
||||||
|
className="flex-1 h-px max-w-[120px] mr-auto"
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
'linear-gradient(to right, rgba(0,100,148,0.25), transparent)',
|
||||||
|
}}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,17 +49,29 @@ function TrustCard({ item, index, t }: TrustCardProps) {
|
|||||||
<motion.div
|
<motion.div
|
||||||
variants={revealVariants}
|
variants={revealVariants}
|
||||||
className={cn(
|
className={cn(
|
||||||
'group flex flex-col items-start gap-4 p-6',
|
'group relative flex flex-col items-start gap-4 p-8',
|
||||||
'rounded-2xl bg-surface-high shadow-subtle',
|
'rounded-2xl bg-surface-high shadow-subtle',
|
||||||
'transition-shadow duration-300 hover:shadow-card',
|
'transition-shadow duration-300 hover:shadow-card',
|
||||||
'cursor-default',
|
'overflow-hidden cursor-default',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
{/* Top accent gradient bar — fades in on hover */}
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
'pointer-events-none absolute top-0 left-6 right-6 h-[3px] rounded-full',
|
||||||
|
'opacity-0 group-hover:opacity-100 transition-opacity duration-300',
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(to right, #006494, #5BA4D9)',
|
||||||
|
}}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Icon with scale-bounce on scroll reveal */}
|
{/* Icon with scale-bounce on scroll reveal */}
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={iconBounceVariants}
|
variants={iconBounceVariants}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center justify-center w-12 h-12 rounded-xl',
|
'flex items-center justify-center w-12 h-12 rounded-full',
|
||||||
'bg-primary/8',
|
'bg-primary/8',
|
||||||
'transition-transform duration-300 ease-out',
|
'transition-transform duration-300 ease-out',
|
||||||
'group-hover:-translate-y-1',
|
'group-hover:-translate-y-1',
|
||||||
@@ -67,7 +79,7 @@ function TrustCard({ item, index, t }: TrustCardProps) {
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
size={28}
|
size={24}
|
||||||
className="text-primary transition-colors duration-300"
|
className="text-primary transition-colors duration-300"
|
||||||
strokeWidth={1.75}
|
strokeWidth={1.75}
|
||||||
/>
|
/>
|
||||||
@@ -99,8 +111,16 @@ export default function TrustBar() {
|
|||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
aria-label="Trust indicators"
|
aria-label="Trust indicators"
|
||||||
className="bg-surface-low py-16"
|
className="relative bg-surface-low py-16"
|
||||||
>
|
>
|
||||||
|
{/* Gradient bridge — blends hero surface-high into this section */}
|
||||||
|
<div
|
||||||
|
className="absolute top-0 left-0 right-0 h-8 pointer-events-none"
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(to bottom, #ffffff, transparent)',
|
||||||
|
}}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
<div className="max-w-6xl mx-auto px-6">
|
<div className="max-w-6xl mx-auto px-6">
|
||||||
{/* Stagger wrapper — triggers children revealVariants on scroll */}
|
{/* Stagger wrapper — triggers children revealVariants on scroll */}
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|||||||
Reference in New Issue
Block a user