first commit

This commit is contained in:
2026-04-15 22:37:39 +03:00
parent 3c59557946
commit 1590bef227
156 changed files with 13823 additions and 84 deletions

View File

@@ -0,0 +1,154 @@
"use client";
import { motion } from "framer-motion";
import { resortData } from "@/src/data/resort";
import Image from "next/image";
import Link from "next/link";
import { notFound, useParams } from "next/navigation";
const getCloudinaryUrl = (publicId: string) => {
return `https://res.cloudinary.com/du7xohbct/image/upload/q_auto,f_auto,w_2000/${publicId}`;
};
export default function RoomDetailsPage() {
const params = useParams();
const slug = params.slug as string;
const lang = "tr"; // Mocked locale
const room = resortData.rooms.find((r) => r.slug === slug);
if (!room) {
notFound();
}
return (
<div className="bg-[#FAF9F6] min-h-screen">
{/* Hero Header */}
<section className="relative h-[65vh] w-full overflow-hidden">
<motion.div
initial={{ scale: 1.1 }}
animate={{ scale: 1 }}
transition={{ duration: 1.5 }}
className="absolute inset-0"
>
<Image
src={getCloudinaryUrl(room.mainImageId)}
alt={room.name[lang]}
fill
className="object-cover brightness-75"
priority
/>
</motion.div>
<div className="absolute inset-0 flex flex-col items-center justify-center text-center z-10 px-6">
<motion.span
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="text-white text-xs tracking-[0.5em] font-bold uppercase mb-4"
>
{room.size} {room.capacity}
</motion.span>
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="text-white text-5xl md:text-8xl font-serif"
>
{room.name[lang]}
</motion.h1>
</div>
</section>
{/* Content Section */}
<section className="max-w-7xl mx-auto px-6 py-32">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-20">
{/* Left: Description & Details */}
<div className="lg:col-span-2 space-y-16">
<div className="space-y-6">
<h2 className="text-sm font-bold tracking-[0.3em] text-gold uppercase">ODA HAKKINDA</h2>
<p className="text-2xl md:text-3xl font-serif text-gray-900 leading-relaxed font-light italic">
{room.description[lang]}
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-8 pt-10 border-t border-black/5">
{room.features.map((f) => {
const feat = resortData.featureIcons[f];
return feat ? (
<div key={f} className="flex items-center gap-4 group">
<div className="w-12 h-12 rounded-full border border-black/5 flex items-center justify-center text-xl bg-white transition-colors group-hover:border-gold group-hover:bg-gold/5">
{feat.icon}
</div>
<span className="text-[10px] font-bold tracking-widest text-gray-400 uppercase">
{feat.label[lang]}
</span>
</div>
) : null;
})}
</div>
</div>
{/* Right: Sticky Sidebar / Call to Action */}
<div className="lg:col-span-1">
<div className="sticky top-32 bg-white p-10 shadow-2xl shadow-black/5 border border-black/5">
<div className="space-y-8">
<div>
<h4 className="text-[10px] font-bold tracking-widest text-gray-400 uppercase mb-2">KONAKLAMA TİPİ</h4>
<p className="text-xl font-serif text-gray-900">{room.capacity}</p>
</div>
<div>
<h4 className="text-[10px] font-bold tracking-widest text-gray-400 uppercase mb-2">ODA BÜYÜKLÜĞÜ</h4>
<p className="text-xl font-serif text-gray-900">{room.size}</p>
</div>
<div className="pt-8">
<Link
href={resortData.bookingUrl}
className="block w-full bg-[#5C6353] text-white text-center py-5 text-[10px] font-bold tracking-[0.3em] uppercase hover:bg-gold transition-all"
>
ŞİMDİ REZERVASYON YAP
</Link>
<p className="mt-4 text-[9px] text-gray-400 text-center tracking-tight leading-normal">
* En iyi fiyat garantisi ve resmi web sitesi avantajları ile yerinizi ayırtın.
</p>
</div>
</div>
</div>
</div>
</div>
</section>
{/* Gallery Highlight */}
<section className="bg-white py-24 pb-48">
<div className="max-w-7xl mx-auto px-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-1">
{room.galleryImageIds.slice(0, 3).map((id, i) => (
<div key={id} className="relative aspect-square overflow-hidden group">
<Image
src={getCloudinaryUrl(id)}
alt={`${room.name[lang]} Gallery ${i + 1}`}
fill
className="object-cover grayscale hover:grayscale-0 transition-all duration-700"
/>
</div>
))}
</div>
<div className="mt-20 text-center">
<Link
href="/accommodation"
className="text-[10px] font-bold tracking-[0.4em] text-gray-400 hover:text-gold transition-colors inline-flex items-center gap-4"
>
<span className="w-12 h-px bg-current" />
GERİ DÖN
<span className="w-12 h-px bg-current" />
</Link>
</div>
</div>
</section>
</div>
);
}

View File

@@ -0,0 +1,90 @@
"use client";
import { motion } from "framer-motion";
import { resortData } from "@/src/data/resort";
import { useState } from "react";
import AccommodationCard from "../components/AccommodationCard";
export default function AccommodationPage() {
const lang = "tr";
const [filter, setFilter] = useState<"all" | "rooms" | "residence">("all");
const filteredRooms = resortData.rooms.filter(room => {
if (filter === "all") return true;
if (filter === "rooms") return !room.slug.includes("rezidans");
if (filter === "residence") return room.slug.includes("rezidans");
return true;
});
return (
<div className="bg-[#ECE7E1] min-h-screen pt-72 md:pt-96 pb-48 flex flex-col items-center">
<div className="max-w-[1600px] mx-auto px-6 ">
{/* Header Section */}
<div className="space-y-12 mb-32 mt-40">
<div className="space-y-6">
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
className="flex items-center gap-4"
>
<div className="w-12 h-px bg-gold" />
<span className="text-gold text-[10px] tracking-[0.5em] font-bold uppercase block">
KONAKLAMA DENEYİMİ
</span>
</motion.div>
<motion.h1
initial={{ y: 30, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.1, duration: 0.8 }}
className="text-6xl md:text-8xl font-serif text-gray-900 leading-[0.85] tracking-tight"
>
Zarafet & <br /><span className="italic font-light">Ege Serüveni</span>
</motion.h1>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3 }}
className="pt-10 border-t border-black/5"
>
<p className="text-gray-500 font-light text-2xl leading-relaxed italic border-l-4 border-gold/20 pl-8 max-w-2xl">
&ldquo;Bardakçı Koyu&apos;nun büyüleyici manzarasında, lüksün ve huzurun buluştuğu noktada sizi ırlıyoruz.&rdquo;
</p>
</motion.div>
</div>
<div className="flex bg-white p-2 rounded-full shadow-sm border border-black/5 w-fit">
{["all", "rooms", "residence"].map((id) => (
<button
key={id}
onClick={() => setFilter(id as any)}
className={`px-10 py-3 rounded-full text-[10px] font-bold tracking-[0.2em] transition-all duration-500 ${filter === id ? "bg-[#5C6353] text-white shadow-lg" : "text-gray-400 hover:text-gray-900"
}`}
>
{id === "all" ? "HEPSİ" : id === "rooms" ? "ODALAR" : "REZİDANS"}
</button>
))}
</div>
</div>
{/* Accommodation Grid - Single Column for Wide Mode */}
<div className="flex flex-col gap-y-16">
{filteredRooms.map((room) => (
<motion.div
key={room.slug}
layout
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
>
<AccommodationCard room={room} lang={lang} />
</motion.div>
))}
</div>
</div>
</div>
);
}

