Next.js 14 website with standalone output configured for Docker deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
122 lines
4.6 KiB
TypeScript
122 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
import { motion, useInView } from "framer-motion";
|
|
import { useRef } from "react";
|
|
import Image from "next/image";
|
|
|
|
const lines = [
|
|
{ text: "You've already planned the Hagia Sophia.", delay: 0, check: true },
|
|
{ text: "You've booked the Bosphorus cruise.", delay: 0.1, check: true },
|
|
{ text: "You've saved the Grand Bazaar food tour.", delay: 0.2, check: true },
|
|
{ text: "", delay: 0 },
|
|
{ text: "And you'll love all of it. Millions do.", delay: 0.4 },
|
|
{ text: "", delay: 0 },
|
|
{ text: "But here's what nobody tells you:", highlight: true, delay: 0.6 },
|
|
];
|
|
|
|
const facts = [
|
|
{
|
|
bold: "Istanbul's best neighbourhood for breakfast?",
|
|
text: " It's on the Asian side.",
|
|
},
|
|
{
|
|
bold: "The boulevard that rivals the Champs-Élysées?",
|
|
text: " Asian side.",
|
|
},
|
|
{
|
|
bold: "The sunset that makes the European skyline look like a painting?",
|
|
text: " You can only see it from the Asian side.",
|
|
},
|
|
{
|
|
bold: "The restaurants where Istanbul's own elite spend their weekends?",
|
|
text: " All on the Asian side.",
|
|
},
|
|
];
|
|
|
|
export function Manifesto() {
|
|
const ref = useRef(null);
|
|
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
|
|
|
return (
|
|
<section className="py-20 md:py-32 section-padding bg-warm-sand" ref={ref}>
|
|
<div className="max-w-7xl mx-auto">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-20 items-center">
|
|
<div className="space-y-6">
|
|
<div className="space-y-1">
|
|
{lines.map((line, i) =>
|
|
line.text === "" ? (
|
|
<div key={i} className="h-4" />
|
|
) : (
|
|
<motion.p
|
|
key={i}
|
|
className={`text-lg md:text-xl leading-relaxed ${
|
|
line.highlight
|
|
? "text-coral-spritz font-display font-bold text-2xl md:text-3xl mt-4"
|
|
: "text-deep-nazar/80"
|
|
}`}
|
|
initial={{ opacity: 0, x: -20 }}
|
|
animate={isInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ delay: line.delay, duration: 0.5 }}
|
|
>
|
|
{line.check && (
|
|
<span className="text-coral-spritz mr-2 font-semibold">✓</span>
|
|
)}
|
|
{line.text}
|
|
</motion.p>
|
|
)
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-4 mt-8">
|
|
{facts.map((fact, i) => (
|
|
<motion.p
|
|
key={i}
|
|
className="text-base md:text-lg leading-relaxed text-deep-nazar/80"
|
|
initial={{ opacity: 0, x: -20 }}
|
|
animate={isInView ? { opacity: 1, x: 0 } : {}}
|
|
transition={{ delay: 0.8 + i * 0.15, duration: 0.5 }}
|
|
>
|
|
<strong className="text-deep-nazar">{fact.bold}</strong>
|
|
{fact.text}
|
|
</motion.p>
|
|
))}
|
|
</div>
|
|
|
|
<motion.div
|
|
className="pt-6"
|
|
initial={{ opacity: 0 }}
|
|
animate={isInView ? { opacity: 1 } : {}}
|
|
transition={{ delay: 1.6 }}
|
|
>
|
|
<p className="text-lg md:text-xl text-deep-nazar font-display font-semibold leading-relaxed">
|
|
The Anatolian Edit exists for a simple reason: the best day of
|
|
your Istanbul trip shouldn't be the one you already planned. It
|
|
should be the one you almost missed.
|
|
</p>
|
|
</motion.div>
|
|
</div>
|
|
|
|
<motion.div
|
|
className="relative"
|
|
initial={{ opacity: 0, scale: 0.95 }}
|
|
animate={isInView ? { opacity: 1, scale: 1 } : {}}
|
|
transition={{ delay: 0.5, duration: 0.8 }}
|
|
>
|
|
<div className="relative aspect-[4/5] rounded-3xl overflow-hidden">
|
|
<Image
|
|
src="/images/manifesto_coastline.jpg"
|
|
alt="Locals relaxing on the green waterfront of Istanbul's Asian side with the Marmara Sea and Princes' Islands in the background"
|
|
fill
|
|
className="object-cover"
|
|
sizes="(max-width: 1024px) 100vw, 50vw"
|
|
/>
|
|
</div>
|
|
<div className="absolute -bottom-4 -left-4 bg-sun-yolk text-deep-nazar rounded-2xl px-5 py-3 font-display font-bold text-sm rotate-[-4deg]" style={{ boxShadow: "0 6px 20px rgba(0,0,0,0.18), 0 2px 6px rgba(0,0,0,0.12)" }}>
|
|
90% of tourists never see this side
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|