Next.js 14 website with standalone output configured for Docker deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
88 lines
3.2 KiB
TypeScript
88 lines
3.2 KiB
TypeScript
"use client";
|
||
|
||
import { motion, useInView } from "framer-motion";
|
||
import { useRef } from "react";
|
||
import { SectionHeader } from "@/components/shared/SectionHeader";
|
||
import { AnimatedCounter } from "@/components/shared/AnimatedCounter";
|
||
import { pressQuotes } from "@/lib/constants";
|
||
|
||
const stats = [
|
||
{
|
||
title: "Where Istanbulites Actually Go",
|
||
stat: "1 in 3",
|
||
description:
|
||
"Istanbul residents lives on the Asian side. On weekends, thousands more cross over for breakfast, shopping, and the best sunsets in the city.",
|
||
},
|
||
{
|
||
title: "Named One of the World's 50 Coolest Neighbourhoods",
|
||
stat: "Top 50",
|
||
description:
|
||
"Kadıköy was named by Time Out as one of the 50 coolest neighbourhoods on Earth. You've probably never heard of it.",
|
||
},
|
||
{
|
||
title: "Zero Tourist Crowds",
|
||
stat: "50,000",
|
||
description:
|
||
"While Sultanahmet processes 50,000 visitors a day, you'll share Moda's waterfront with students, artists, and cats. Mostly cats.",
|
||
},
|
||
];
|
||
|
||
export function WhyAsianSide() {
|
||
const ref = useRef(null);
|
||
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
||
|
||
return (
|
||
<section className="py-20 md:py-32 section-padding" ref={ref}>
|
||
<div className="max-w-7xl mx-auto">
|
||
<SectionHeader
|
||
eyebrow="Why the Asian Side?"
|
||
title="The Side Istanbul Loves Most"
|
||
subtitle="Don't take our word for it. Take Istanbul's."
|
||
/>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-16">
|
||
{stats.map((stat, i) => (
|
||
<motion.div
|
||
key={i}
|
||
className="bg-white rounded-3xl p-8 shadow-sm border border-deep-nazar/5 text-center"
|
||
initial={{ opacity: 0, y: 30 }}
|
||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||
transition={{ delay: i * 0.15, duration: 0.6 }}
|
||
>
|
||
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-bosphorus/10 text-bosphorus font-display font-bold text-lg mb-4">
|
||
{stat.stat === "50,000" ? (
|
||
<AnimatedCounter target={50000} suffix="" />
|
||
) : (
|
||
stat.stat
|
||
)}
|
||
</div>
|
||
<h3 className="font-display text-lg font-bold text-deep-nazar mb-3">
|
||
{stat.title}
|
||
</h3>
|
||
<p className="text-deep-nazar/60 text-sm leading-relaxed">
|
||
{stat.description}
|
||
</p>
|
||
</motion.div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Press marquee */}
|
||
<div className="overflow-hidden py-6 border-y border-deep-nazar/10">
|
||
<div className="animate-marquee flex whitespace-nowrap">
|
||
{[...pressQuotes, ...pressQuotes].map((press, i) => (
|
||
<span
|
||
key={i}
|
||
className="mx-8 text-deep-nazar/40 text-sm font-medium inline-flex items-center gap-2"
|
||
>
|
||
<span className="italic">“{press.quote}”</span>
|
||
<span className="text-bosphorus font-semibold">
|
||
— {press.source}
|
||
</span>
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|