- Rename /news route to /blog across all pages, links, and sitemap - Fix hardcoded English text in Experiences, QuoteSection, and Footer by wiring all content through the TR/EN dictionaries - Clean up Footer: remove non-existent page links, add contact column - Update contact info: new phone numbers, bilgi@ email, No:71 address, Instagram @ayrisapart link - Add suppressHydrationWarning to <body> to silence browser-extension attribute mismatch errors - Add sizes prop to all fill-mode Next.js Image components Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
168 lines
5.3 KiB
TypeScript
168 lines
5.3 KiB
TypeScript
'use client'
|
|
|
|
import { motion, AnimatePresence } from 'framer-motion'
|
|
import { useState, useEffect } from 'react'
|
|
import Link from 'next/link'
|
|
import Image from 'next/image'
|
|
|
|
const heroSlots = [
|
|
{
|
|
srcs: [
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641696/ayrisapart/hero/1_cxfvrw.jpg',
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641696/ayrisapart/hero/9_vym2pc.jpg',
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641696/ayrisapart/hero/8_kjih1z.jpg',
|
|
],
|
|
rotate: 15,
|
|
top: '5%',
|
|
left: '22%',
|
|
size: 'w-48 h-64 md:w-64 md:h-80',
|
|
delay: 0.2
|
|
},
|
|
{
|
|
srcs: [
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641695/ayrisapart/hero/7_yuouc0.jpg',
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641695/ayrisapart/hero/4_eeyoge.jpg',
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641695/ayrisapart/hero/3_tolqzo.jpg',
|
|
],
|
|
rotate: -15,
|
|
top: '8%',
|
|
left: '58%',
|
|
size: 'w-52 h-64 md:w-72 md:h-80',
|
|
delay: 0.4
|
|
},
|
|
{
|
|
srcs: [
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641695/ayrisapart/hero/2_dfrmwi.jpg',
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641695/ayrisapart/hero/5_mmcnjw.jpg',
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641696/ayrisapart/hero/6_x44c4w.jpg',
|
|
],
|
|
rotate: -8,
|
|
top: '60%',
|
|
left: '18%',
|
|
size: 'w-48 h-60 md:w-64 md:h-72',
|
|
delay: 0.6
|
|
},
|
|
{
|
|
srcs: [
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641695/ayrisapart/hero/4_eeyoge.jpg',
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641695/ayrisapart/hero/5_mmcnjw.jpg',
|
|
'https://res.cloudinary.com/du7xohbct/image/upload/v1776641696/ayrisapart/hero/9_vym2pc.jpg',
|
|
],
|
|
rotate: -8,
|
|
top: '62%',
|
|
left: '62%',
|
|
size: 'w-52 h-64 md:w-72 md:h-88',
|
|
delay: 0.8
|
|
}
|
|
]
|
|
|
|
function FloatingSlot({ slot, idx }: { slot: typeof heroSlots[0], idx: number }) {
|
|
const [currentIdx, setCurrentIdx] = useState(0)
|
|
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
setCurrentIdx((prev) => (prev + 1) % slot.srcs.length)
|
|
}, 4000 + idx * 500)
|
|
return () => clearInterval(interval)
|
|
}, [slot.srcs.length, idx])
|
|
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.8, rotate: 0, y: 50 }}
|
|
animate={{
|
|
opacity: 1,
|
|
scale: 1,
|
|
rotate: slot.rotate,
|
|
y: 0
|
|
}}
|
|
transition={{
|
|
duration: 1.2,
|
|
delay: slot.delay,
|
|
ease: [0.33, 1, 0.68, 1]
|
|
}}
|
|
style={{
|
|
top: slot.top,
|
|
left: slot.left,
|
|
zIndex: 5
|
|
}}
|
|
className={`absolute ${slot.size} hidden md:block overflow-hidden`}
|
|
>
|
|
<AnimatePresence initial={false}>
|
|
<motion.div
|
|
key={currentIdx}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 2, ease: "easeInOut" }}
|
|
className="absolute inset-0"
|
|
>
|
|
<Image
|
|
src={slot.srcs[currentIdx]}
|
|
alt="Luxury Experience"
|
|
fill
|
|
className="object-cover"
|
|
sizes="(max-width: 768px) 100vw, 33vw"
|
|
priority={idx < 2}
|
|
/>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
</motion.div>
|
|
)
|
|
}
|
|
|
|
export default function Hero({ lang, dict }: { lang: string, dict: any }) {
|
|
return (
|
|
<section className="sticky top-30 z-[20] h-screen w-full bg-[#FAF7F0] flex items-center justify-center overflow-hidden">
|
|
{/* BACKGROUND TEXT */}
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.95 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
transition={{ duration: 1.5, ease: [0.33, 1, 0.68, 1] }}
|
|
className="relative z-10 w-full text-center px-4"
|
|
>
|
|
<h1 className="text-[18vw] leading-[0.8] font-serif tracking-[-0.04em] text-[#1F1C19] uppercase select-none drop-shadow-sm">
|
|
{dict.hero.title}
|
|
</h1>
|
|
</motion.div>
|
|
|
|
{/* FLOATING SLOTS */}
|
|
{heroSlots.map((slot, idx) => (
|
|
<FloatingSlot key={idx} slot={slot} idx={idx} />
|
|
))}
|
|
|
|
{/* BOTTOM CONTENT */}
|
|
<div className="absolute bottom-36 right-12 z-30 max-w-sm text-right flex flex-col items-end space-y-6">
|
|
<motion.p
|
|
initial={{ opacity: 0, x: 20 }}
|
|
animate={{ opacity: 1, x: 0 }}
|
|
transition={{ delay: 1.2, duration: 0.8 }}
|
|
className="text-[#1A1A1A]/70 text-[14px] leading-relaxed font-medium"
|
|
>
|
|
{dict.hero.desc}
|
|
</motion.p>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 1.4, duration: 0.8 }}
|
|
>
|
|
<Link
|
|
href={`/${lang}/suites`}
|
|
className="inline-block px-8 py-4 bg-[#1A1A1A] text-white text-[12px] font-bold uppercase tracking-widest hover:bg-[#C88C4B] transition-colors duration-300 rounded-[2px] shadow-lg shadow-black/10"
|
|
>
|
|
{dict.hero.explore}
|
|
</Link>
|
|
</motion.div>
|
|
</div>
|
|
|
|
<motion.div
|
|
animate={{ y: [0, 10, 0] }}
|
|
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
|
|
className="absolute bottom-8 left-1/2 -translate-x-1/2 opacity-30"
|
|
>
|
|
<div className="w-[1px] h-12 bg-[#1A1A1A]" />
|
|
</motion.div>
|
|
</section>
|
|
)
|
|
}
|