13
app/activities/page.tsx Normal file
View File

@@ -0,0 +1,13 @@
export default function PlaceholderPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-white py-32 section-padding">
<div className="max-w-4xl mx-auto text-center space-y-8">
<h1 className="text-6xl font-serif text-bodrum-blue tracking-widest opacity-20">COMING SOON</h1>
<div className="w-24 h-px bg-turquoise mx-auto" />
<p className="text-xl text-gray-400 italic font-light">
Bu bölüm çok yakında yayına girecektir.
</p>
</div>
</div>
);
}

13
app/beach/page.tsx Normal file
View File

@@ -0,0 +1,13 @@
export default function PlaceholderPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-zinc-50 py-32 section-padding">
<div className="max-w-4xl mx-auto text-center">
<h1 className="text-6xl font-serif text-bodrum-blue mb-8 uppercase tracking-widest">SOON</h1>
<p className="text-xl text-gray-400 italic font-light leading-relaxed">
&ldquo;Ege'nin turkuaz sularında eşsiz bir sahil deneyimi çok yakında.&rdquo;
</p>
<div className="mt-12 h-px w-24 bg-turquoise mx-auto" />
</div>
</div>
);
}

View File

@@ -0,0 +1,86 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { resortData } from "@/src/data/resort";
import { motion } from "framer-motion";
import { useRef } from "react";
interface AccommodationCardProps {
room: any;
lang: "tr";
}
const getCloudinaryUrl = (publicId: string, width = 1200) => {
if (!publicId) return "https://images.unsplash.com/photo-1566073771259-6a8506099945?auto=format&fit=crop&q=80&w=1200";
return `https://res.cloudinary.com/du7xohbct/image/upload/q_auto,f_auto,w_${width}/${publicId}`;
};
export default function AccommodationCard({ room, lang }: AccommodationCardProps) {
const allImages = [
room.mainImageId,
...(room.galleryImageIds || [])
];
const scrollRef = useRef<HTMLDivElement>(null);
return (
<div className="flex flex-col w-full bg-[#ECE7E1] mb-12 shadow-sm">
{/* Precision Header Section */}
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center px-10 py-12 gap-10">
{/* Left: Title & Descriptions */}
<div className="flex-[2] space-y-3">
<h3 className="text-[#4F5B3A] text-2xl font-serif tracking-tight leading-none mb-1">
{room.name[lang]} <span className="text-sm font-sans opacity-60">({room.size})</span>
</h3>
<p className="text-[13px] leading-relaxed text-[#4F5B3A] opacity-90 font-light max-w-2xl">
{room.description[lang]}
</p>
</div>
{/* Right: Booking Button */}
<div className="flex-shrink-0 w-full lg:w-auto">
<Link
href={`/accommodation/${room.slug}`}
className="block w-full lg:w-auto bg-[#C87E4B] hover:bg-[#A6693E] text-black px-12 py-4 text-[11px] font-bold tracking-[0.2em] rounded-md transition-all duration-300 transform hover:-translate-y-1 shadow-xl hover:shadow-[#C87E4B]/20 text-center uppercase"
>
Odayı İncele
</Link>
</div>
</div>
{/* DRAGGABLE SLIDER */}
<div
style={{ height: '550px', position: 'relative', width: '100vw', left: '50%', right: '50%', marginLeft: '-50vw', marginRight: '-50vw' }}
className="overflow-hidden cursor-grab active:cursor-grabbing bg-zinc-200"
>
<motion.div
ref={scrollRef}
drag="x"
dragConstraints={{ right: 80, left: -((allImages.length - 1) * 720) }}
className="flex h-full gap-4 px-[15vw]"
>
{allImages.map((id, idx) => (
<div
key={`${id}-${idx}`}
style={{ height: '100%', position: 'relative', minWidth: '700px', flexShrink: 0 }}
className="rounded-sm overflow-hidden shadow-sm group"
>
<Image
src={getCloudinaryUrl(id, 1200)}
alt="Gallery"
fill
style={{ objectFit: 'cover' }}
sizes="(max-width: 1024px) 100vw, 80vw"
className="transition-transform duration-700 group-hover:scale-105 pointer-events-none"
draggable={false}
priority={idx < 2}
/>
</div>
))}
</motion.div>
</div>
</div>
);
}

View File

@@ -0,0 +1,41 @@
import { ExperienceSection } from "@/src/data/resort";
import { getCloudinaryUrl } from "@/src/lib/cloudinary";
import Link from "next/link";
import Image from "next/image";
interface ExperienceCardProps {
experience: ExperienceSection;
lang: "tr" | "en" | "de";
}
export default function ExperienceCard({ experience, lang }: ExperienceCardProps) {
return (
<Link
href={experience.href}
className="group relative overflow-hidden rounded-2xl aspect-square block"
>
<Image
src={getCloudinaryUrl(experience.imageId, { width: 800, height: 800, crop: "fill" })}
alt={experience.title[lang]}
fill
className="object-cover transition-transform duration-1000 group-hover:scale-110 group-hover:rotate-1"
/>
{/* Dark Overlay */}
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-60 transition-opacity duration-700 group-hover:opacity-40" />
{/* Minimal Gold Border Hover */}
<div className="absolute inset-4 border border-white/0 transition-all duration-700 group-hover:border-gold/30 group-hover:backdrop-blur-[2px] flex items-center justify-center">
<span className="text-white text-xs font-bold tracking-[0.4em] opacity-0 translate-y-4 transition-all duration-700 group-hover:opacity-100 group-hover:translate-y-0 border-b border-gold pb-1">
{lang === "tr" ? "KEŞFET" : lang === "en" ? "EXPLORE" : "ENTDECKEN"}
</span>
</div>
{/* Content */}
<div className="absolute bottom-8 left-8 text-white group-hover:opacity-0 transition-opacity duration-500">
<h3 className="text-2xl font-serif font-bold mb-1 tracking-wider">{experience.title[lang]}</h3>
<p className="text-white/70 text-[10px] uppercase tracking-[0.2em] font-medium">{experience.subtitle[lang]}</p>
</div>
</Link>
);
}

