first commit

This commit is contained in:
mstfyldz
2026-06-03 03:59:46 +03:00
parent 5882fc12c1
commit bba389b513
30 changed files with 3153 additions and 149 deletions
+94
View File
@@ -0,0 +1,94 @@
"use client";
import { useTranslations } from 'next-intl';
import { motion } from 'framer-motion';
import { Waves, Leaf, Music } from 'lucide-react';
import Image from 'next/image';
const features = [
{ icon: Waves, titleKey: 'card1_title', descKey: 'card1_desc', color: 'text-aqua', bg: 'bg-aqua/10' },
{ icon: Leaf, titleKey: 'card2_title', descKey: 'card2_desc', color: 'text-amber', bg: 'bg-amber/10' },
{ icon: Music, titleKey: 'card3_title', descKey: 'card3_desc', color: 'text-coral', bg: 'bg-coral/10' },
];
export default function About() {
const t = useTranslations('About');
return (
<section id="about" className="py-28 bg-sand text-midnight relative">
<div className="container mx-auto px-6 lg:px-12">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-20 items-center">
{/* Left content */}
<motion.div
initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="space-y-8"
>
<div>
<span className="text-xs tracking-[0.4em] uppercase font-black text-coral mb-4 block">
Beach &amp; More
</span>
<h2 className="font-heading text-5xl md:text-6xl font-black leading-tight text-midnight mb-6">
{t('title')}
</h2>
<p className="text-lg leading-relaxed text-midnight/60">{t('description')}</p>
</div>
<div className="space-y-2">
{features.map(({ icon: Icon, titleKey, descKey, color, bg }, i) => (
<motion.div
key={titleKey}
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: i * 0.1 + 0.2 }}
className="flex items-start gap-4 p-4 rounded-2xl hover:bg-sandy/50 transition-colors"
>
<div className={`p-3 rounded-xl ${bg} ${color} flex-shrink-0`}>
<Icon size={20} />
</div>
<div>
<h3 className="font-heading text-lg font-bold mb-0.5">{t(titleKey)}</h3>
<p className="text-midnight/55 text-sm leading-relaxed">{t(descKey)}</p>
</div>
</motion.div>
))}
</div>
</motion.div>
{/* Right image */}
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.8 }}
className="relative"
>
{/* Glow ring */}
<div
className="absolute inset-0 rounded-3xl opacity-30 blur-2xl scale-105"
style={{ background: 'linear-gradient(135deg, #FF5A36, #FFA827, #00BFA5)' }}
/>
<div className="relative h-[560px] w-full rounded-3xl overflow-hidden shadow-2xl">
<Image
src="https://picsum.photos/800/1100?random=2"
alt="Kozmos atmosferi"
fill
sizes="(max-width: 1024px) 100vw, 50vw"
className="object-cover"
/>
<div
className="absolute inset-0"
style={{ background: 'linear-gradient(to top, rgba(8,13,24,0.35) 0%, transparent 55%)' }}
/>
</div>
</motion.div>
</div>
</div>
</section>
);
}
+149
View File
@@ -0,0 +1,149 @@
"use client";
import { useTranslations } from 'next-intl';
import { motion } from 'framer-motion';
import Image from 'next/image';
import { Users, Check, Sparkles } from 'lucide-react';
export default function Accommodation() {
const t = useTranslations('Accommodation');
const openWhatsApp = () => {
const phone = process.env.NEXT_PUBLIC_WHATSAPP_NUMBER || '905000000000';
window.open(`https://wa.me/${phone}`, '_blank');
};
const rooms = [
{
id: 'standard',
image: 'https://picsum.photos/600/400?random=3',
capacity: 2,
features: t.raw('rooms.standard.features') as string[],
featured: false,
gradient: 'linear-gradient(135deg, #00BFA5, #00E5FF)',
},
{
id: 'sea_view',
image: 'https://picsum.photos/600/400?random=4',
capacity: 2,
features: t.raw('rooms.sea_view.features') as string[],
featured: true,
gradient: 'linear-gradient(135deg, #FF5A36, #FFA827)',
},
{
id: 'garden_suite',
image: 'https://picsum.photos/600/400?random=5',
capacity: 4,
features: t.raw('rooms.garden_suite.features') as string[],
featured: false,
gradient: 'linear-gradient(135deg, #00BFA5, #00E5FF)',
},
];
return (
<section id="accommodation" className="py-28 bg-sandy/25 text-midnight">
<div className="container mx-auto px-6 lg:px-12">
<div className="text-center max-w-3xl mx-auto mb-16">
<motion.span
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-xs tracking-[0.4em] uppercase font-black text-coral mb-4 block"
>
Konaklama
</motion.span>
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="font-heading text-5xl md:text-6xl font-black mb-6 text-midnight"
>
{t('title')}
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 }}
className="text-lg text-midnight/60"
>
{t('description')}
</motion.p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{rooms.map((room, index) => (
<motion.div
key={room.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1, duration: 0.6 }}
className={`relative rounded-3xl overflow-hidden bg-white shadow-lg hover:shadow-2xl transition-all duration-500 hover:-translate-y-1 ${
room.featured ? 'outline outline-2 outline-offset-2 outline-coral' : ''
}`}
>
{/* Featured badge */}
{room.featured && (
<div
className="absolute top-4 right-4 z-10 flex items-center gap-1.5 px-3 py-1.5 rounded-full text-[11px] font-black text-white tracking-wider"
style={{ background: 'linear-gradient(135deg, #FF5A36, #FFA827)' }}
>
<Sparkles size={10} />
FEATURED
</div>
)}
{/* Image */}
<div className="relative h-52 w-full overflow-hidden">
<Image
src={room.image}
alt={t(`rooms.${room.id}.name`)}
fill
sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
className="object-cover transition-transform duration-700 hover:scale-110"
/>
<div
className="absolute inset-0"
style={{ background: 'linear-gradient(to top, rgba(8,13,24,0.5), transparent)' }}
/>
</div>
{/* Content */}
<div className="p-7">
<h3 className="font-heading text-xl font-black mb-2 text-midnight">
{t(`rooms.${room.id}.name`)}
</h3>
<div className="flex items-center gap-2 text-midnight/45 mb-5 text-sm">
<Users size={14} />
<span>
{t('capacity')} {room.capacity} {t('person')}
</span>
</div>
<ul className="space-y-2 mb-7">
{room.features.map((feature, i) => (
<li key={i} className="flex items-center gap-2.5 text-sm text-midnight/65">
<Check size={13} className="text-aqua flex-shrink-0" />
<span>{feature}</span>
</li>
))}
</ul>
<button
onClick={openWhatsApp}
className="w-full py-3.5 rounded-2xl font-black text-sm text-white transition-all duration-300 hover:opacity-90 hover:scale-[1.02]"
style={{ background: room.gradient }}
>
{t('book_whatsapp')}
</button>
</div>
</motion.div>
))}
</div>
</div>
</section>
);
}
+139
View File
@@ -0,0 +1,139 @@
"use client";
import { useTranslations } from 'next-intl';
import { motion } from 'framer-motion';
import Image from 'next/image';
import { Sun } from 'lucide-react';
export default function Beach() {
const t = useTranslations('Beach');
const openWhatsApp = () => {
const phone = process.env.NEXT_PUBLIC_WHATSAPP_NUMBER || '905000000000';
window.open(
`https://wa.me/${phone}?text=Merhaba,%20şezlong%20rezervasyonu%20yaptırmak%20istiyorum.`,
'_blank',
);
};
const stats = [
{ value: '∞', label: 'Şezlong' },
{ value: '7/7', label: 'Açık' },
{ value: 'VIP', label: 'Cabana' },
];
return (
<section id="beach" className="pt-28 pb-36 bg-sand text-midnight relative overflow-hidden">
<div className="container mx-auto px-6 lg:px-12">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
{/* Left masonry photos */}
<div className="grid grid-cols-2 gap-3">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="space-y-3"
>
<div className="relative h-64 rounded-2xl overflow-hidden shadow-md">
<Image
src="https://picsum.photos/400/600?random=10"
alt="Plaj"
fill
sizes="(max-width: 1024px) 50vw, 25vw"
className="object-cover hover:scale-105 transition-transform duration-700"
/>
</div>
<div className="relative h-48 rounded-2xl overflow-hidden shadow-md">
<Image
src="https://picsum.photos/400/400?random=11"
alt="Şezlong"
fill
sizes="(max-width: 1024px) 50vw, 25vw"
className="object-cover hover:scale-105 transition-transform duration-700"
/>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.15 }}
className="space-y-3 pt-8"
>
<div className="relative h-48 rounded-2xl overflow-hidden shadow-md">
<Image
src="https://picsum.photos/400/400?random=12"
alt="Deniz"
fill
sizes="(max-width: 1024px) 50vw, 25vw"
className="object-cover hover:scale-105 transition-transform duration-700"
/>
</div>
<div className="relative h-64 rounded-2xl overflow-hidden shadow-md">
<Image
src="https://picsum.photos/400/600?random=13"
alt="Koy"
fill
sizes="(max-width: 1024px) 50vw, 25vw"
className="object-cover hover:scale-105 transition-transform duration-700"
/>
</div>
</motion.div>
</div>
{/* Right content */}
<motion.div
initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7 }}
className="space-y-7"
>
<div className="inline-flex items-center gap-2 font-black text-amber tracking-wider uppercase text-sm">
<Sun size={18} />
Beach Club
</div>
<h2 className="font-heading text-5xl md:text-6xl font-black text-midnight leading-tight">
{t('title')}
</h2>
<p className="text-lg text-midnight/60 leading-relaxed">{t('description')}</p>
{/* Stats */}
<div className="grid grid-cols-3 gap-4 py-6 border-y border-sandy">
{stats.map(({ value, label }) => (
<div key={label} className="text-center">
<div className="font-heading text-2xl font-black gradient-text-warm">{value}</div>
<div className="text-[11px] text-midnight/45 mt-1 uppercase tracking-wider">{label}</div>
</div>
))}
</div>
{/* Info & CTA */}
<div className="p-6 rounded-2xl bg-sandy/50 border border-sandy">
<p className="text-midnight/70 text-sm font-medium mb-5">{t('reservation_info')}</p>
<button
onClick={openWhatsApp}
className="px-8 py-3.5 rounded-full font-black text-sm text-midnight transition-all duration-300 hover:scale-105 hover:shadow-lg"
style={{ background: 'linear-gradient(135deg, #FFA827, #FFD166)' }}
>
{t('book_lounger')}
</button>
</div>
</motion.div>
</div>
</div>
{/* Wave → midnight */}
<div className="absolute bottom-0 left-0 w-full leading-none pointer-events-none">
<svg viewBox="0 0 1440 64" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" className="w-full h-16">
<path d="M0,32 C240,64 480,0 720,32 C960,64 1200,0 1440,32 L1440,64 L0,64 Z" fill="#080D18" />
</svg>
</div>
</section>
);
}
+200
View File
@@ -0,0 +1,200 @@
"use client";
import { useTranslations } from 'next-intl';
import { motion } from 'framer-motion';
import { MapPin, Phone, Send } from 'lucide-react';
import { Instagram } from '@/components/InstagramIcon';
import { useState } from 'react';
export default function Contact() {
const t = useTranslations('Contact');
const [formData, setFormData] = useState({ name: '', date: '', guests: '', message: '' });
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const phone = process.env.NEXT_PUBLIC_WHATSAPP_NUMBER || '905000000000';
const text = `*Yeni Rezervasyon Talebi*%0A%0A*Ad Soyad:* ${formData.name}%0A*Tarih:* ${formData.date}%0A*Kişi Sayısı:* ${formData.guests}%0A*Mesaj:* ${formData.message}`;
window.open(`https://wa.me/${phone}?text=${text}`, '_blank');
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const contactItems = [
{
Icon: MapPin,
label: 'Adres',
value: t('address'),
color: 'text-coral',
bg: 'bg-coral/10',
},
{
Icon: Phone,
label: 'Telefon / WhatsApp',
value: `+${process.env.NEXT_PUBLIC_WHATSAPP_NUMBER || '90 500 000 00 00'}`,
color: 'text-aqua',
bg: 'bg-aqua/10',
},
{
Icon: Instagram,
label: 'Instagram',
value: '@kozmosbeach',
href: 'https://www.instagram.com/kozmosbeach/',
color: 'text-amber',
bg: 'bg-amber/10',
},
];
const inputClass =
'w-full bg-sand border border-sandy rounded-xl px-4 py-3 text-sm focus:outline-none focus:border-coral focus:ring-1 focus:ring-coral transition-all placeholder:text-midnight/30';
return (
<section id="contact" className="py-28 bg-sandy/25 text-midnight">
<div className="container mx-auto px-6 lg:px-12">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-center mb-16"
>
<span className="text-xs tracking-[0.4em] uppercase font-black text-coral mb-4 block">
Bize Ulaşın
</span>
<h2 className="font-heading text-5xl md:text-6xl font-black text-midnight">
{t('title')}
</h2>
</motion.div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16">
{/* Left: info + form */}
<motion.div
initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
className="space-y-8"
>
{/* Contact info */}
<div className="space-y-5">
{contactItems.map(({ Icon, label, value, href, color, bg }) => (
<div key={label} className="flex items-start gap-4">
<div className={`p-3 rounded-xl ${bg} ${color} flex-shrink-0`}>
<Icon size={19} />
</div>
<div>
<p className="font-bold text-sm text-midnight/50 mb-0.5">{label}</p>
{href ? (
<a
href={href}
target="_blank"
rel="noreferrer"
className={`${color} font-medium hover:underline`}
>
{value}
</a>
) : (
<p className="text-midnight font-medium">{value}</p>
)}
</div>
</div>
))}
</div>
{/* Form */}
<form
onSubmit={handleSubmit}
className="bg-white p-8 rounded-3xl shadow-sm border border-sandy space-y-5"
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<div className="space-y-1.5">
<label className="text-[11px] font-black uppercase tracking-wider text-midnight/45">
{t('form.name')}
</label>
<input
required
type="text"
name="name"
value={formData.name}
onChange={handleChange}
className={inputClass}
/>
</div>
<div className="space-y-1.5">
<label className="text-[11px] font-black uppercase tracking-wider text-midnight/45">
{t('form.date')}
</label>
<input
required
type="date"
name="date"
value={formData.date}
onChange={handleChange}
className={inputClass}
/>
</div>
</div>
<div className="space-y-1.5">
<label className="text-[11px] font-black uppercase tracking-wider text-midnight/45">
{t('form.guests')}
</label>
<input
required
type="number"
min="1"
name="guests"
value={formData.guests}
onChange={handleChange}
className={inputClass}
/>
</div>
<div className="space-y-1.5">
<label className="text-[11px] font-black uppercase tracking-wider text-midnight/45">
{t('form.message')}
</label>
<textarea
rows={3}
name="message"
value={formData.message}
onChange={handleChange}
className={inputClass + ' resize-none'}
/>
</div>
<button
type="submit"
className="w-full py-4 rounded-2xl font-black text-sm text-white flex items-center justify-center gap-2 hover:opacity-90 hover:scale-[1.01] transition-all shadow-md"
style={{ background: 'linear-gradient(135deg, #FF5A36, #FFA827)' }}
>
<Send size={15} />
{t('form.submit')}
</button>
</form>
</motion.div>
{/* Right: map */}
<motion.div
initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
className="min-h-[420px] lg:min-h-[600px] w-full rounded-3xl overflow-hidden shadow-2xl"
>
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d12754.717654763133!2d28.3144!3d36.9507!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x14bf76ccf312ba37%3A0xcdaaa97b102b4bb7!2sAkyaka%2C%20Ula%2FMu%C4%9Fla!5e0!3m2!1str!2str!4v1700000000000!5m2!1str!2str"
width="100%"
height="100%"
style={{ border: 0 }}
allowFullScreen
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
/>
</motion.div>
</div>
</div>
</section>
);
}
+116
View File
@@ -0,0 +1,116 @@
"use client";
import { useTranslations } from 'next-intl';
import { motion } from 'framer-motion';
import { Utensils, Wine, Coffee, ArrowRight } from 'lucide-react';
const CARDS = [
{ id: 'seafood', icon: Utensils, accent: '#FF5A36' },
{ id: 'cocktails', icon: Wine, accent: '#00BFA5' },
{ id: 'breakfast', icon: Coffee, accent: '#FFA827' },
];
export default function Dining() {
const t = useTranslations('Dining');
const menuUrl = process.env.NEXT_PUBLIC_MENU_URL || '#';
return (
<section
id="dining"
className="pt-20 pb-28 text-white relative overflow-hidden"
style={{ background: '#080D18' }}
>
{/* Background blobs */}
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<div
className="absolute bottom-0 left-1/4 w-[500px] h-[500px] rounded-full opacity-20"
style={{ background: 'radial-gradient(circle, #FF5A36 0%, transparent 70%)' }}
/>
<div
className="absolute top-0 right-1/4 w-[400px] h-[400px] rounded-full opacity-15"
style={{ background: 'radial-gradient(circle, #00BFA5 0%, transparent 70%)' }}
/>
</div>
<div className="container mx-auto px-6 lg:px-12 relative z-10">
<div className="text-center max-w-3xl mx-auto mb-16">
<motion.span
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-xs tracking-[0.4em] uppercase font-black text-amber mb-4 block"
>
Yeme &amp; İçme
</motion.span>
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="font-heading text-5xl md:text-6xl font-black mb-6"
>
<span className="gradient-text">{t('title')}</span>
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 }}
className="text-lg text-white/50 mb-8"
>
{t('description')}
</motion.p>
<motion.a
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.2 }}
href={menuUrl}
target={menuUrl !== '#' ? '_blank' : '_self'}
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-8 py-3.5 rounded-full font-black text-sm text-midnight transition-all hover:scale-105"
style={{ background: 'linear-gradient(135deg, #FFA827, #FFD166)' }}
>
{t('view_menu')}
<ArrowRight size={15} />
</motion.a>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{CARDS.map(({ id, icon: Icon, accent }, index) => (
<motion.div
key={id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1 + 0.2 }}
className="group relative p-8 rounded-3xl border border-white/10 hover:border-white/25 transition-all duration-300"
style={{ background: 'rgba(255,255,255,0.04)' }}
>
{/* Icon */}
<div
className="w-14 h-14 rounded-2xl mb-6 flex items-center justify-center"
style={{ background: `${accent}18`, color: accent }}
>
<Icon size={26} />
</div>
<h3 className="font-heading text-2xl font-black mb-3 text-white">
{t(`cards.${id}.title`)}
</h3>
<p className="text-white/45 text-sm leading-relaxed">{t(`cards.${id}.desc`)}</p>
{/* Bottom accent line on hover */}
<div
className="absolute bottom-0 left-8 right-8 h-[1px] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-full"
style={{ background: `linear-gradient(90deg, transparent, ${accent}, transparent)` }}
/>
</motion.div>
))}
</div>
</div>
</section>
);
}
+366
View File
@@ -0,0 +1,366 @@
"use client";
import { useTranslations } from 'next-intl';
import { motion } from 'framer-motion';
import { Calendar, Music, Disc3, CalendarDays, Zap } from 'lucide-react';
/* ── static data ───────────────────────────────── */
const WEEK_DAYS = ['Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt', 'Paz'];
const ACTIVE_DAYS: Record<number, string> = {
3: '#FF5A36',
4: '#00E5FF',
5: '#00E5FF',
};
/* ── sub-components ────────────────────────────── */
function VinylDisc() {
return (
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 4, repeat: Infinity, ease: 'linear' }}
className="w-36 h-36 md:w-44 md:h-44 rounded-full relative flex-shrink-0"
style={{
background:
'repeating-conic-gradient(#0a0f1e 0deg 18deg, #141e30 18deg 36deg)',
boxShadow: '0 0 48px rgba(0,229,255,0.35), 0 0 80px rgba(0,229,255,0.12)',
}}
>
{/* Grooves */}
{[55, 45, 35].map((size) => (
<div
key={size}
className="absolute rounded-full border border-white/[0.04]"
style={{ inset: `${(100 - size) / 2}%` }}
/>
))}
{/* Label */}
<div
className="absolute inset-[35%] rounded-full flex items-center justify-center"
style={{ background: 'radial-gradient(circle, #00E5FF, #0084A8)' }}
>
<div className="w-2 h-2 rounded-full bg-white/80" />
</div>
</motion.div>
);
}
function EqBars({ color }: { color: string }) {
const heights = [0.4, 0.8, 0.5, 1, 0.65, 0.9, 0.45, 0.75, 0.55, 0.85];
return (
<div className="flex items-end gap-[3px] h-8">
{heights.map((h, i) => (
<motion.div
key={i}
className="w-1.5 rounded-full origin-bottom"
style={{ background: color, height: `${h * 100}%` }}
animate={{ scaleY: [h, h > 0.6 ? 0.2 : 1, h] }}
transition={{
duration: 0.5 + Math.random() * 0.4,
repeat: Infinity,
repeatType: 'mirror',
delay: i * 0.07,
ease: 'easeInOut',
}}
/>
))}
</div>
);
}
function WineGlassIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M8 22h8" />
<path d="M12 15v7" />
<path d="M12 15a5 5 0 0 0 5-5c0-2-.5-4-2-8H9c-1.5 4-2 6-2 8a5 5 0 0 0 5 5Z" />
<path d="M7 10h10" />
</svg>
);
}
/* ── main component ────────────────────────────── */
export default function Events() {
const t = useTranslations('Events');
return (
<section
id="events"
className="pt-28 pb-36 text-white relative overflow-hidden"
style={{ background: '#080D18' }}
>
{/* Aurora background blobs */}
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<div
className="absolute top-[-20%] left-[-10%] w-[600px] h-[600px] rounded-full opacity-[0.12]"
style={{ background: 'radial-gradient(circle, #00E5FF 0%, transparent 70%)' }}
/>
<div
className="absolute bottom-[-10%] right-[-5%] w-[500px] h-[500px] rounded-full opacity-[0.1]"
style={{ background: 'radial-gradient(circle, #FF5A36 0%, transparent 70%)' }}
/>
<div
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[700px] h-[700px] rounded-full opacity-[0.05]"
style={{ background: 'radial-gradient(circle, #FFA827 0%, transparent 70%)' }}
/>
</div>
{/* Fine dot grid */}
<div
className="absolute inset-0 opacity-[0.035] pointer-events-none"
style={{
backgroundImage: 'radial-gradient(circle, rgba(255,255,255,0.9) 1px, transparent 1px)',
backgroundSize: '36px 36px',
}}
/>
<div className="container mx-auto px-6 lg:px-12 relative z-10 space-y-10">
{/* ── Header ───────────────────────────────── */}
<div className="flex flex-col lg:flex-row lg:items-end justify-between gap-6">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
>
<div className="flex items-center gap-2 mb-4">
<Zap size={13} className="text-coral" />
<span className="text-xs tracking-[0.4em] uppercase font-black text-coral">
Live Events
</span>
</div>
<h2 className="font-heading text-5xl md:text-6xl font-black leading-tight">
{t('title')}
</h2>
<p className="text-white/45 mt-3 text-base max-w-md leading-relaxed">
{t('description')}
</p>
</motion.div>
<motion.button
initial={{ opacity: 0, x: 20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
className="self-start lg:self-auto flex items-center gap-2 px-7 py-3.5 rounded-full font-black text-sm text-white flex-shrink-0 transition-all hover:scale-105"
style={{ background: 'linear-gradient(135deg, #FF5A36, #FFA827)' }}
>
<CalendarDays size={15} />
{t('view_calendar')}
</motion.button>
</div>
{/* ── Weekly schedule bar ───────────────────── */}
<motion.div
initial={{ opacity: 0, y: 12 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="flex items-center gap-2 md:gap-4 bg-white/[0.04] rounded-2xl px-6 py-4 border border-white/[0.07]"
>
{WEEK_DAYS.map((day, i) => {
const accent = ACTIVE_DAYS[i];
const isActive = !!accent;
return (
<div key={day} className="flex-1 flex flex-col items-center gap-2">
<span
className={`text-[10px] font-black uppercase tracking-wider ${
isActive ? 'text-white' : 'text-white/25'
}`}
>
{day}
</span>
<div
className={`w-1.5 h-1.5 rounded-full transition-all ${isActive ? 'scale-125' : ''}`}
style={{
background: isActive ? accent : 'rgba(255,255,255,0.12)',
boxShadow: isActive ? `0 0 8px ${accent}` : 'none',
}}
/>
</div>
);
})}
</motion.div>
{/* ── Featured Event: DJ Nights ────────────── */}
<motion.div
initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.7 }}
className="border-spin-cyan rounded-3xl p-px overflow-hidden"
>
<div
className="rounded-3xl p-8 md:p-10 flex flex-col md:flex-row items-center gap-8 overflow-hidden relative"
style={{ background: '#0C1828' }}
>
{/* Glow center */}
<div
className="absolute inset-0 pointer-events-none"
style={{
background:
'radial-gradient(ellipse at 80% 50%, rgba(0,229,255,0.08) 0%, transparent 60%)',
}}
/>
{/* Left: text */}
<div className="flex-1 relative z-10">
<div className="flex items-center gap-3 mb-5">
<span
className="text-[10px] font-black uppercase tracking-[0.35em] px-3 py-1.5 rounded-full"
style={{ background: '#00E5FF1A', color: '#00E5FF' }}
>
EN POPÜLER
</span>
<div className="flex items-center gap-1.5 text-white/40 text-xs">
<Calendar size={11} />
<span>{t('cards.dj.time')}</span>
</div>
</div>
<h3 className="font-heading text-4xl md:text-5xl font-black mb-3 text-white leading-tight">
{t('cards.dj.title')}
</h3>
<div className="flex items-center gap-3 mb-7">
<Disc3 size={18} style={{ color: '#00E5FF' }} />
<span className="text-white/50 text-sm">Open-air · Beach Stage</span>
</div>
<EqBars color="#00E5FF" />
</div>
{/* Right: vinyl */}
<div className="relative z-10 flex-shrink-0">
<VinylDisc />
</div>
</div>
</motion.div>
{/* ── Secondary events: 2-col grid ─────────── */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
{/* Rakı Geceleri */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 }}
className="border-spin-coral rounded-3xl p-px group cursor-pointer"
>
<div
className="rounded-3xl p-7 h-full flex flex-col gap-5 relative overflow-hidden transition-colors duration-300 group-hover:bg-[#110a0a]"
style={{ background: '#0C0808' }}
>
<div
className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-400 pointer-events-none"
style={{
background:
'radial-gradient(ellipse at 20% 50%, rgba(255,90,54,0.1) 0%, transparent 65%)',
}}
/>
<div className="flex items-start justify-between">
<div
className="w-12 h-12 rounded-2xl flex items-center justify-center"
style={{ background: '#FF5A361A', color: '#FF5A36' }}
>
<WineGlassIcon />
</div>
<span
className="text-[10px] font-black uppercase tracking-wider px-2.5 py-1 rounded-full"
style={{ background: '#FF5A361A', color: '#FF5A36' }}
>
PER
</span>
</div>
<div>
<h3 className="font-heading text-2xl font-black text-white mb-2">
{t('cards.raki.title')}
</h3>
<div className="flex items-center gap-1.5 text-sm text-white/40">
<Calendar size={12} />
<span>{t('cards.raki.time')}</span>
</div>
</div>
<div className="mt-auto opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<EqBars color="#FF5A36" />
</div>
</div>
</motion.div>
{/* Canlı Müzik */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.2 }}
className="border-spin-amber rounded-3xl p-px group cursor-pointer"
>
<div
className="rounded-3xl p-7 h-full flex flex-col gap-5 relative overflow-hidden transition-colors duration-300 group-hover:bg-[#110d04]"
style={{ background: '#0C0A04' }}
>
<div
className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-400 pointer-events-none"
style={{
background:
'radial-gradient(ellipse at 80% 50%, rgba(255,168,39,0.1) 0%, transparent 65%)',
}}
/>
<div className="flex items-start justify-between">
<div
className="w-12 h-12 rounded-2xl flex items-center justify-center"
style={{ background: '#FFA8271A', color: '#FFA827' }}
>
<Music size={22} />
</div>
<span
className="text-[10px] font-black uppercase tracking-wider px-2.5 py-1 rounded-full"
style={{ background: '#FFA8271A', color: '#FFA827' }}
>
ÖZEL
</span>
</div>
<div>
<h3 className="font-heading text-2xl font-black text-white mb-2">
{t('cards.live_music.title')}
</h3>
<div className="flex items-center gap-1.5 text-sm text-white/40">
<Calendar size={12} />
<span>{t('cards.live_music.time')}</span>
</div>
</div>
<div className="mt-auto opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<EqBars color="#FFA827" />
</div>
</div>
</motion.div>
</div>
</div>
{/* Wave → sand */}
<div className="absolute bottom-0 left-0 w-full leading-none pointer-events-none">
<svg viewBox="0 0 1440 64" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" className="w-full h-16">
<path d="M0,20 C360,64 720,0 1080,40 C1260,56 1380,10 1440,20 L1440,64 L0,64 Z" fill="#FFF8EE" />
</svg>
</div>
</section>
);
}
+94
View File
@@ -0,0 +1,94 @@
"use client";
import { useTranslations } from 'next-intl';
import { Instagram } from '@/components/InstagramIcon';
import { Link, usePathname } from '@/i18n/routing';
export default function Footer() {
const t = useTranslations('Footer');
const tNav = useTranslations('Navbar');
const pathname = usePathname();
const navLinks = [
{ href: pathname === '/' ? '#about' : '/#about', label: tNav('about') },
{ href: '/accommodation', label: tNav('accommodation') },
{ href: pathname === '/' ? '#beach' : '/#beach', label: tNav('beach') },
{ href: pathname === '/' ? '#dining' : '/#dining', label: tNav('dining') },
{ href: pathname === '/' ? '#events' : '/#events', label: tNav('events') },
{ href: pathname === '/' ? '#gallery' : '/#gallery', label: tNav('gallery') },
{ href: pathname === '/' ? '#contact' : '/#contact', label: tNav('contact') },
];
return (
<footer
className="text-white py-16 relative overflow-hidden"
style={{ background: '#080D18' }}
>
{/* Rainbow top border */}
<div
className="absolute top-0 left-0 right-0 h-[1px]"
style={{
background:
'linear-gradient(90deg, transparent 0%, #FF5A36 25%, #FFA827 50%, #00BFA5 75%, transparent 100%)',
}}
/>
<div className="container mx-auto px-6 lg:px-12">
<div className="flex flex-col md:flex-row justify-between items-start gap-12 mb-12">
{/* Brand */}
<div>
<Link href="/" className="inline-flex flex-col mb-3">
<span className="font-heading text-5xl font-black uppercase tracking-tighter gradient-text-warm">
Kozmos
</span>
<span className="text-[10px] tracking-[0.5em] font-body uppercase text-white/30 mt-0.5">
Beach &amp; More
</span>
</Link>
<p className="text-white/35 text-sm mt-1">{t('tagline')}</p>
</div>
{/* Nav links */}
<div className="flex flex-wrap gap-x-7 gap-y-3">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-sm text-white/40 hover:text-white transition-colors"
>
{link.label}
</Link>
))}
</div>
{/* Social */}
<a
href="https://www.instagram.com/kozmosbeach/"
target="_blank"
rel="noreferrer"
className="w-12 h-12 rounded-2xl border border-white/10 flex items-center justify-center text-white/40 hover:border-coral hover:text-coral transition-all"
>
<Instagram size={19} />
</a>
</div>
<div className="pt-8 border-t border-white/10 flex flex-col md:flex-row justify-between items-center gap-4 text-xs text-white/25">
<div>{t('rights')}</div>
<div>
Created by{' '}
<a
href="https://ayris.tech"
target="_blank"
rel="noopener noreferrer"
className="text-white/40 hover:text-coral transition-colors"
>
ayris.tech
</a>
</div>
</div>
</div>
</footer>
);
}
+162
View File
@@ -0,0 +1,162 @@
"use client";
import { useTranslations } from 'next-intl';
import { motion, AnimatePresence } from 'framer-motion';
import { Instagram } from '@/components/InstagramIcon';
import { useState, useEffect } from 'react';
import { X, ChevronLeft, ChevronRight } from 'lucide-react';
export default function Gallery() {
const t = useTranslations('Gallery');
const images = Array.from({ length: 12 }).map((_, i) => ({
id: i,
src: `https://picsum.photos/600/${400 + (i % 5) * 50}?random=${30 + i}`,
alt: `Gallery ${i + 1}`,
}));
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
// Close lightbox on escape key
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') setSelectedIndex(null);
if (e.key === 'ArrowRight' && selectedIndex !== null) {
setSelectedIndex((prev) => (prev !== null ? (prev + 1) % images.length : null));
}
if (e.key === 'ArrowLeft' && selectedIndex !== null) {
setSelectedIndex((prev) => (prev !== null ? (prev - 1 + images.length) % images.length : null));
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [selectedIndex, images.length]);
return (
<section id="gallery" className="py-24 bg-white text-midnight">
<div className="container mx-auto px-6 lg:px-12">
{/* Header */}
<div className="flex flex-col md:flex-row justify-between items-end mb-10">
<motion.div
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
>
<span className="text-xs tracking-[0.4em] uppercase font-black text-coral mb-3 block">
Fotoğraflar
</span>
<h2 className="font-heading text-5xl md:text-6xl font-black text-midnight">
{t('title')}
</h2>
</motion.div>
<motion.a
initial={{ opacity: 0, x: 20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
href="https://www.instagram.com/kozmosbeach/"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 px-5 py-2.5 rounded-full border border-coral/30 text-coral font-bold text-sm hover:bg-coral hover:text-white transition-all"
>
<Instagram size={15} />
{t('follow_instagram')}
</motion.a>
</div>
{/* Masonry grid */}
<div className="columns-1 sm:columns-2 md:columns-3 lg:columns-4 gap-3 space-y-3">
{images.map((img, index) => (
<motion.div
key={img.id}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: (index % 4) * 0.07 }}
onClick={() => setSelectedIndex(index)}
className="relative rounded-2xl overflow-hidden group break-inside-avoid cursor-pointer"
>
<img
src={img.src}
alt={img.alt}
className="w-full h-auto object-cover transition-transform duration-700 group-hover:scale-105"
loading="lazy"
/>
{/* Coral gradient overlay */}
<div
className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
style={{
background:
'linear-gradient(135deg, rgba(255,90,54,0.55), rgba(0,191,165,0.4))',
}}
/>
{/* Plus icon */}
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<span className="text-white text-4xl font-thin select-none">+</span>
</div>
</motion.div>
))}
</div>
</div>
{/* Lightbox Overlay */}
<AnimatePresence>
{selectedIndex !== null && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-[100] flex items-center justify-center bg-midnight/95 backdrop-blur-md"
>
{/* Close Button */}
<button
onClick={() => setSelectedIndex(null)}
className="absolute top-6 right-6 z-[110] text-white/70 hover:text-white transition-colors"
>
<X size={36} />
</button>
{/* Left/Right Navigation */}
<button
onClick={(e) => {
e.stopPropagation();
setSelectedIndex((selectedIndex - 1 + images.length) % images.length);
}}
className="absolute left-4 md:left-10 z-[110] p-3 rounded-full bg-white/10 text-white hover:bg-white/20 transition-all"
>
<ChevronLeft size={30} />
</button>
<button
onClick={(e) => {
e.stopPropagation();
setSelectedIndex((selectedIndex + 1) % images.length);
}}
className="absolute right-4 md:right-10 z-[110] p-3 rounded-full bg-white/10 text-white hover:bg-white/20 transition-all"
>
<ChevronRight size={30} />
</button>
{/* Image */}
<motion.div
key={selectedIndex}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ type: "spring", damping: 25, stiffness: 300 }}
className="relative max-w-5xl w-full h-[80vh] px-4 md:px-24"
onClick={(e) => e.stopPropagation()}
>
<img
src={images[selectedIndex].src}
alt={images[selectedIndex].alt}
className="w-full h-full object-contain drop-shadow-2xl"
/>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</section>
);
}
+156
View File
@@ -0,0 +1,156 @@
"use client";
import { useTranslations } from 'next-intl';
import { motion } from 'framer-motion';
import { ChevronDown, MapPin } from 'lucide-react';
export default function Hero() {
const t = useTranslations('Hero');
const openWhatsApp = () => {
const phone = process.env.NEXT_PUBLIC_WHATSAPP_NUMBER || '905000000000';
window.open(`https://wa.me/${phone}`, '_blank');
};
return (
<section
id="hero"
className="relative w-full h-screen flex items-center justify-center overflow-hidden"
style={{ background: '#080D18' }}
>
{/* ── Ambient gradient blobs ───────────────────── */}
<div className="absolute inset-0 overflow-hidden">
<motion.div
animate={{ x: [0, 60, -20, 0], y: [0, -50, 30, 0], scale: [1, 1.15, 0.95, 1] }}
transition={{ duration: 28, repeat: Infinity, ease: 'easeInOut' }}
className="absolute -top-56 -left-56 w-[700px] h-[700px] rounded-full"
style={{ background: 'radial-gradient(circle, rgba(255,90,54,0.28) 0%, transparent 65%)' }}
/>
<motion.div
animate={{ x: [0, -80, 50, 0], y: [0, 70, -30, 0], scale: [1, 0.85, 1.2, 1] }}
transition={{ duration: 32, repeat: Infinity, ease: 'easeInOut', delay: 6 }}
className="absolute -bottom-40 -right-40 w-[600px] h-[600px] rounded-full"
style={{ background: 'radial-gradient(circle, rgba(0,191,165,0.22) 0%, transparent 65%)' }}
/>
<motion.div
animate={{ x: [0, 100, -60, 0], y: [0, -80, 50, 0], scale: [1, 1.3, 0.85, 1] }}
transition={{ duration: 22, repeat: Infinity, ease: 'easeInOut', delay: 12 }}
className="absolute top-1/2 right-1/3 w-[400px] h-[400px] rounded-full"
style={{ background: 'radial-gradient(circle, rgba(255,168,39,0.18) 0%, transparent 65%)' }}
/>
</div>
{/* Grain overlay */}
<div
className="absolute inset-0 z-[1] opacity-[0.035] pointer-events-none"
style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")`,
}}
/>
{/* ── Content ──────────────────────────────────── */}
<div className="relative z-10 container mx-auto px-6 flex flex-col items-center text-center">
{/* Location */}
<motion.p
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7 }}
className="text-[11px] tracking-[0.45em] uppercase text-coral font-bold mb-10"
>
Akyaka &nbsp;·&nbsp; Gökova Körfezi
</motion.p>
{/* Brand name — static gradient, no shimmer */}
<motion.h1
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.9, delay: 0.15 }}
className="font-heading font-black uppercase tracking-tight leading-none gradient-text-warm mb-4"
style={{ fontSize: 'clamp(3rem, 8vw, 6.5rem)' }}
>
KOZMOS
</motion.h1>
{/* Thin divider */}
<motion.div
initial={{ scaleX: 0 }}
animate={{ scaleX: 1 }}
transition={{ duration: 0.8, delay: 0.35 }}
className="w-16 h-px mb-5"
style={{ background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent)' }}
/>
{/* Subtitle */}
<motion.p
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.45 }}
className="text-sm md:text-base tracking-[0.55em] uppercase text-white/40 font-medium mb-6"
>
Beach &amp; More
</motion.p>
{/* Tagline */}
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.6, delay: 0.6 }}
className="text-white/55 text-sm md:text-base max-w-xs leading-relaxed mb-12"
>
{t('tagline')}
</motion.p>
{/* CTAs */}
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.75 }}
className="flex flex-col sm:flex-row gap-3"
>
<button
onClick={openWhatsApp}
className="px-8 py-3.5 rounded-full font-bold text-sm tracking-wider text-white transition-all duration-300 hover:scale-105 hover:shadow-[0_0_32px_rgba(255,90,54,0.45)]"
style={{ background: 'linear-gradient(135deg, #FF5A36, #FFA827)' }}
>
{t('cta_reservation')}
</button>
<a
href="#contact"
className="flex items-center justify-center gap-2 px-7 py-3.5 rounded-full text-sm tracking-wider font-medium text-white/70 border border-white/15 hover:border-white/35 hover:text-white transition-all duration-300"
>
<MapPin size={14} />
{t('cta_location')}
</a>
</motion.div>
</div>
{/* ── Wave divider ─────────────────────────────── */}
<div className="absolute bottom-0 left-0 w-full z-10 leading-none">
<svg
viewBox="0 0 1440 72"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="none"
className="w-full h-[72px]"
>
<path
d="M0,36 C240,72 480,0 720,36 C960,72 1200,0 1440,36 L1440,72 L0,72 Z"
fill="#FFF8EE"
/>
</svg>
</div>
{/* Scroll cue */}
<motion.div
className="absolute bottom-10 left-1/2 -translate-x-1/2 z-20 text-white/35"
animate={{ y: [0, 7, 0] }}
transition={{ repeat: Infinity, duration: 1.8 }}
>
<a href="#about" aria-label="Scroll down">
<ChevronDown size={24} />
</a>
</motion.div>
</section>
);
}
+22
View File
@@ -0,0 +1,22 @@
import React from 'react';
export function Instagram({ size = 24, className = "" }: { size?: number, className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={className}
>
<rect width="20" height="20" x="2" y="2" rx="5" ry="5" />
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" />
<line x1="17.5" x2="17.51" y1="6.5" y2="6.5" />
</svg>
);
}
+173
View File
@@ -0,0 +1,173 @@
"use client";
import { useTranslations } from 'next-intl';
import { Link, usePathname, useRouter } from '@/i18n/routing';
import { useState, useEffect } from 'react';
import { Menu, X } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
export default function Navbar({ locale }: { locale: string }) {
const t = useTranslations('Navbar');
const pathname = usePathname();
const router = useRouter();
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 50);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const switchLocale = (newLocale: string) => {
router.replace(pathname, { locale: newLocale });
};
const navLinks = [
{ href: pathname === '/' ? '#about' : '/#about', label: t('about') },
{ href: '/accommodation', label: t('accommodation') },
{ href: pathname === '/' ? '#beach' : '/#beach', label: t('beach') },
{ href: pathname === '/' ? '#dining' : '/#dining', label: t('dining') },
{ href: pathname === '/' ? '#events' : '/#events', label: t('events') },
{ href: pathname === '/' ? '#gallery' : '/#gallery', label: t('gallery') },
{ href: pathname === '/' ? '#contact' : '/#contact', label: t('contact') },
];
return (
<nav
className={`fixed top-0 left-0 w-full z-50 transition-all duration-300 ${
isScrolled
? 'bg-white/90 backdrop-blur-lg shadow-sm py-4 border-b border-sandy/40'
: 'bg-transparent py-6'
}`}
>
<div className="container mx-auto px-6 lg:px-12 flex justify-between items-center">
{/* Logo */}
<Link href="/" className="flex flex-col items-start">
<span
className={`font-heading text-2xl font-black uppercase tracking-tighter ${
isScrolled ? 'text-midnight' : 'gradient-text-warm'
}`}
>
Kozmos
</span>
<span
className={`text-[10px] tracking-[0.4em] font-body uppercase ${
isScrolled ? 'text-coral' : 'text-white/40'
}`}
>
Beach &amp; More
</span>
</Link>
{/* Desktop Nav */}
<div className="hidden lg:flex items-center gap-8">
<div className="flex gap-6">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className={`text-sm font-medium tracking-wide transition-colors hover:text-coral ${
isScrolled ? 'text-midnight/70' : 'text-white/80'
}`}
>
{link.label}
</Link>
))}
</div>
<div
className={`flex items-center gap-3 border-l pl-5 ${
isScrolled ? 'border-sandy' : 'border-white/20'
}`}
>
{(['tr', 'en'] as const).map((l) => (
<button
key={l}
onClick={() => switchLocale(l)}
className={`text-[11px] font-black uppercase tracking-wider transition-colors ${
locale === l
? 'text-coral'
: isScrolled
? 'text-midnight/40'
: 'text-white/40'
}`}
>
{l}
</button>
))}
</div>
<a
href="#contact"
className="px-5 py-2.5 rounded-full text-xs font-black text-white tracking-wider transition-all hover:scale-105 hover:shadow-[0_0_24px_rgba(255,90,54,0.45)]"
style={{ background: 'linear-gradient(135deg, #FF5A36, #FFA827)' }}
>
{t('reservation')}
</a>
</div>
{/* Mobile toggle */}
<button
className="lg:hidden"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
aria-label="Toggle menu"
>
{isMobileMenuOpen ? (
<X className={isScrolled ? 'text-midnight' : 'text-white'} size={26} />
) : (
<Menu className={isScrolled ? 'text-midnight' : 'text-white'} size={26} />
)}
</button>
</div>
{/* Mobile menu */}
<AnimatePresence>
{isMobileMenuOpen && (
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="lg:hidden absolute top-full left-0 w-full bg-white/96 backdrop-blur-lg shadow-xl border-t border-sandy/30"
>
<div className="flex flex-col py-6 px-8 gap-3">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-midnight text-base font-heading font-semibold py-2 border-b border-sandy/20 hover:text-coral transition-colors"
onClick={() => setIsMobileMenuOpen(false)}
>
{link.label}
</Link>
))}
<div className="flex gap-4 pt-3">
{(['tr', 'en'] as const).map((l) => (
<button
key={l}
onClick={() => switchLocale(l)}
className={`text-sm font-black uppercase ${locale === l ? 'text-coral' : 'text-midnight/40'}`}
>
{l}
</button>
))}
</div>
<a
href="#contact"
className="mt-2 text-center py-3.5 rounded-full text-sm font-black text-white"
style={{ background: 'linear-gradient(135deg, #FF5A36, #FFA827)' }}
onClick={() => setIsMobileMenuOpen(false)}
>
{t('reservation')}
</a>
</div>
</motion.div>
)}
</AnimatePresence>
</nav>
);
}
+21
View File
@@ -0,0 +1,21 @@
"use client";
import { MessageCircle } from 'lucide-react';
export default function WhatsAppButton() {
const openWhatsApp = () => {
const phone = process.env.NEXT_PUBLIC_WHATSAPP_NUMBER || '905000000000';
window.open(`https://wa.me/${phone}`, '_blank');
};
return (
<button
onClick={openWhatsApp}
className="fixed bottom-6 right-6 z-50 bg-[#25D366] text-white p-4 rounded-full shadow-[0_4px_14px_0_rgba(37,211,102,0.39)] hover:shadow-[0_6px_20px_rgba(37,211,102,0.23)] hover:scale-105 transition-all duration-300 flex items-center justify-center group"
aria-label="WhatsApp ile iletişime geçin"
>
<div className="absolute inset-0 rounded-full bg-[#25D366] animate-ping opacity-20"></div>
<MessageCircle size={28} className="relative z-10" />
</button>
);
}