feat: replace geometric placeholders with real project and brand images
All checks were successful
Build & Push / build-and-push (push) Successful in 1m35s

- Monaco Ocean: monaco_high_res.jpg in featured card
- Port Nimara: anguilla.png in small card
- Port Amador: panama.png in small card
- Philosophy section: philosophy_image.png replaces AbstractGeometry
- About "Our Story": our_story.png replaces StoryGeometry
- Removed all geometric placeholder components (GeometricPlaceholder,
  AbstractGeometry, StoryGeometry)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-06 16:32:09 -04:00
parent bcc09542b7
commit 1f6bb7d066
8 changed files with 39 additions and 592 deletions

View File

@@ -9,6 +9,7 @@ import {
fadeVariants,
viewportOnce,
} from '@/lib/animations';
import Image from 'next/image';
import { Link } from '@/i18n/navigation';
import { Lock, Clock, ArrowRight } from 'lucide-react';
@@ -21,6 +22,7 @@ interface Project {
/** number of tags to resolve from the translation array */
tagCount: number;
featured?: boolean;
image: string;
}
interface ComingSoonItem {
@@ -37,16 +39,19 @@ const PROJECTS: Project[] = [
slug: 'monaco-ocean',
tagCount: 2,
featured: true,
image: '/images/monaco_high_res.jpg',
},
{
i18nKey: 'portNimara',
slug: 'port-nimara',
tagCount: 2,
image: '/images/anguilla.png',
},
{
i18nKey: 'portAmador',
slug: 'port-amador',
tagCount: 2,
image: '/images/panama.png',
},
];
@@ -86,274 +91,6 @@ const comingSoonVariants = {
},
};
// ─── Geometric Placeholder ────────────────────────────────────────────────────
function GeometricPlaceholder({
variant = 'featured',
cardVariant = 'default',
className,
}: {
variant?: 'featured' | 'small';
cardVariant?: 'default' | 'nimara' | 'amador';
className?: string;
}) {
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 (
<div
className={cn(
'relative overflow-hidden bg-gradient-to-br',
gradientMap[cardVariant],
className,
)}
aria-hidden="true"
>
<div className="absolute inset-0">
{isFeatured ? (
/* ── Featured (Monaco): blueprint feel ── */
<>
{/* Large filled circle top-right */}
<div
className="absolute rounded-full bg-white/[0.06]"
style={{ width: '55%', height: '55%', top: '-15%', right: '-10%' }}
/>
{/* Large circle ring — blueprint layer */}
<div
className="absolute rounded-full"
style={{
width: '45%',
height: '45%',
top: '10%',
left: '5%',
border: '1px solid rgba(255,255,255,0.12)',
opacity: 0.4,
}}
/>
{/* Medium circle bottom-left */}
<div
className="absolute rounded-full bg-white/[0.08]"
style={{ width: '40%', height: '40%', bottom: '-20%', left: '-5%' }}
/>
{/* Subtle grid overlay (blueprint feel) */}
<div
className="absolute inset-0 opacity-[0.035]"
style={{
backgroundImage:
'linear-gradient(rgba(255,255,255,1) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,1) 1px, transparent 1px)',
backgroundSize: '32px 32px',
}}
/>
{/* Diagonal stripes very subtle */}
<div
className="absolute inset-0 opacity-[0.025]"
style={{
backgroundImage:
'repeating-linear-gradient(-45deg, #fff 0, #fff 1px, transparent 0, transparent 50%)',
backgroundSize: '28px 28px',
}}
/>
{/* Small dot cluster */}
<div
className="absolute opacity-[0.12]"
style={{
width: '20%',
height: '20%',
top: '55%',
right: '18%',
backgroundImage: 'radial-gradient(circle, #fff 1.5px, transparent 1.5px)',
backgroundSize: '8px 8px',
}}
/>
{/* Blueprint horizontal line — wide, white/20 */}
<div
className="absolute bg-white/[0.22] rounded-full"
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>
{/* 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>
);
}
// ─── Tag Chip ─────────────────────────────────────────────────────────────────
function TagChip({ label, showDot = false }: { label: string; showDot?: boolean }) {
@@ -383,12 +120,15 @@ function FeaturedCard({ project, readLabel, t }: { project: Project; readLabel:
'transition-shadow duration-300 hover:shadow-[0_24px_48px_rgba(25,28,29,0.10)]',
)}
>
{/* Geometric image placeholder */}
<GeometricPlaceholder
variant="featured"
cardVariant="default"
className="w-full aspect-[16/9] md:aspect-[2/1]"
/>
<div className="relative w-full aspect-[16/9] md:aspect-[2/1] overflow-hidden">
<Image
src={project.image}
alt={t(`work.projects.${project.i18nKey}.title`)}
fill
className="object-cover transition-transform duration-500 group-hover:scale-[1.03]"
sizes="(max-width: 768px) 100vw, 66vw"
/>
</div>
{/* Content */}
<div className="flex flex-col flex-1 p-7 gap-4">
@@ -432,13 +172,7 @@ function FeaturedCard({ project, readLabel, t }: { project: Project; readLabel:
// ─── Small Card ───────────────────────────────────────────────────────────────
const SLUG_TO_VARIANT: Record<string, 'nimara' | 'amador'> = {
'port-nimara': 'nimara',
'port-amador': 'amador',
};
function SmallCard({ project, readLabel, t }: { project: Project; readLabel: string; t: (key: string) => string }) {
const cardVariant = SLUG_TO_VARIANT[project.slug] ?? 'nimara';
const tags = Array.from({ length: project.tagCount }, (_, i) =>
t(`work.projects.${project.i18nKey}.tags.${i}`),
);
@@ -453,7 +187,6 @@ function SmallCard({ project, readLabel, t }: { project: Project; readLabel: str
'hover:shadow-subtle',
)}
>
{/* Geometric placeholder — grayscale to color on hover */}
<div className="relative overflow-hidden">
<div
className={cn(
@@ -462,11 +195,15 @@ function SmallCard({ project, readLabel, t }: { project: Project; readLabel: str
'opacity-80 group-hover:opacity-100',
)}
>
<GeometricPlaceholder
variant="small"
cardVariant={cardVariant}
className="w-full aspect-[16/7]"
/>
<div className="relative w-full aspect-[16/7]">
<Image
src={project.image}
alt={t(`work.projects.${project.i18nKey}.title`)}
fill
className="object-cover"
sizes="(max-width: 768px) 100vw, 33vw"
/>
</div>
</div>
</div>