View File

@@ -0,0 +1,49 @@
"use client";
import { resortData } from "@/src/data/resort";
import Link from "next/link";
export default function FloatingBookingBar() {
// Normally would have state for dates, but following PRD's "minimal bar"
const lang = "tr"; // Mocked locale
return (
<div className="fixed bottom-6 left-0 right-0 z-40 px-6 animate-fade-in-up">
<div className="max-w-4xl mx-auto glass-dark text-white rounded-full p-2 flex items-center shadow-2xl">
<div className="flex-1 flex items-center divide-x divide-white/10 px-4 overflow-x-auto no-scrollbar">
{/* Check In */}
<div className="px-4 py-2 flex flex-col min-w-[120px]">
<span className="text-[10px] uppercase tracking-widest text-white/50">
{resortData.floatingBar.checkIn[lang]}
</span>
<span className="text-sm font-semibold italic">Select Date</span>
</div>
{/* Check Out */}
<div className="px-4 py-2 flex flex-col min-w-[120px]">
<span className="text-[10px] uppercase tracking-widest text-white/50">
{resortData.floatingBar.checkOut[lang]}
</span>
<span className="text-sm font-semibold italic">Select Date</span>
</div>
{/* Guests */}
<div className="px-4 py-2 flex flex-col min-w-[100px]">
<span className="text-[10px] uppercase tracking-widest text-white/50">
{resortData.floatingBar.guests[lang]}
</span>
<span className="text-sm font-semibold">2 Adults</span>
</div>
</div>
<Link
href={resortData.bookingUrl}
target="_blank"
className="bg-gold text-white px-8 py-3 rounded-full font-bold text-sm hover:bg-gold-dark transition-all whitespace-nowrap"
>
{resortData.floatingBar.search[lang]}
</Link>
</div>
</div>
);
}

124
app/components/Footer.tsx Normal file
View File

@@ -0,0 +1,124 @@
"use client";
import Link from "next/link";
import { resortData } from "@/src/data/resort";
export default function Footer() {
return (
<footer className="bg-[#EAE5D8] pt-24 pb-8 px-6 overflow-hidden border-t border-black/5">
<div className="max-w-7xl mx-auto">
{/* Main Footer Content */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-12 mb-20">
{/* Column 1: Brand & Newsletter */}
<div className="lg:col-span-1 space-y-8">
<h2 className="text-3xl font-serif text-gray-900 tracking-tighter">SALMAKIS</h2>
<div className="space-y-4">
<div className="relative group">
<input
type="email"
placeholder="E-Posta Adresiniz"
className="w-full bg-white border-none py-4 px-6 pr-32 text-xs tracking-widest outline-none focus:ring-1 focus:ring-gold transition-all"
/>
<button className="absolute right-1 top-1 bottom-1 bg-[#C59D5F] text-white px-6 text-[10px] font-bold tracking-widest hover:bg-gold transition-all">
ABONE OL
</button>
</div>
<label className="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" className="w-3 h-3 accent-gold border-gray-300" />
<span className="text-[10px] text-gray-400 font-light tracking-wider group-hover:text-gray-600 transition-colors">
Kişisel verilerimin işlenmesini kabul ediyorum.
</span>
</label>
</div>
<div className="flex gap-4 pt-4">
{resortData.social.map((social) => (
<Link
key={social.platform}
href={social.url}
className="text-gray-900 hover:text-gold transition-colors text-lg"
>
<i className={`fab fa-${social.platform.toLowerCase()}`}></i>
</Link>
))}
</div>
</div>
{/* Column 2: Salmakis */}
<div className="space-y-6">
<h4 className="text-[10px] font-bold tracking-[0.3em] text-gray-400 uppercase">SALMAKIS</h4>
<ul className="space-y-3">
{["Doğada.", "Sofrada.", "Odalarda.", "Bizimle."].map((item) => (
<li key={item}>
<Link href="#" className="text-sm font-light text-gray-600 hover:text-gold transition-colors">{item}</Link>
</li>
))}
</ul>
</div>
{/* Column 3: Sayfalar */}
<div className="space-y-6">
<h4 className="text-[10px] font-bold tracking-[0.3em] text-gray-400 uppercase">SAYFALAR</h4>
<ul className="space-y-3">
{["Konaklama", "S.S.S", "Sürdürülebilirlik", "Resim Galerisi", "Fiyat Listesi"].map((item) => (
<li key={item}>
<Link href="#" className="text-sm font-light text-gray-600 hover:text-gold transition-colors">{item}</Link>
</li>
))}
</ul>
</div>
{/* Column 4: Yasal */}
<div className="space-y-6">
<h4 className="text-[10px] font-bold tracking-[0.3em] text-gray-400 uppercase">VERİ KORUMA</h4>
<ul className="space-y-3">
{["Künye", "Gizlilik Politikası", "KVKK", "Çerez Ayarları"].map((item) => (
<li key={item}>
<Link href="#" className="text-sm font-light text-gray-600 hover:text-gold transition-colors">{item}</Link>
</li>
))}
</ul>
</div>
{/* Column 5: İletişim */}
<div className="space-y-6">
<h4 className="text-[10px] font-bold tracking-[0.3em] text-gray-400 uppercase">İLETİŞİM</h4>
<ul className="space-y-3">
<li><Link href="#" className="text-sm font-light text-gray-600 hover:text-gold transition-colors">Oda Rezerve Et</Link></li>
<li><Link href="#" className="text-sm font-light text-gray-600 hover:text-gold transition-colors">Yol Tarifi Al</Link></li>
<li><Link href="#" className="text-sm font-light text-gray-600 hover:text-gold transition-colors">İletişime Geç</Link></li>
<li className="pt-2 text-sm text-gray-900 font-medium tracking-wider">{resortData.contact.phone}</li>
<li className="text-sm text-gray-400 font-light">{resortData.contact.email}</li>
</ul>
</div>
</div>
{/* Brand Logos / Partners */}
<div className="border-t border-black/5 py-16 flex flex-wrap justify-center items-center gap-12 md:gap-24 opacity-40 grayscale hover:grayscale-0 transition-all duration-700">
{/* Placeholder Logos reflecting the style in the image */}
<div className="text-2xl font-serif tracking-widest text-[#1a2e1e]">BODRUM</div>
<div className="text-sm font-bold tracking-[0.4em] text-[#1a2e1e]">EGE MUTFAĞI</div>
<div className="text-xl font-serif text-[#1a2e1e] border border-current px-2">S</div>
<div className="text-2xl font-sans font-extralight tracking-[0.3em] text-[#1a2e1e]">BLUE FLAG</div>
</div>
{/* Bottom copyright area */}
<div className="relative pt-12 flex flex-col md:flex-row justify-between items-center gap-4 text-[9px] tracking-[0.2em] font-medium text-gray-400 uppercase">
<div>© 2026 HER HAKKI SAKLIDIR.</div>
{/* Decorative Mountain Line (SVG) */}
<div className="absolute bottom-[-20px] left-1/2 -translate-x-1/2 w-full max-w-4xl opacity-10 -z-10">
<svg viewBox="0 0 1000 100" className="w-full">
<path d="M0 100 L200 60 L400 90 L600 40 L800 70 L1000 50 L1000 100 Z" fill="none" stroke="currentColor" strokeWidth="1" />
</svg>
</div>
<div>SALMAKIS RESORT & SPA</div>
</div>
</div>
</footer>
);
}

80
app/components/Navbar.tsx Normal file
View File

@@ -0,0 +1,80 @@
"use client";
import Link from "next/link";
export default function Navbar() {
const lang = "tr"; // Mocked locale
const navLinks = [
{ label: { tr: "KONAKLAMA", en: "ACCOMMODATION", de: "UNTERKUNFT" }, href: "/accommodation", side: "left" },
{ label: { tr: "YİYECEK & İÇECEK", en: "FOOD & BEVERAGE", de: "FOOD & BEVERAGE" }, href: "/dining", side: "left" },
{ label: { tr: "AKTİVİTE", en: "ACTIVITIES", de: "AKTIVITÄTEN" }, href: "/activities", side: "left" },
{ label: { tr: "ORGANİZASYON", en: "ORGANIZER", de: "ORGANISATION" }, href: "/organizations", side: "right" },
{ label: { tr: "GALERİ", en: "GALLERY", de: "GALERIE" }, href: "/gallery", side: "right" },
{ label: { tr: "SPA CENTER", en: "SPA CENTER", de: "SPA CENTER" }, href: "/spa", side: "right" },
];
return (
<nav className=" top-0 left-0 right-0 z-50 transition-all duration-500 py-6 bg-white shadow-sm border-b border-gray-50">
<div className="max-w-[1600px] mx-auto px-10 flex items-center justify-between">
{/* Left Menu */}
<div className="hidden lg:flex items-center space-x-12 flex-1">
{navLinks.filter(l => l.side === "left").map((link) => (
<Link
key={link.href}
href={link.href}
className="group relative text-[11px] font-medium tracking-[0.2em] transition-all text-gray-900 overflow-hidden"
>
<span className="relative z-10 block transition-transform duration-300 group-hover:-translate-y-full">
{link.label[lang as "tr"]}
</span>
<span className="absolute inset-0 z-20 block transition-transform duration-300 translate-y-full group-hover:translate-y-0 text-gold italic">
{link.label[lang as "tr"]}
</span>
</Link>
))}
</div>
{/* Centered Logo */}
<div className="flex flex-col items-center">
<Link href="/" className="flex flex-col items-center group">
<span className="text-3xl md:text-5xl font-serif font-light tracking-[0.3em] text-gray-900 leading-none">
SALMAKIS
</span>
<span className="text-[10px] tracking-[0.6em] mt-2 text-gray-400 font-sans uppercase">
Resort & Spa
</span>
</Link>
</div>
{/* Right Menu */}
<div className="hidden lg:flex items-center justify-end space-x-12 flex-1">
{navLinks.filter(l => l.side === "right").map((link) => (
<Link
key={link.href}
href={link.href}
className="group relative text-[11px] font-medium tracking-[0.2em] transition-all text-gray-900 overflow-hidden"
>
<span className="relative z-10 block transition-transform duration-300 group-hover:-translate-y-full">
{link.label[lang as "tr"]}
</span>
<span className="absolute inset-0 z-20 block transition-transform duration-300 translate-y-full group-hover:translate-y-0 text-gold italic">
{link.label[lang as "tr"]}
</span>
</Link>
))}
</div>
{/* Mobile Menu */}
<div className="lg:hidden">
<button className="p-2 text-gray-900">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1} stroke="currentColor" className="w-8 h-8">
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 9h16.5m-16.5 6.75h16.5" />
</svg>
</button>
</div>
</div>
</nav>
);
}

