bca3d83bb4
- Duplicated mobile scroll-pause logic (3s timeout) to homepage Signature Menu (using .signature-card + .relative.h-48 structure, with video support added to signatureDishes). - Updated both /menu and homepage Signature Menu from 2s to 3s pause before playing video on scroll stop. - Added onMouse handlers for desktop hover consistency on signature cards. - Maintains posters while scrolling, plays video after 3s pause on item. - Desktop hover behavior preserved. - Builds cleanly.
509 lines
22 KiB
TypeScript
509 lines
22 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* SHAHI KITCHEN HOMEPAGE
|
|
* Version with advanced Signature Menu + elegant hero (pre full Sultan redesign)
|
|
*/
|
|
|
|
import React, { useEffect, useState } from "react";
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
import Lenis from "lenis";
|
|
import { gsap } from "gsap";
|
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
|
import { Crown, Clock, ArrowRight, UtensilsCrossed } from "lucide-react";
|
|
import Navbar from "@/components/Navbar";
|
|
import Footer from "@/components/Footer";
|
|
import { useCart } from "@/components/CartContext";
|
|
import { useLanguage } from "@/lib/language-context";
|
|
import { getTranslation } from "@/lib/translations";
|
|
|
|
gsap.registerPlugin(ScrollTrigger);
|
|
|
|
export default function ShahiKitchenHomepage() {
|
|
const { addToCart } = useCart();
|
|
const { language } = useLanguage();
|
|
const t = getTranslation(language);
|
|
|
|
const [menuFilter, setMenuFilter] = useState<"All" | "Rice" | "Curry" | "Meat" | "Street" | "Roll" | "Sweet">("All");
|
|
const [isMobile, setIsMobile] = useState(false);
|
|
|
|
// Mobile video helpers for Signature Menu (duplicated logic, 3s pause)
|
|
const playMobileVideoSignature = (id: string) => {
|
|
document.querySelectorAll<HTMLElement>(".signature-card").forEach((card) => {
|
|
if (card.dataset.id !== id) return;
|
|
const media = card.querySelector<HTMLElement>(".relative.h-48");
|
|
if (!media) return;
|
|
const img = media.querySelector<HTMLImageElement>("img");
|
|
const video = media.querySelector<HTMLVideoElement>("video");
|
|
const gradient = media.querySelector<HTMLElement>(".absolute.inset-0.bg-gradient-to-b");
|
|
if (img) img.style.opacity = "0";
|
|
if (video) {
|
|
video.style.opacity = "1";
|
|
video.play().catch(() => {});
|
|
}
|
|
if (gradient) gradient.style.opacity = "0";
|
|
});
|
|
};
|
|
|
|
const pauseAllMobileVideosSignature = () => {
|
|
document.querySelectorAll<HTMLElement>(".signature-card").forEach((card) => {
|
|
const media = card.querySelector<HTMLElement>(".relative.h-48");
|
|
if (!media) return;
|
|
const img = media.querySelector<HTMLImageElement>("img");
|
|
const video = media.querySelector<HTMLVideoElement>("video");
|
|
const gradient = media.querySelector<HTMLElement>(".absolute.inset-0.bg-gradient-to-b");
|
|
if (img) img.style.opacity = "";
|
|
if (video) {
|
|
video.style.opacity = "0";
|
|
video.pause();
|
|
video.currentTime = 0;
|
|
}
|
|
if (gradient) gradient.style.opacity = "";
|
|
});
|
|
};
|
|
|
|
const findFocusedVideoItemSignature = (): string | null => {
|
|
const cards = document.querySelectorAll<HTMLElement>('.signature-card[data-has-video="true"]');
|
|
let bestId: string | null = null;
|
|
let bestScore = 0;
|
|
const vh = window.innerHeight;
|
|
const viewportCenter = vh / 2;
|
|
cards.forEach((card) => {
|
|
const rect = card.getBoundingClientRect();
|
|
if (rect.bottom <= 0 || rect.top >= vh) return;
|
|
const visibleTop = Math.max(rect.top, 0);
|
|
const visibleBottom = Math.min(rect.bottom, vh);
|
|
const visibleHeight = visibleBottom - visibleTop;
|
|
const intersectionRatio = visibleHeight / rect.height;
|
|
const cardCenter = rect.top + rect.height / 2;
|
|
const distFromCenter = Math.abs(cardCenter - viewportCenter);
|
|
const centerScore = Math.max(0, 1 - distFromCenter / (vh / 2));
|
|
const score = intersectionRatio * 0.6 + centerScore * 0.4;
|
|
if (score > bestScore && score > 0.15) {
|
|
bestScore = score;
|
|
bestId = card.dataset.id || null;
|
|
}
|
|
});
|
|
return bestId;
|
|
};
|
|
|
|
// Lenis smooth scroll
|
|
useEffect(() => {
|
|
const lenis = new Lenis({
|
|
duration: 1.1,
|
|
easing: (t: number) => Math.min(1, 1.001 * (-Math.pow(2, -10 * t) + 1)),
|
|
smoothWheel: true,
|
|
});
|
|
const raf = (time: number) => { lenis.raf(time); requestAnimationFrame(raf); };
|
|
requestAnimationFrame(raf);
|
|
return () => lenis.destroy();
|
|
}, []);
|
|
|
|
// Mobile detection for signature menu video logic
|
|
useEffect(() => {
|
|
const updateMobile = () => setIsMobile(window.innerWidth < 768);
|
|
updateMobile();
|
|
window.addEventListener("resize", updateMobile);
|
|
return () => window.removeEventListener("resize", updateMobile);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!isMobile) {
|
|
pauseAllMobileVideosSignature();
|
|
return;
|
|
}
|
|
let scrollTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
const onScrollOrTouch = () => {
|
|
pauseAllMobileVideosSignature();
|
|
if (scrollTimeout) clearTimeout(scrollTimeout);
|
|
scrollTimeout = setTimeout(() => {
|
|
const focusedId = findFocusedVideoItemSignature();
|
|
if (focusedId) {
|
|
playMobileVideoSignature(focusedId);
|
|
}
|
|
}, 3000);
|
|
};
|
|
window.addEventListener("scroll", onScrollOrTouch, { passive: true });
|
|
window.addEventListener("touchmove", onScrollOrTouch, { passive: true });
|
|
// Initial play after load on mobile (if not scrolling)
|
|
const initialTimeout = setTimeout(() => {
|
|
const firstFocused = findFocusedVideoItemSignature();
|
|
if (firstFocused) {
|
|
playMobileVideoSignature(firstFocused);
|
|
}
|
|
}, 1500);
|
|
return () => {
|
|
window.removeEventListener("scroll", onScrollOrTouch);
|
|
window.removeEventListener("touchmove", onScrollOrTouch);
|
|
if (scrollTimeout) clearTimeout(scrollTimeout);
|
|
clearTimeout(initialTimeout);
|
|
};
|
|
}, [isMobile]);
|
|
|
|
const signatureDishes = [
|
|
{
|
|
id: "chicken-biryani",
|
|
name: "Chicken Biryani",
|
|
category: "Rice",
|
|
price: 149,
|
|
time: "32 min",
|
|
desc: "Fragrant aged basmati layered with spiced chicken, saffron & caramelized onions",
|
|
image: "chicken-biryani-poster.jpg",
|
|
video: "chicken-biryani.mp4"
|
|
},
|
|
{
|
|
id: "bong-nihari",
|
|
name: "Bong Nihari",
|
|
category: "Curry",
|
|
price: 199,
|
|
time: "45 min",
|
|
desc: "Slow-cooked beef shank in rich aromatic gravy with ginger, lemon & fresh naan",
|
|
image: "bong-nihari-poster.jpg",
|
|
video: "bong-nihari.mp4"
|
|
},
|
|
{
|
|
id: "panipuri",
|
|
name: "Golgappy / Panipuri",
|
|
category: "Street",
|
|
price: 69,
|
|
time: "15 min",
|
|
desc: "Crispy hollow puris filled with spiced chickpeas, potatoes & tangy tamarind water",
|
|
image: "panipuri-poster.jpg",
|
|
video: "panipuri.mp4"
|
|
},
|
|
{
|
|
id: "lamm-palak",
|
|
name: "Lamm Palak",
|
|
category: "Meat",
|
|
price: 179,
|
|
time: "30 min",
|
|
desc: "Tender lamb cooked with fresh spinach in a flavorful mild gravy",
|
|
image: "lamm-palak-poster.jpg",
|
|
video: "lamm-palak.mp4"
|
|
},
|
|
{
|
|
id: "chicken-karahi",
|
|
name: "Chicken Karahi",
|
|
category: "Curry",
|
|
price: 149,
|
|
time: "28 min",
|
|
desc: "Wok-tossed chicken in a robust tomato, chili & ginger gravy",
|
|
image: "chicken-karahi-poster.jpg",
|
|
video: "chicken-karahi.mp4"
|
|
},
|
|
{
|
|
id: "chicken-haleem",
|
|
name: "Chicken Haleem",
|
|
category: "Curry",
|
|
price: 149,
|
|
time: "35 min",
|
|
desc: "Slow-cooked shredded chicken with lentils, wheat & aromatic spices",
|
|
image: "chicken-haleem-poster.jpg",
|
|
video: "chicken-haleem.mp4"
|
|
},
|
|
{
|
|
id: "tikka-boti-roll",
|
|
name: "Tikka Boti Roll",
|
|
category: "Roll",
|
|
price: 99,
|
|
time: "20 min",
|
|
desc: "Juicy chicken tikka wrapped in soft naan with mint chutney & onions",
|
|
image: "tikka-boti-roll-poster.jpg",
|
|
video: "tikka-boti-roll.mp4"
|
|
},
|
|
{
|
|
id: "jalebi",
|
|
name: "Jalebi",
|
|
category: "Sweet",
|
|
price: 119,
|
|
time: "12 min",
|
|
desc: "Crispy golden saffron spirals soaked in fragrant sugar syrup",
|
|
image: "jalebi-poster.jpg",
|
|
video: "jalebi.mp4"
|
|
},
|
|
{
|
|
id: "kulfi",
|
|
name: "Kulfi",
|
|
category: "Sweet",
|
|
price: 39,
|
|
time: "10 min",
|
|
desc: "Traditional frozen milk dessert with cardamom, pistachio & saffron",
|
|
image: "kulfi-poster.jpg",
|
|
video: "kulfi.mp4"
|
|
},
|
|
];
|
|
|
|
const filtered = menuFilter === "All" ? signatureDishes : signatureDishes.filter(d => d.category === menuFilter);
|
|
|
|
const addDish = (d: any) => {
|
|
addToCart({ id: d.id, name: d.name, price: d.price, image: d.image });
|
|
};
|
|
|
|
// Advanced GSAP effects for signature cards
|
|
useEffect(() => {
|
|
const cards = document.querySelectorAll<HTMLElement>(".signature-card");
|
|
|
|
gsap.fromTo(cards,
|
|
{ opacity: 0, y: 45 },
|
|
{
|
|
opacity: 1, y: 0, duration: 0.9, ease: "power3.out", stagger: 0.06,
|
|
scrollTrigger: { trigger: ".signature-menu-grid", start: "top 82%", once: true }
|
|
}
|
|
);
|
|
|
|
cards.forEach((card) => {
|
|
const image = card.querySelector(".signature-image") as HTMLElement | null;
|
|
let bounds: DOMRect;
|
|
|
|
const onMouseMove = (e: MouseEvent) => {
|
|
if (!bounds) bounds = card.getBoundingClientRect();
|
|
const x = ((e.clientX - bounds.left) / bounds.width - 0.5) * 2;
|
|
const y = ((e.clientY - bounds.top) / bounds.height - 0.5) * 2;
|
|
|
|
gsap.to(card, {
|
|
rotationY: x * 11, rotationX: -y * 8, transformPerspective: 1400, duration: 0.35, ease: "power2.out"
|
|
});
|
|
if (image) gsap.to(image, { x: x * 8, y: y * 6, duration: 0.45, ease: "power2.out" });
|
|
};
|
|
|
|
const onMouseLeave = () => {
|
|
gsap.to(card, { rotationY: 0, rotationX: 0, duration: 1.1, ease: "elastic.out(1, 0.45)" });
|
|
if (image) gsap.to(image, { x: 0, y: 0, scale: 1, duration: 0.9, ease: "power2.out" });
|
|
};
|
|
|
|
const onMouseEnter = () => {
|
|
if (image) gsap.to(image, { scale: 1.12, duration: 1.1, ease: "power2.out" });
|
|
};
|
|
|
|
card.addEventListener("mousemove", onMouseMove);
|
|
card.addEventListener("mouseleave", onMouseLeave);
|
|
card.addEventListener("mouseenter", onMouseEnter);
|
|
|
|
(card as any)._cleanup = () => {
|
|
card.removeEventListener("mousemove", onMouseMove);
|
|
card.removeEventListener("mouseleave", onMouseLeave);
|
|
card.removeEventListener("mouseenter", onMouseEnter);
|
|
};
|
|
});
|
|
|
|
return () => {
|
|
cards.forEach(card => (card as any)._cleanup?.());
|
|
};
|
|
}, [filtered]);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-[#F8F5F0] text-[#2C2A26]">
|
|
<Navbar />
|
|
|
|
{/* HERO - Full Banner Video with responsive framing */}
|
|
<section className="relative min-h-[70dvh] sm:min-h-[78dvh] md:min-h-[85dvh] lg:min-h-[92dvh] xl:min-h-[100dvh]
|
|
flex items-center justify-center pt-40 sm:pt-44 md:pt-52 lg:pt-[200px] xl:pt-[220px] overflow-hidden bg-[#fbf7ef]">
|
|
|
|
{/* Banner Video - Responsive sources for proper mobile/iPad visualization (portrait asset) + desktop (landscape).
|
|
Mobile/iPad (<=1024px) uses the compressed banner_mobile-optimized versions (WebM primary for size/speed, MP4 fallback).
|
|
These were generated from the provided banner_mobile.mp4 (original kept as source asset). */}
|
|
<video
|
|
autoPlay
|
|
muted
|
|
playsInline
|
|
preload="auto"
|
|
className="absolute inset-0 z-0 w-full h-full object-cover
|
|
object-[50%_10%] sm:object-[50%_12%] md:object-[50%_16%]
|
|
lg:object-[50%_20%] xl:object-[50%_24%]"
|
|
onError={(e) => console.error('Hero banner video failed to load', e)}
|
|
>
|
|
{/* Mobile + iPad (portrait-friendly framing for better visualization on small screens) */}
|
|
<source
|
|
src="/images/logo/banner_mobile-optimized.webm"
|
|
type="video/webm"
|
|
media="(max-width: 1024px)"
|
|
/>
|
|
<source
|
|
src="/images/logo/banner_mobile-optimized.mp4"
|
|
type="video/mp4"
|
|
media="(max-width: 1024px)"
|
|
/>
|
|
{/* Desktop / large screens */}
|
|
<source src="/images/logo/banner1.mp4" type="video/mp4" />
|
|
</video>
|
|
|
|
{/* Subtle gradient to improve visibility of baked-in text */}
|
|
<div className="absolute inset-0 z-5 bg-gradient-to-b from-black/5 via-transparent to-black/30" />
|
|
</section>
|
|
|
|
{/* ADVANCED SIGNATURE MENU */}
|
|
<section id="menu" className="bg-[#fffdf8] px-6 py-20 lg:px-8">
|
|
<div className="mx-auto max-w-7xl">
|
|
<div className="flex flex-col md:flex-row md:items-end justify-between gap-6 mb-10">
|
|
<div>
|
|
<div className="mb-3 inline-flex items-center gap-2 rounded-full bg-[#fff6dc] px-4 py-1.5 text-sm font-semibold text-[#60420d]">
|
|
<UtensilsCrossed className="h-4 w-4" /> {t.signatureMenu.title}
|
|
</div>
|
|
<h2 className="font-serif text-5xl leading-none tracking-[-0.055em] sm:text-6xl md:text-7xl break-words">{t.signatureMenu.title}</h2>
|
|
</div>
|
|
<p className="max-w-md text-lg font-medium text-[#4b5563]">{t.signatureMenu.subtitle}</p>
|
|
</div>
|
|
|
|
{/* Filters - modern horizontal scroller on mobile for easy thumb use */}
|
|
<div className="mb-8 -mx-1 px-1 flex gap-2 overflow-x-auto snap-x snap-mandatory pb-2 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
|
|
{["All", "Rice", "Curry", "Meat", "Street", "Roll", "Sweet"].map((cat) => (
|
|
<button
|
|
key={cat}
|
|
onClick={() => setMenuFilter(cat as any)}
|
|
className={`flex-shrink-0 snap-start rounded-full px-5 py-2.5 text-sm font-bold transition-all active:scale-[0.97] touch-manipulation ${menuFilter === cat ? "bg-[#101724] text-white" : "border border-[#e5e1d7] bg-white text-[#182235] hover:border-[#c99a2e] hover:bg-[#fff6dc] active:bg-[#fff6dc]"}`}
|
|
>
|
|
{(t.signatureMenu as any)[`filter${cat}`] || cat}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Menu Grid with Advanced Effects */}
|
|
<div className="signature-menu-grid grid gap-5 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
|
|
<AnimatePresence>
|
|
{filtered.map((dish, index) => (
|
|
<motion.div
|
|
key={dish.id}
|
|
data-id={dish.id}
|
|
data-has-video={!!dish.video}
|
|
layout
|
|
onClick={() => addDish(dish)}
|
|
className="signature-card group flex cursor-pointer flex-col overflow-hidden rounded-[2rem] border border-[#EDE6D9] bg-white active:scale-[0.985] active:border-[#c99a2e]/60 transition-transform touch-manipulation"
|
|
>
|
|
<div
|
|
className="relative h-48 overflow-hidden bg-[#F2EDE4]"
|
|
onMouseEnter={dish.video ? (e) => {
|
|
const video = e.currentTarget.querySelector("video");
|
|
if (video) video.play().catch(() => {});
|
|
} : undefined}
|
|
onMouseLeave={dish.video ? (e) => {
|
|
const video = e.currentTarget.querySelector("video");
|
|
if (video) {
|
|
video.pause();
|
|
video.currentTime = 0;
|
|
}
|
|
} : undefined}
|
|
>
|
|
{dish.video ? (
|
|
<>
|
|
{/* Static Poster (first frame) */}
|
|
<img
|
|
src={`/images/dishes/${dish.image}`}
|
|
alt={dish.name}
|
|
className="signature-image absolute inset-0 w-full h-full object-cover transition-opacity duration-150"
|
|
loading="lazy"
|
|
onError={(e) => {
|
|
const base = (dish.video || dish.image || "").replace(".mp4", "").replace(/-optimized/g, "").replace(/-poster/g, "");
|
|
const fallbacks = [
|
|
`/images/dishes/${base}-poster.jpg`,
|
|
`/images/dishes/${base}.jpg`,
|
|
].filter(Boolean);
|
|
let i = 0;
|
|
const tryNext = () => {
|
|
if (i < fallbacks.length) {
|
|
(e.target as HTMLImageElement).src = fallbacks[i++];
|
|
}
|
|
};
|
|
tryNext();
|
|
}}
|
|
/>
|
|
{/* Video for mobile scroll-pause and desktop hover */}
|
|
<video
|
|
muted
|
|
loop
|
|
playsInline
|
|
preload="metadata"
|
|
className="absolute inset-0 w-full h-full object-cover opacity-0 group-hover:opacity-100 transition-opacity duration-150"
|
|
>
|
|
<source
|
|
src={`/videos/${dish.video.replace(".mp4", "-optimized.webm")}`}
|
|
type="video/webm"
|
|
/>
|
|
<source
|
|
src={`/videos/${dish.video.replace(".mp4", "-optimized.mp4")}`}
|
|
type="video/mp4"
|
|
/>
|
|
<source
|
|
src={`/videos/${dish.video.replace(".mp4", ".webm")}`}
|
|
type="video/webm"
|
|
/>
|
|
<source src={`/videos/${dish.video}`} type="video/mp4" />
|
|
</video>
|
|
<div className="absolute inset-0 bg-gradient-to-b from-black/5 via-transparent to-black/25 group-hover:opacity-0 transition-opacity" />
|
|
<div className="absolute top-4 right-4 px-4 py-1 rounded-full bg-white/95 text-[#B38B4D] text-sm font-medium tracking-tight shadow-sm border border-[#EDE6D9]">
|
|
{dish.price} kr
|
|
</div>
|
|
</>
|
|
) : (
|
|
<>
|
|
<img
|
|
src={`/images/dishes/${dish.image}`}
|
|
alt={dish.name}
|
|
className="signature-image absolute inset-0 w-full h-full object-cover transition-transform duration-700"
|
|
/>
|
|
<div className="absolute inset-0 bg-gradient-to-b from-black/5 via-transparent to-black/25" />
|
|
<div className="absolute top-4 right-4 px-4 py-1 rounded-full bg-white/95 text-[#B38B4D] text-sm font-medium tracking-tight shadow-sm border border-[#EDE6D9]">
|
|
{dish.price} kr
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
|
|
<div className="p-5 flex-1 flex flex-col">
|
|
<h4 className="text-[21px] leading-tight tracking-[-0.5px] mb-2 group-hover:text-[#B38B4D] transition-colors">
|
|
{dish.name}
|
|
</h4>
|
|
<p className="text-[#6B665F] text-[13.5px] leading-snug flex-1">{(t.signatureMenuItems as any)?.[dish.id] || dish.desc}</p>
|
|
|
|
<button
|
|
onClick={(e) => { e.stopPropagation(); addDish(dish); }}
|
|
className="mt-5 w-full py-3.5 text-sm tracking-[0.6px] border border-[#B38B4D] text-[#B38B4D] rounded-full hover:bg-[#B38B4D] hover:text-white active:bg-[#8C6B3A] active:text-white active:scale-[0.985] font-medium transition-all touch-manipulation"
|
|
>
|
|
{t.signatureMenu.addToTable}
|
|
</button>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</AnimatePresence>
|
|
</div>
|
|
|
|
<div className="mt-10 text-center">
|
|
<a href="/menu" className="inline-flex items-center gap-2 text-sm font-bold tracking-wider text-[#B38B4D] hover:text-[#8C6B3A]">
|
|
{t.signatureMenu.viewFullMenu} <ArrowRight className="h-4 w-4" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* THE SHAHI EXPERIENCE */}
|
|
<section id="experience" className="section bg-white border-y border-[#EDE6D9]">
|
|
<div className="max-w-6xl mx-auto px-6">
|
|
<div className="text-center mb-14">
|
|
<div className="text-[#B38B4D] text-xs tracking-[3px] mb-3">{language === 'sv' ? 'SHAHI-SÄTTET' : 'THE SHAHI WAY'}</div>
|
|
<h3 className="text-5xl md:text-6xl tracking-[-2px]">{language === 'sv' ? 'Mer än en måltid.\nEtt ögonblick av kunglighet.' : 'More than a meal.\nA moment of royalty.'}</h3>
|
|
</div>
|
|
|
|
<div className="grid md:grid-cols-3 gap-6">
|
|
{(language === 'sv' ? [
|
|
{ title: "Den Legendariska Buffén", desc: "Vår berömda lunchbuffé har över 20 roterande rätter — curry, biryani, färsk naan och sötsaker." },
|
|
{ title: "Shahi Sötsaker", desc: "Hemgjord mithai dagligen. Från färsk jalebi till rasmalai — det perfekta söta avslutet." },
|
|
{ title: "Varm Gästfrihet", desc: "Oavsett om du är här för en snabb lunch eller familjefest, behandlas du alltid som kunglighet." },
|
|
] : [
|
|
{ title: "The Legendary Buffet", desc: "Our famous lunch buffet features over 20 rotating dishes — curries, biryanis, fresh naan, and sweets." },
|
|
{ title: "Shahi Sweets", desc: "Homemade mithai made daily. From fresh Jalebi to Rasmalai — the perfect sweet ending." },
|
|
{ title: "Warm Hospitality", desc: "Whether you're here for a quick lunch or a family celebration, you will always be treated like royalty." },
|
|
]).map((item, index) => (
|
|
<div key={index} className="experience-card group relative border border-[#EDE6D9] p-9 rounded-2xl bg-[#F8F5F0] overflow-hidden">
|
|
<div className="text-[#B38B4D] text-6xl font-light mb-9 tracking-[-2px]">0{index + 1}</div>
|
|
<h4 className="text-[29px] tracking-[-0.8px] mb-5 text-[#2C2A26] leading-tight">{item.title}</h4>
|
|
<p className="text-[#6B665F] text-[15.5px] leading-relaxed">{item.desc}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|