View File

@@ -0,0 +1,57 @@
"use client";
import { useRef } from "react";
import { motion, useScroll, useTransform, useSpring } from "framer-motion";
export default function ScrollVideo() {
const containerRef = useRef<HTMLDivElement>(null);
// Use scroll progress within this section
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start start", "end end"]
});
// Smooth out the scroll value
const smoothProgress = useSpring(scrollYProgress, {
stiffness: 100,
damping: 30,
restDelta: 0.001
});
// Calculate the expansion: Starts at 25% circle, grows to 150% (to fully cover screen)
const clipPathValue = useTransform(smoothProgress, [0, 1], ["circle(25% at 50% 50%)", "circle(100% at 50% 50%)"]);
// Also fade the overlay
const overlayOpacity = useTransform(smoothProgress, [0, 0.8], [0.3, 0]);
return (
<section
ref={containerRef}
className="relative h-[250vh] bg-white"
>
<div className="sticky top-0 h-screen w-full flex items-center justify-center overflow-hidden z-30">
<motion.div
style={{ clipPath: clipPathValue }}
className="relative w-full h-full flex items-center justify-center bg-white will-change-transform shadow-2xl"
>
{/* YouTube Embed with Hardware Acceleration */}
<div className="absolute inset-0 pointer-events-none scale-110 will-change-transform">
<iframe
className="absolute top-1/2 left-1/2 w-[115vw] h-[115vh] -translate-x-1/2 -translate-y-1/2 object-cover pointer-events-none"
src="https://www.youtube.com/embed/avqL1kRkX0c?autoplay=1&mute=1&controls=0&loop=1&playlist=avqL1kRkX0c&playsinline=1&rel=0&modestbranding=1"
allow="autoplay; encrypted-media"
allowFullScreen
></iframe>
</div>
{/* Subtle Overlay */}
<motion.div
style={{ opacity: overlayOpacity }}
className="absolute inset-0 bg-black/20 z-10 pointer-events-none"
/>
</motion.div>
</div>
</section>
);
}

View File

@@ -0,0 +1,22 @@
import { resortData } from "@/src/data/resort";
export default function SecurityBadge({ lang }: { lang: "tr" | "en" | "de" }) {
return (
<div className="flex flex-col items-center text-center max-w-2xl mx-auto py-12 px-6 border-t border-gray-100">
<div className="w-16 h-16 bg-bodrum-blue rounded-full mb-6 flex items-center justify-center text-white shadow-xl shadow-bodrum-blue/20">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-8 h-8">
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 0 1-1.043 3.296 3.745 3.745 0 0 1-3.296 1.043A3.745 3.745 0 0 1 12 21a3.745 3.745 0 0 1-3.127-1.593 3.745 3.745 0 0 1-3.296-1.043 3.745 3.745 0 0 1-1.043-3.296A3.745 3.745 0 0 1 3 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 0 1 1.043-3.296 3.745 3.745 0 0 1 3.296-1.043A3.745 3.745 0 0 1 12 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 0 1 3.296 1.043 3.746 3.746 0 0 1 1.043 3.296A3.745 3.745 0 0 1 21 12Z" />
</svg>
</div>
<h4 className="text-xl font-bold text-bodrum-blue mb-2">
{resortData.securityBadge.title[lang]}
</h4>
<p className="text-gray-500 text-sm leading-relaxed mb-4">
{resortData.securityBadge.description[lang]}
</p>
<div className="text-[10px] uppercase tracking-[0.2em] font-bold text-gray-400">
Official Website Protection Verified Secure
</div>
</div>
);
}

View File

@@ -0,0 +1,16 @@
"use client";
import { ReactLenis } from "lenis/react";
import { ReactNode } from "react";
export default function SmoothScroll({ children }: { children: ReactNode }) {
return (
<ReactLenis root options={{
lerp: 0.05,
duration: 1.5,
smoothWheel: true
}}>
{children}
</ReactLenis>
);
}

View File

@@ -0,0 +1,95 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { getCloudinaryUrl } from "@/src/lib/cloudinary";
import { motion } from "framer-motion";
interface SplitSectionProps {
title: string;
subtitle: string;
description: string;
mainImage: string;
secondImage: string;
href: string;
reverse?: boolean;
}
export default function SplitSection({
title,
subtitle,
description,
mainImage,
secondImage,
href,
reverse = false
}: SplitSectionProps) {
return (
<section className={`flex flex-col ${reverse ? 'md:flex-row-reverse' : 'md:flex-row'} min-h-screen w-full bg-[#FAF9F6] overflow-hidden`}>
{/* Text Side */}
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8, ease: "easeOut" }}
className="w-full md:w-1/2 flex flex-col justify-center px-10 md:px-24 py-20"
>
<span className="text-gold text-xs tracking-[0.4em] font-bold uppercase mb-6 block">
{subtitle}
</span>
<h2 className="text-5xl md:text-7xl font-serif text-gray-900 leading-tight mb-8">
{title}
</h2>
<p className="text-gray-500 text-lg leading-relaxed max-w-md mb-12 font-light">
{description}
</p>
<div>
<Link
href={href}
className="group relative inline-block overflow-hidden bg-[#C59D5F] text-white px-10 py-4 rounded-md text-[10px] font-bold tracking-widest transition-all hover:bg-gold uppercase active:scale-95"
>
<span className="relative z-10">KEŞFET</span>
<motion.div
className="absolute inset-0 bg-white/20 -translate-x-full group-hover:translate-x-full transition-transform duration-700 ease-in-out"
/>
</Link>
</div>
</motion.div>
{/* Image Side */}
<div className="w-full md:w-1/2 relative min-h-[500px] md:min-h-screen">
<motion.div
initial={{ scale: 1.1, opacity: 0 }}
whileInView={{ scale: 1, opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, ease: "easeOut" }}
className="relative w-full h-full"
>
<Image
src={getCloudinaryUrl(mainImage, { width: 1200, height: 1600, crop: "fill" })}
alt={title}
fill
className="object-cover"
/>
</motion.div>
{/* Floating Secondary Image with Parallax-ish Scroll Effect */}
<motion.div
initial={{ opacity: 0, x: reverse ? -100 : 100, y: 50 }}
whileInView={{ opacity: 1, x: reverse ? "-40px" : "40px", y: 0 }}
viewport={{ once: true }}
transition={{ duration: 1, delay: 0.4, ease: "easeOut" }}
className={`absolute bottom-20 ${reverse ? 'left-0' : 'right-0'} z-10 w-48 md:w-80 aspect-[3/4] shadow-2xl hidden md:block`}
>
<Image
src={getCloudinaryUrl(secondImage, { width: 600, height: 800, crop: "fill" })}
alt={`${title} detail`}
fill
className="object-cover border-[10px] border-white"
/>
</motion.div>
</div>
</section>
);
}

View File

@@ -0,0 +1,120 @@
"use client";
import { motion } from "framer-motion";
import { useRef, useState, useEffect } from "react";
const testimonials = [
{
name: "Ayşe Y.",
location: "Türkiye",
text: "Bardakçı Koyu'nun büyüleyici manzarasında unutulmaz bir tatil geçirdik. Personelin ilgisi ve otelin zarafeti bizi mest etti.",
stars: 5
},
{
name: "John D.",
location: "United Kingdom",
text: "An absolutely stunning resort. The spa treatment was world-class, and the breakfast by the sea was the highlight of our stay.",
stars: 5
},
{
name: "Klaus M.",
location: "Germany",
text: "Sehr schönes Hotel mit exzellentem Service. Der Privatstrand ist kristallklar ve sehr ruhig. Wir kommen auf jeden Fall wieder!",
stars: 5
},
{
name: "Elena S.",
location: "Italy",
text: "Un posto magico. La colazione è fantastica e la camera con vista mare ha superato le nostre aspettative. Grazie Salmakis!",
stars: 5
},
{
name: "Mehmet A.",
location: "Türkiye",
text: "Gastronomi anlamında gerçekten çok başarılı. Her akşam farklı bir lezzet şöleni yaşadık. Sahili ise kelimenin tam anlamıyla kusursuz.",
stars: 5
}
];
export default function TestimonialsSlider() {
const constraintsRef = useRef<HTMLDivElement>(null);
const [width, setWidth] = useState(0);
useEffect(() => {
if (constraintsRef.current) {
// Calculate how much we can drag: total width - container width
setWidth(constraintsRef.current.scrollWidth - constraintsRef.current.offsetWidth);
}
}, []);
return (
<section className="bg-[#EAE5D8] py-24 overflow-hidden">
<div className="max-w-7xl mx-auto px-6 mb-16">
<div className="flex flex-col md:flex-row justify-between items-end gap-8">
<div className="space-y-4">
<span className="text-gold text-xs tracking-[0.4em] font-bold uppercase">
MİSAFİR YORUMLARI
</span>
<h2 className="text-5xl md:text-7xl font-serif text-gray-900 leading-tight">
Misafirlerimiz <br /> Ne Diyor?
</h2>
</div>
<div className="max-w-md text-right md:text-right flex flex-col items-end gap-6 pb-4">
<p className="text-gray-600 font-light leading-relaxed">
Gerçek deneyimler ve samimi sözler. Salmakis Resort & Spa&apos;daki anıların
her türlü tanımdan daha fazlasını anlattığına inanıyoruz.
</p>
<button className="bg-[#C59D5F] text-white px-8 py-3 rounded-md text-[10px] font-bold tracking-[0.2em] hover:bg-gold transition-all uppercase">
DENEYİMİNİZİ PAYLAŞIN
</button>
</div>
</div>
</div>
{/* Slider Container */}
<div className="relative w-full overflow-hidden pb-10">
<motion.div
ref={constraintsRef}
drag="x"
dragConstraints={{ right: 0, left: -width }}
dragElastic={0.1}
whileTap={{ cursor: "grabbing" }}
className="flex gap-6 px-6 md:px-[calc((100vw-1280px)/2)] cursor-grab whitespace-nowrap"
initial={{ x: 100, opacity: 0 }}
whileInView={{ x: 0, opacity: 1 }}
transition={{ duration: 1 }}
>
{testimonials.map((item, i) => (
<div
key={i}
className="min-w-[320px] md:min-w-[400px] bg-[#3A4D3F] p-10 flex flex-col justify-between h-[450px] shadow-2xl select-none"
>
<div className="space-y-6">
<div className="flex gap-1">
{[...Array(item.stars)].map((_, i) => (
<span key={i} className="text-white text-sm"></span>
))}
</div>
<div className="space-y-4">
<span className="text-white/40 text-[10px] tracking-widest font-bold uppercase">
{item.location}
</span>
<p className="text-white/90 text-lg md:text-xl font-light leading-relaxed italic whitespace-normal">
&ldquo;{item.text}&rdquo;
</p>
</div>
</div>
<div className="pt-6 border-t border-white/10">
<span className="text-white text-xs tracking-[0.2em] font-bold uppercase">
{item.name}
</span>
</div>
</div>
))}
</motion.div>
</div>
</section>
);
}

30
app/contact/page.tsx Normal file
View File

@@ -0,0 +1,30 @@
export default function Contact() {
return (
<div className="min-h-screen py-32 section-padding bg-white">
<div className="max-w-7xl mx-auto flex flex-col md:flex-row items-start justify-between gap-20">
<div className="flex-1 space-y-8">
<span className="text-turquoise uppercase tracking-widest text-xs font-bold">CONTACT</span>
<h1 className="text-5xl md:text-7xl font-serif text-bodrum-blue leading-tight">
Let&apos;s talk about your <br /> next getaway.
</h1>
<div className="space-y-4 pt-12">
<p className="text-2xl text-gray-400 font-light hover:text-turquoise transition-colors cursor-pointer">info@salmakis.com.tr</p>
<p className="text-2xl text-gray-400 font-light">+90 252 316 65 06</p>
</div>
</div>
<div className="w-full md:w-1/3 bg-zinc-50 p-12 rounded-3xl shadow-2xl shadow-black/5">
<h3 className="text-xl font-bold mb-8">Location</h3>
<p className="text-gray-500 leading-relaxed mb-12 italic">
Bardakçı Koyu, <br />
Bodrum, Muğla, <br />
Türkiye
</p>
<div className="aspect-square bg-white rounded-2xl border border-gray-100 flex items-center justify-center text-gray-300">
[Interactive Map Placeholder]
</div>
</div>
</div>
</div>
);
}

13
app/dining/page.tsx Normal file
View File

@@ -0,0 +1,13 @@
export default function PlaceholderPage({ params }: { params: { slug: string } }) {
return (
<div className="min-h-screen flex items-center justify-center bg-zinc-50 py-32 section-padding">
<div className="max-w-4xl mx-auto text-center">
<h1 className="text-6xl font-serif text-bodrum-blue mb-8 uppercase tracking-widest">SOON</h1>
<p className="text-xl text-gray-400 italic font-light leading-relaxed">
&ldquo;Ege'nin kalbindeki bu deneyimi çok yakında keşfedeceksiniz.&rdquo;
</p>
<div className="mt-12 h-px w-24 bg-turquoise mx-auto" />
</div>
</div>
);
}

13
app/gallery/page.tsx Normal file
View File

@@ -0,0 +1,13 @@
export default function PlaceholderPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-white py-32 section-padding">
<div className="max-w-4xl mx-auto text-center space-y-8">
<h1 className="text-6xl font-serif text-bodrum-blue tracking-widest opacity-20">COMING SOON</h1>
<div className="w-24 h-px bg-turquoise mx-auto" />
<p className="text-xl text-gray-400 italic font-light">
Bu bölüm çok yakında yayına girecektir.
</p>
</div>
</div>
);
}

View File

@@ -1,26 +1,81 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
@theme {
--font-sans: var(--font-oswald), ui-sans-serif, system-ui;
--font-serif: var(--font-playfair), ui-serif, Georgia;
--color-gold: #C5A059;
--color-gold-dark: #A67C00;
--color-bodrum-blue: #002B45;
--color-sand: #FAF9F6;
--radius-xl: 1rem;
--radius-2xl: 1.5rem;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}
@media (prefers-color-scheme: dark) {
@layer base {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--background: #ffffff;
--foreground: #111111;
}
body {
background-color: var(--background);
color: var(--foreground);
font-feature-settings: "ss01", "ss02", "cv01", "cv02";
}
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
@layer components {
.glass {
@apply bg-white/70 backdrop-blur-md border border-white/20;
}
.glass-dark {
@apply bg-black/50 backdrop-blur-md border border-white/10;
}
.btn-primary {
@apply px-6 py-3 bg-gold text-white rounded-full font-medium transition-all hover:bg-gold-dark hover:scale-105 active:scale-95 shadow-lg shadow-gold/10;
}
.section-padding {
@apply py-20 px-6 md:px-12 lg:px-24;
}
}
/* Custom Animations */
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-up {
animation: fade-in-up 0.8s ease-out forwards;
}
.delay-100 { animation-delay: 100ms; }
.delay-200 { animation-delay: 200ms; }
.delay-300 { animation-delay: 300ms; }
/* Portrait Card Aspect Ratio */
.aspect-portrait {
aspect-ratio: 3 / 4;
}
/* Hide scrollbar for Chrome, Safari and Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}

View File

@@ -1,20 +1,25 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import { Oswald, Playfair_Display } from "next/font/google";
import "./globals.css";
import Navbar from "./components/Navbar";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
import Footer from "./components/Footer";
import SmoothScroll from "./components/SmoothScroll";
const oswald = Oswald({
variable: "--font-oswald",
subsets: ["latin", "latin-ext"],
weight: ["200", "300", "400", "500", "600", "700"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
const playfair = Playfair_Display({
variable: "--font-playfair",
subsets: ["latin", "latin-ext"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Salmakis Resort & Spa | Official Website",
description: "Experience the magic of the Aegean at Salmakis Resort & Spa Bodrum. Luxury accommodation, spa, and beach experience.",
};
export default function RootLayout({
@@ -23,11 +28,17 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">{children}</body>
<html lang="tr" className={`${oswald.variable} ${playfair.variable}`}>
<body className="antialiased min-h-screen flex flex-col font-sans selection:bg-gold/30">
<Navbar />
<SmoothScroll>
<main className="flex-grow">
{children}
</main>
<Footer />
</SmoothScroll>
</body>
</html>
);
}

View File

@@ -0,0 +1,13 @@
export default function PlaceholderPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-white py-32 section-padding">
<div className="max-w-4xl mx-auto text-center space-y-8">
<h1 className="text-6xl font-serif text-bodrum-blue tracking-widest opacity-20">COMING SOON</h1>
<div className="w-24 h-px bg-turquoise mx-auto" />
<p className="text-xl text-gray-400 italic font-light">
Bu bölüm çok yakında yayına girecektir.
</p>
</div>
</div>
);
}

View File

@@ -1,65 +1,244 @@
"use client";
import { useMemo, useRef } from "react";
import Link from "next/link";
import Image from "next/image";
import { motion, useScroll, useTransform } from "framer-motion";
import { resortData } from "@/src/data/resort";
import ScrollVideo from "./components/ScrollVideo";
import SplitSection from "./components/SplitSection";
import TestimonialsSlider from "./components/TestimonialsSlider";
export default function Home() {
const portfolioRef = useRef(null);
const { scrollYProgress } = useScroll({
target: portfolioRef,
offset: ["start end", "end start"]
});
const portfolioBgY = useTransform(scrollYProgress, [0, 1], ["0%", "15%"]);
const portfolioScale = useTransform(scrollYProgress, [0, 0.5, 1], [1.05, 1, 1.05]);
return (
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
<div className="flex flex-col">
<section className="relative h-[85vh] w-full bg-[#FAF9F6] flex flex-col items-center justify-center pt-20 overflow-hidden">
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 1.5, ease: "easeOut" }}
className="absolute bottom-[-20%] w-[1200px] h-[1200px] bg-white rounded-full"
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
<div className="relative z-20 flex flex-col items-center text-center px-6">
<motion.h1
initial={{ y: 30, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 1, ease: "easeOut" }}
className="flex flex-col items-center"
>
<span className="text-gray-900 text-6xl md:text-9xl font-sans font-medium tracking-[0.1em] leading-none mb-2 uppercase">
KUSURSUZ
</span>
<motion.span
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ delay: 0.5, duration: 1 }}
className="text-gray-900 text-6xl md:text-9xl font-serif italic font-light tracking-[0.05em] leading-none"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
BİR KAÇIŞ
</motion.span>
</motion.h1>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1, duration: 1 }}
className="mt-16"
>
<Link
href={resortData.bookingUrl}
className="bg-[#5C6353] text-white px-10 py-4 rounded-full text-[10px] font-bold tracking-[0.3em] hover:bg-gold transition-all shadow-xl shadow-black/5"
>
Learning
</a>{" "}
center.
REZERVASYON YAP
</Link>
</motion.div>
</div>
</section>
<ScrollVideo />
<section className="bg-white py-32 md:py-48 px-6 text-center">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 1 }}
className="max-w-5xl mx-auto space-y-12"
>
<p className="text-3xl md:text-5xl font-serif text-gray-900 leading-[1.6] font-light">
Ege&apos;nin kalbinde, tarihin ve denizin kucaklaştığı özel bir dünyada,
<span className="italic"> unutulmaz anılar</span> biriktirmek için tasarlandı.
</p>
<div className="w-16 h-px bg-gold mx-auto opacity-60" />
</motion.div>
</section>
<section ref={portfolioRef} className="relative min-h-[120vh] py-32 flex flex-col justify-between overflow-hidden">
<motion.div
style={{ y: portfolioBgY, scale: portfolioScale }}
className="absolute inset-0 z-0 h-full w-full"
>
<Image
src="https://images.unsplash.com/photo-1510414842594-a61c69b5ae57?auto=format&fit=crop&q=80&w=2000"
alt="Palms"
fill
className="object-cover brightness-50"
priority
/>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
className="relative z-10 text-center"
>
<span className="text-white text-[10px] tracking-[0.5em] font-bold uppercase opacity-70">
HAYATI KEŞFEDİN
</span>
</motion.div>
<div className="relative z-10 flex flex-col items-center text-center px-6">
<h2 className="flex flex-col items-center">
<motion.span
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
className="text-white text-4xl md:text-7xl font-sans font-extralight tracking-[0.2em] uppercase leading-tight"
>
YAVAŞ YAŞAMANIN <span className="font-serif italic font-light lowercase">lüksü</span>
</motion.span>
<motion.span
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2, duration: 1 }}
className="text-white text-5xl md:text-8xl font-sans font-medium tracking-[0.1em] uppercase leading-tight"
>
SALMAKIS&apos;TE
</motion.span>
</h2>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
<div className="relative z-10 max-w-[1600px] mx-auto w-full px-10 grid grid-cols-1 md:grid-cols-3 gap-12 text-center md:text-left border-t border-white/20 pt-12">
{[
{ tag: "ÖZEL KOYUNUZ", title: "KUSURSUZ SAHİL" },
{ tag: "RİTÜELLER", title: "SPA CENTER" },
{ tag: "GURME", title: "DENİZ MUTFAĞI" },
].map((item, i) => (
<motion.div
key={i}
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ delay: i * 0.1, duration: 0.8 }}
className="group cursor-pointer"
>
<span className="text-white/40 text-[9px] tracking-[0.4em] font-bold block mb-2 transition-colors group-hover:text-gold">
{item.tag}
</span>
<h4 className="text-white text-xl md:text-2xl font-sans font-light tracking-[0.2em] group-hover:italic transition-all">
{item.title}
</h4>
</motion.div>
))}
</div>
</main>
</section>
<section className="bg-white py-32 md:py-48 px-6 text-center">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 1 }}
className="max-w-5xl mx-auto space-y-12"
>
<p className="text-3xl md:text-5xl font-serif text-gray-900 leading-[1.6] font-light">
Salmakis Resort & Spa olarak biz, sadece bir tatil değil, dönüştürücü bir deneyim sunuyoruz.
Halikarnassos&apos;un antik büyüsünü modern lüksle birleştiren benzersiz konumumuz ve misafirperverliğimizle,
Ege&apos;nin en özel köşesinde sizi ırlıyoruz.
</p>
<div className="w-16 h-px bg-gold mx-auto opacity-60" />
<p className="text-lg md:text-2xl font-serif text-gray-400 italic font-light tracking-[0.2em] uppercase">
SESSİZ ZARAFETİN VE KALİTENİN BULUŞMA NOKTASI.
</p>
</motion.div>
</section>
<SplitSection
title="KONAKLAMA"
subtitle="LÜKS & KONFOR"
description="Ege'nin kalbinde, her detayı huzurla işlenmiş odalarımızda lüksü ve konforu yeniden tanımlayın. Maviye uyanmanın en şık hali."
mainImage="salmakis/rooms/deluxe_room_1"
secondImage="salmakis/rooms/superior_room_1"
href="/accommodation"
/>
<SplitSection
title="YİYECEK & İÇECEK"
subtitle="GURME DENEYİM"
description="Denizden gelen tazeliğin Ege otlarıyla buluştuğu, her lokmada ayrı bir hikaye anlatan gastronomi yolculuğuna hazır olun."
mainImage="salmakis/experiences/dining"
secondImage="salmakis/experiences/dining"
href="/dining"
reverse
/>
<SplitSection
title="SPA CENTER"
subtitle="RUHSAL YENİLENME"
description="Bedeninizi ve ruhunuzu antik ritüellerle yenileyin. Salmakis'in şifalı sularında ve uzman ellerde kendinizi yeniden keşfedeceksiniz."
mainImage="salmakis/experiences/spa"
secondImage="salmakis/spa/turkish_bath"
href="/spa"
/>
<SplitSection
title="MAVİ KOY"
subtitle="SAHİL & BEACH"
description="Mavi bayraklı kristal sularımızda, Bodrum güneşinin ve denizin en saf halini keşfedin. Gününüzü turkuaz sularda sonlandırın."
mainImage="salmakis/experiences/beach"
secondImage="salmakis/experiences/beach"
href="/beach"
reverse
/>
<TestimonialsSlider />
<section className="py-24 bg-zinc-50 overflow-hidden">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 1.2 }}
className="max-w-4xl mx-auto text-center space-y-8 px-6"
>
<span className="text-gold uppercase tracking-[0.5em] text-xs font-bold">
MİRASIMIZ
</span>
<h2 className="text-4xl md:text-5xl font-serif text-[#1D3557]">
{resortData.legend.title.tr}
</h2>
<div className="relative">
<div className="absolute -top-12 -left-12 text-9xl text-gold/10 font-serif -z-10">
&ldquo;
</div>
<p className="text-lg md:text-xl text-gray-600 leading-relaxed font-light italic">
{resortData.legend.text.tr}
</p>
</div>
<div className="pt-8">
<div className="w-16 h-px bg-gold mx-auto" />
</div>
</motion.div>
</section>
</div>
);
}

102
app/spa/page.tsx Normal file
View File

@@ -0,0 +1,102 @@
import { resortData } from "@/src/data/resort";
import { getCloudinaryUrl } from "@/src/lib/cloudinary";
import Image from "next/image";
export default function Spa() {
const lang = "tr"; // Mocked locale
return (
<div className="bg-white">
{/* Immersive Hero Header */}
<section className="relative h-[60vh] w-full flex items-center justify-center overflow-hidden">
<Image
src={getCloudinaryUrl("salmakis/spa/spa_hero", { width: 1920, height: 1080, crop: "fill", quality: 90 })}
alt="SPA Center"
fill
className="object-cover brightness-90 animate-fade-in-up"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/30 to-transparent" />
<div className="relative z-10 text-center text-white px-6">
<span className="text-turquoise uppercase tracking-[1em] text-xs font-bold mb-4 block">
REJUVENATE
</span>
<h1 className="text-5xl md:text-7xl font-serif font-bold tracking-tight mb-4">
{resortData.nav.spa[lang]}
</h1>
<div className="w-24 h-px bg-white mx-auto" />
</div>
</section>
{/* Intro Section - White Marble Aesthetic */}
<section className="py-24 section-padding bg-gradient-to-b from-zinc-50 to-white">
<div className="max-w-4xl mx-auto text-center space-y-12">
<div className="inline-block p-4 border border-turquoise/20 rounded-full">
<div className="w-12 h-12 flex items-center justify-center bg-turquoise/10 rounded-full text-turquoise text-2xl"></div>
</div>
<h2 className="text-3xl md:text-5xl font-serif text-bodrum-blue font-medium leading-tight">
{lang === "tr"
? "Saf beyaz mermerin huzuru ve asırlık şifa gelenekleriyle ruhunuzu arındırın."
: "Purify your soul with the peace of pure white marble and centuries-old healing traditions."}
</h2>
<p className="text-gray-500 text-lg font-light leading-relaxed max-w-2xl mx-auto">
{lang === "tr"
? "Salmakis SPA & Wellness, modern terapileri geleneksel Türk hamamı ritüelleriyle birleştirerek size eşsiz bir yenilenme deneyimi sunuyor."
: "Salmakis SPA & Wellness offers you a unique rejuvenation experience by combining modern therapies with traditional Turkish bath rituals."}
</p>
</div>
</section>
{/* Masonry-Style Treatments Gallery */}
<section className="pb-32 section-padding bg-white">
<div className="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-8 lg:gap-12">
{resortData.spaTreatments.map((item, idx) => (
<div
key={item.slug}
className={`flex flex-col group ${idx % 2 === 1 ? 'md:mt-24' : ''} animate-fade-in-up`}
style={{ animationDelay: `${idx * 150}ms` }}
>
<div className="relative aspect-video rounded-3xl overflow-hidden mb-8 shadow-2xl shadow-black/5">
<Image
src={getCloudinaryUrl(item.imageId, { width: 1200, height: 800, crop: "fill" })}
alt={item.name[lang]}
fill
className="object-cover grayscale-[20%] group-hover:grayscale-0 transition-all duration-700 group-hover:scale-105"
/>
<div className="absolute top-6 right-6 bg-white/90 backdrop-blur-md px-4 py-2 rounded-full text-[10px] font-bold tracking-widest text-turquoise">
{item.duration}
</div>
</div>
<div className="px-4">
<h3 className="text-3xl font-serif font-bold text-gray-900 mb-4">{item.name[lang]}</h3>
<p className="text-gray-500 leading-relaxed font-light mb-6">
{item.description[lang]}
</p>
<button className="text-xs font-black tracking-[0.2em] text-turquoise border-b-2 border-turquoise pb-1 hover:text-bodrum-blue hover:border-bodrum-blue transition-colors uppercase">
{lang === "tr" ? "DETAYLARI İNCELE" : "VIEW DETAILS"}
</button>
</div>
</div>
))}
</div>
</section>
{/* Marble Callout */}
<section className="h-screen relative flex items-center justify-center">
<Image
src={getCloudinaryUrl("salmakis/spa/marble_texture", { width: 1920, height: 1080, crop: "fill" })}
alt="White Marble"
fill
className="object-cover opacity-20"
/>
<div className="relative z-10 max-w-2xl text-center px-6">
<h4 className="text-turquoise uppercase tracking-[0.4em] text-sm font-bold mb-8">Bodrum Tradition</h4>
<p className="text-4xl font-serif italic text-bodrum-blue leading-normal">
{lang === "tr"
? "&ldquo;Güneşin sıcaklığı ve suyun huzuru burada birleşiyor.&rdquo;"
: "&ldquo;Where the warmth of the sun and the peace of the water unite.&rdquo;"}
</p>
</div>
</section>
</div>
);
}