Files
Hotel-Booking/Frontend/src/features/content/pages/HomePage.tsx
Iliyan Angelov 86e78247c3 updates
2025-12-01 23:30:28 +02:00

1268 lines
64 KiB
TypeScript

import React, { useState, useEffect, useMemo } from 'react';
import { Link } from 'react-router-dom';
import {
ArrowRight,
AlertCircle,
X,
ChevronLeft,
ChevronRight,
ZoomIn,
} from 'lucide-react';
import * as LucideIcons from 'lucide-react';
import {
BannerCarousel,
BannerSkeleton,
RoomCardSkeleton,
RoomCarousel,
SearchRoomForm,
} from '../../rooms/components';
import bannerService from '../services/bannerService';
import roomService from '../../rooms/services/roomService';
import pageContentService from '../services/pageContentService';
import type { Banner } from '../services/bannerService';
import type { Room } from '../../rooms/services/roomService';
import type { PageContent } from '../services/pageContentService';
const HomePage: React.FC = () => {
const [banners, setBanners] = useState<Banner[]>([]);
const [featuredRooms, setFeaturedRooms] = useState<Room[]>([]);
const [newestRooms, setNewestRooms] = useState<Room[]>([]);
const [pageContent, setPageContent] = useState<PageContent | null>(null);
const [isLoadingBanners, setIsLoadingBanners] =
useState(true);
const [isLoadingRooms, setIsLoadingRooms] = useState(true);
const [isLoadingNewest, setIsLoadingNewest] = useState(true);
const [, setIsLoadingContent] = useState(true);
const [error, setError] = useState<string | null>(null);
const [apiError, setApiError] = useState(false);
const [apiErrorMessage, setApiErrorMessage] = useState<string>('');
const [lightboxOpen, setLightboxOpen] = useState(false);
const [lightboxIndex, setLightboxIndex] = useState(0);
const [lightboxImages, setLightboxImages] = useState<string[]>([]);
// Prevent body scroll when API error modal is shown
useEffect(() => {
if (apiError) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'unset';
}
return () => {
document.body.style.overflow = 'unset';
};
}, [apiError]);
useEffect(() => {
if (!lightboxOpen) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
setLightboxOpen(false);
} else if (e.key === 'ArrowLeft' && lightboxImages.length > 1) {
setLightboxIndex((prev) => (prev === 0 ? lightboxImages.length - 1 : prev - 1));
} else if (e.key === 'ArrowRight' && lightboxImages.length > 1) {
setLightboxIndex((prev) => (prev === lightboxImages.length - 1 ? 0 : prev + 1));
}
};
window.addEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'hidden';
return () => {
window.removeEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'unset';
};
}, [lightboxOpen, lightboxImages.length]);
const combinedRooms = useMemo(() => {
const roomMap = new Map<number, Room>();
featuredRooms.forEach(room => {
roomMap.set(room.id, room);
});
newestRooms.forEach(room => {
if (!roomMap.has(room.id)) {
roomMap.set(room.id, room);
}
});
return Array.from(roomMap.values());
}, [featuredRooms, newestRooms]);
useEffect(() => {
const fetchPageContent = async () => {
try {
setIsLoadingContent(true);
setApiError(false);
const response = await pageContentService.getHomeContent();
if (response.status === 'success' && response.data?.page_content) {
const content = response.data.page_content;
if (typeof content.features === 'string') {
try {
content.features = JSON.parse(content.features);
} catch {
content.features = [];
}
}
if (typeof content.amenities === 'string') {
try {
content.amenities = JSON.parse(content.amenities);
} catch {
content.amenities = [];
}
}
if (typeof content.testimonials === 'string') {
try {
content.testimonials = JSON.parse(content.testimonials);
} catch (e) {
content.testimonials = [];
}
}
if (typeof content.gallery_images === 'string') {
try {
content.gallery_images = JSON.parse(content.gallery_images);
} catch (e) {
content.gallery_images = [];
}
}
if (typeof content.stats === 'string') {
try {
content.stats = JSON.parse(content.stats);
} catch (e) {
content.stats = [];
}
}
if (typeof content.luxury_features === 'string') {
try {
content.luxury_features = JSON.parse(content.luxury_features);
} catch (e) {
content.luxury_features = [];
}
}
if (typeof content.luxury_gallery === 'string') {
try {
const parsed = JSON.parse(content.luxury_gallery);
content.luxury_gallery = Array.isArray(parsed) ? parsed.filter(img => img && typeof img === 'string' && img.trim() !== '') : [];
} catch (e) {
content.luxury_gallery = [];
}
}
if (Array.isArray(content.luxury_gallery)) {
content.luxury_gallery = content.luxury_gallery.filter(img => img && typeof img === 'string' && img.trim() !== '');
} else {
content.luxury_gallery = [];
}
if (typeof content.luxury_testimonials === 'string') {
try {
content.luxury_testimonials = JSON.parse(content.luxury_testimonials);
} catch (e) {
content.luxury_testimonials = [];
}
}
if (typeof content.luxury_services === 'string') {
try {
content.luxury_services = JSON.parse(content.luxury_services);
} catch (e) {
content.luxury_services = [];
}
}
if (typeof content.luxury_experiences === 'string') {
try {
content.luxury_experiences = JSON.parse(content.luxury_experiences);
} catch (e) {
content.luxury_experiences = [];
}
}
if (typeof content.awards === 'string') {
try {
content.awards = JSON.parse(content.awards);
} catch (e) {
content.awards = [];
}
}
if (typeof content.partners === 'string') {
try {
content.partners = JSON.parse(content.partners);
} catch (e) {
content.partners = [];
}
}
setPageContent(content);
if (content.meta_title) {
document.title = content.meta_title;
}
if (content.meta_description) {
let metaDescription = document.querySelector('meta[name="description"]');
if (!metaDescription) {
metaDescription = document.createElement('meta');
metaDescription.setAttribute('name', 'description');
document.head.appendChild(metaDescription);
}
metaDescription.setAttribute('content', content.meta_description);
} else {
setPageContent(null);
}
} else {
setPageContent(null);
}
} catch (err: any) {
console.error('Error fetching page content:', err);
setApiError(true);
setApiErrorMessage('Unable to connect to the server. Please check your internet connection and try again.');
setPageContent(null);
} finally {
setIsLoadingContent(false);
}
};
fetchPageContent();
}, []);
useEffect(() => {
const fetchBanners = async () => {
try {
setIsLoadingBanners(true);
const response = await bannerService
.getBannersByPosition('home');
if (
response.success ||
response.status === 'success'
) {
setBanners(response.data?.banners || []);
} else {
setBanners([]);
}
} catch (err: any) {
console.error('Error fetching banners:', err);
setApiError(true);
setApiErrorMessage('Unable to connect to the server. Please check your internet connection and try again.');
setBanners([]);
} finally {
setIsLoadingBanners(false);
}
};
fetchBanners();
}, []);
useEffect(() => {
const fetchFeaturedRooms = async () => {
try {
setIsLoadingRooms(true);
setError(null);
const response = await roomService.getFeaturedRooms({
featured: true,
limit: 6,
});
if (
response.success ||
response.status === 'success'
) {
const rooms = response.data?.rooms || [];
setFeaturedRooms(rooms);
if (rooms.length === 0) {
setError(null);
}
} else {
setFeaturedRooms([]);
setError(
response.message ||
'Unable to load room list'
);
}
} catch (err: any) {
console.error('Error fetching rooms:', err);
setFeaturedRooms([]);
if (err.response?.status === 429) {
setError(
'Too many requests. Please wait a moment and refresh the page.'
);
} else {
setError(
err.response?.data?.message ||
err.message ||
'Unable to load room list'
);
}
} finally {
setIsLoadingRooms(false);
}
};
fetchFeaturedRooms();
}, []);
useEffect(() => {
const fetchNewestRooms = async () => {
try {
setIsLoadingNewest(true);
const response = await roomService.getRooms({
page: 1,
limit: 6,
sort: 'newest',
});
if (
response.success ||
response.status === 'success'
) {
setNewestRooms(response.data?.rooms || []);
} else {
setNewestRooms([]);
}
} catch (err: any) {
console.error('Error fetching newest rooms:', err);
setApiError(true);
setApiErrorMessage('Unable to connect to the server. Please check your internet connection and try again.');
setNewestRooms([]);
} finally {
setIsLoadingNewest(false);
}
};
fetchNewestRooms();
}, []);
return (
<>
{/* Persistent API Error Modal - Cannot be closed */}
{apiError && (
<div
className="fixed inset-0 z-[99999] bg-black/90 backdrop-blur-sm flex items-center justify-center p-4"
style={{ pointerEvents: 'auto' }}
onKeyDown={(e) => {
// Prevent closing with Escape key
if (e.key === 'Escape') {
e.preventDefault();
e.stopPropagation();
}
}}
onClick={(e) => {
// Prevent closing by clicking outside
e.stopPropagation();
}}
>
<div
className="bg-white rounded-xl shadow-2xl max-w-md w-full p-8 border-2 border-red-500"
onClick={(e) => e.stopPropagation()}
>
<div className="flex flex-col items-center text-center">
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mb-4">
<AlertCircle className="w-8 h-8 text-red-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-3">
API Connection Error
</h2>
<p className="text-gray-600 mb-6 leading-relaxed">
{apiErrorMessage || 'Unable to connect to the server. The API is currently unavailable.'}
</p>
<div className="bg-red-50 border border-red-200 rounded-lg p-4 w-full">
<p className="text-sm text-red-800 font-medium">
Please check:
</p>
<ul className="text-sm text-red-700 mt-2 text-left list-disc list-inside space-y-1">
<li>Your internet connection</li>
<li>If the server is running</li>
<li>If the API endpoint is accessible</li>
</ul>
</div>
<div className="mt-6 w-full">
<button
onClick={() => window.location.reload()}
className="w-full px-6 py-3 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 transition-colors"
>
Refresh Page
</button>
</div>
</div>
</div>
</div>
)}
{}
<section
className="relative w-screen -mt-6"
style={{
marginLeft: 'calc(50% - 50vw)',
marginRight: 'calc(50% - 50vw)'
}}
>
{isLoadingBanners ? (
<BannerSkeleton />
) : banners.length > 0 ? (
<div className="animate-fade-in">
<BannerCarousel banners={banners}>
<SearchRoomForm className="overlay" />
</BannerCarousel>
</div>
) : null}
</section>
<div className="min-h-screen bg-gradient-to-br from-gray-50 via-white to-gray-50/30 relative overflow-hidden">
{}
<div className="fixed inset-0 opacity-[0.015] bg-[radial-gradient(circle_at_1px_1px,#d4af37_1px,transparent_0)] bg-[length:60px_60px] pointer-events-none"></div>
<div className="relative z-10">
{}
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-8 md:py-12 lg:py-16">
{}
<div className="text-center animate-fade-in mb-6 md:mb-8">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4">
{pageContent?.hero_title || (apiError ? '' : 'Featured & Newest Rooms')}
</h2>
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto px-4 leading-relaxed">
{pageContent?.hero_subtitle || pageContent?.description || (apiError ? '' : 'Discover our most popular accommodations and latest additions')}
</p>
{}
<div className="mt-6 md:mt-8 flex justify-center">
<Link
to="/rooms"
className="group relative inline-flex items-center gap-2 px-6 py-2.5 md:px-8 md:py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg font-semibold tracking-wide text-sm md:text-base shadow-md shadow-[#d4af37]/20 hover:shadow-lg hover:shadow-[#d4af37]/30 hover:-translate-y-0.5 transition-all duration-300 overflow-hidden"
>
<span className="absolute inset-0 bg-gradient-to-r from-[#f5d76e] to-[#d4af37] opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
<span className="relative z-10">View All Rooms</span>
<ArrowRight className="w-4 h-4 md:w-5 md:h-5 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
</Link>
</div>
</div>
{}
{(isLoadingRooms || isLoadingNewest) && (
<div className="flex justify-center">
<div className="max-w-md w-full">
<RoomCardSkeleton />
</div>
</div>
)}
{}
{error && !isLoadingRooms && !isLoadingNewest && (
<div
className="luxury-card p-8 text-center animate-fade-in
border-red-200 bg-gradient-to-br from-red-50 to-red-100/50"
>
<div className="inline-flex items-center justify-center w-16 h-16
bg-red-100 rounded-full mb-4">
<AlertCircle
className="w-8 h-8 text-red-600"
/>
</div>
<p className="text-red-800 font-serif font-semibold text-lg mb-2 tracking-tight">
{error}
</p>
<button
onClick={() => window.location.reload()}
className="mt-4 px-6 py-2.5 bg-gradient-to-r from-red-600 to-red-700
text-white rounded-sm font-medium tracking-wide
hover:from-red-700 hover:to-red-800
transition-all duration-300 shadow-lg shadow-red-500/30
hover:shadow-xl hover:shadow-red-500/40 hover:-translate-y-0.5"
>
Try Again
</button>
</div>
)}
{}
{!isLoadingRooms && !isLoadingNewest && (
<>
{combinedRooms.length > 0 ? (
<RoomCarousel
rooms={combinedRooms}
autoSlideInterval={4000}
showNavigation={true}
/>
) : (
<div
className="luxury-card p-12 text-center animate-fade-in"
>
<p className="text-gray-600 text-lg font-light tracking-wide">
No rooms available
</p>
</div>
)}
</>
)}
</section>
{}
{(() => {
const validFeatures = pageContent?.features?.filter(
(f: any) => f && (f.title || f.description)
) || [];
// Only show section if we have features from API, or if pageContent was loaded but is empty (not if API failed)
return validFeatures.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div className="relative bg-white rounded-xl md:rounded-2xl shadow-xl shadow-[#d4af37]/5 p-6 md:p-8 lg:p-10 animate-fade-in overflow-hidden border border-gray-100/50">
{}
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
{}
<div className="absolute inset-0 opacity-[0.015] bg-[radial-gradient(circle_at_1px_1px,#d4af37_1px,transparent_0)] bg-[length:40px_40px]"></div>
<div className="relative grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8">
{validFeatures.length > 0 ? (
validFeatures.map((feature: any, index: number) => (
<div key={`feature-${index}-${feature.title || index}`} className="text-center group relative">
{feature.image ? (
<div className="w-16 h-16 md:w-20 md:h-20 mx-auto mb-4 md:mb-5 rounded-lg overflow-hidden shadow-lg shadow-[#d4af37]/15 group-hover:scale-110 group-hover:shadow-xl group-hover:shadow-[#d4af37]/25 transition-all duration-300 border border-[#d4af37]/20">
<img src={feature.image} alt={feature.title || 'Feature'} className="w-full h-full object-cover" />
</div>
) : (
<div
className="w-16 h-16 md:w-18 md:h-18 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15
rounded-lg flex items-center justify-center mx-auto mb-4 md:mb-5
shadow-lg shadow-[#d4af37]/15 border border-[#d4af37]/25
group-hover:scale-110 group-hover:shadow-xl group-hover:shadow-[#d4af37]/30 group-hover:border-[#d4af37]/40
transition-all duration-300 backdrop-blur-sm"
>
{feature.icon && (LucideIcons as any)[feature.icon] ? (
React.createElement((LucideIcons as any)[feature.icon], {
className: 'w-7 h-7 md:w-8 md:h-8 text-[#d4af37] drop-shadow-md'
})
) : (
<span className="text-3xl"></span>
)}
</div>
)}
{feature.title && (
<h3
className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3
text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight leading-tight"
>
{feature.title}
</h3>
)}
{feature.description && (
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide max-w-xs mx-auto">
{feature.description}
</p>
)}
</div>
))
) : null}
</div>
</div>
</section>
);
})()}
{}
{(pageContent?.luxury_section_title || (pageContent?.luxury_features && pageContent.luxury_features.length > 0)) && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
<div className="text-center mb-8 md:mb-10 animate-fade-in">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
{pageContent.luxury_section_title || 'Experience Unparalleled Luxury'}
</h2>
{pageContent.luxury_section_subtitle && (
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
{pageContent.luxury_section_subtitle}
</p>
)}
</div>
{pageContent.luxury_section_image && (
<div className="mb-8 md:mb-10 animate-fade-in px-4">
<div className="relative rounded-xl md:rounded-2xl overflow-hidden shadow-xl shadow-[#d4af37]/10 group">
<img
src={pageContent.luxury_section_image}
alt="Luxury Experience"
className="w-full h-48 sm:h-64 md:h-80 lg:h-96 object-cover group-hover:scale-105 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent"></div>
</div>
</div>
)}
{pageContent.luxury_features && pageContent.luxury_features.length > 0 && (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
{pageContent.luxury_features.map((feature, index) => (
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
{feature.icon && (LucideIcons as any)[feature.icon] ? (
React.createElement((LucideIcons as any)[feature.icon], {
className: 'w-7 h-7 md:w-8 md:h-8 text-[#d4af37] drop-shadow-md'
})
) : (
<span className="text-2xl md:text-3xl"></span>
)}
</div>
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 text-center tracking-tight leading-tight">
{feature.title}
</h3>
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light text-center tracking-wide">
{feature.description}
</p>
</div>
))}
</div>
)}
</section>
)}
{}
{pageContent?.luxury_gallery && Array.isArray(pageContent.luxury_gallery) && pageContent.luxury_gallery.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div className="text-center mb-8 md:mb-10 animate-fade-in">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
{pageContent.luxury_gallery_section_title || 'Luxury Gallery'}
</h2>
{pageContent.luxury_gallery_section_subtitle && (
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
{pageContent.luxury_gallery_section_subtitle}
</p>
)}
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-2.5 md:gap-3 lg:gap-4 px-4">
{pageContent.luxury_gallery.map((image, index) => {
const imageUrl = image && typeof image === 'string'
? (image.startsWith('http://') || image.startsWith('https://')
? image
: `${import.meta.env.VITE_API_URL?.replace('/api', '') || 'http://localhost:8000'}/${image}`)
: '';
if (!imageUrl) return null;
return (
<div
key={`luxury-gallery-${index}-${image}`}
className="relative group overflow-hidden rounded-lg md:rounded-xl aspect-square animate-fade-in shadow-md shadow-gray-900/5 hover:shadow-xl hover:shadow-[#d4af37]/15 transition-all duration-300 cursor-pointer"
style={{ animationDelay: `${index * 0.05}s` }}
onClick={() => {
const normalizedImages = (pageContent.luxury_gallery || [])
.map(img => {
if (!img || typeof img !== 'string') return null;
return img.startsWith('http://') || img.startsWith('https://')
? img
: `${import.meta.env.VITE_API_URL?.replace('/api', '') || 'http://localhost:8000'}/${img}`
})
.filter(Boolean) as string[];
setLightboxImages(normalizedImages);
setLightboxIndex(index);
setLightboxOpen(true);
}}
>
<img
src={imageUrl}
alt={`Luxury gallery image ${index + 1}`}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
onError={(e) => {
console.error(`Failed to load luxury gallery image: ${imageUrl}`);
(e.target as HTMLImageElement).style.display = 'none';
}}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-black/0 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
<div className="transform translate-y-4 group-hover:translate-y-0 opacity-0 group-hover:opacity-100 transition-all duration-300">
<ZoomIn className="w-8 h-8 md:w-10 md:h-10 text-white drop-shadow-2xl" />
</div>
</div>
<div className="absolute inset-0 border-2 border-transparent group-hover:border-[#d4af37]/60 transition-colors duration-300 rounded-lg md:rounded-xl"></div>
</div>
);
})}
</div>
</section>
)}
{}
{pageContent?.luxury_testimonials && pageContent.luxury_testimonials.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
<div className="text-center mb-8 md:mb-10 animate-fade-in">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
{pageContent.luxury_testimonials_section_title || 'Guest Experiences'}
</h2>
{pageContent.luxury_testimonials_section_subtitle && (
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
{pageContent.luxury_testimonials_section_subtitle}
</p>
)}
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
Hear from our valued guests about their luxury stay
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
{pageContent.luxury_testimonials.map((testimonial, index) => (
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 shadow-lg shadow-gray-900/5 hover:shadow-xl hover:shadow-[#d4af37]/8 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
<div className="flex items-center mb-4 md:mb-5">
{testimonial.image ? (
<div className="relative">
<img src={testimonial.image} alt={testimonial.name} className="w-12 h-12 md:w-14 md:h-14 rounded-full object-cover mr-3 md:mr-4 border-2 border-[#d4af37]/20 shadow-md" />
<div className="absolute inset-0 rounded-full border border-[#d4af37]/40"></div>
</div>
) : (
<div className="w-12 h-12 md:w-14 md:h-14 rounded-full bg-gradient-to-br from-[#d4af37] to-[#f5d76e] flex items-center justify-center text-white font-bold text-base md:text-lg mr-3 md:mr-4 shadow-md border-2 border-white">
{testimonial.name.charAt(0).toUpperCase()}
</div>
)}
<div>
<h4 className="font-semibold text-gray-900 text-base md:text-lg">{testimonial.name}</h4>
{testimonial.title && (
<p className="text-xs md:text-sm text-gray-500 font-light">{testimonial.title}</p>
)}
</div>
</div>
<div className="relative">
<div className="absolute -top-1 -left-1 text-4xl md:text-5xl text-[#d4af37]/8 font-serif">"</div>
<p className="text-sm md:text-base text-gray-700 leading-relaxed italic relative z-10 font-light">{testimonial.quote}</p>
</div>
</div>
))}
</div>
</section>
)}
{}
{pageContent?.stats && Array.isArray(pageContent.stats) && pageContent.stats.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div className="relative bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 rounded-xl md:rounded-2xl p-6 md:p-8 lg:p-10 shadow-xl shadow-black/30 animate-fade-in overflow-hidden border border-[#d4af37]/15">
{}
<div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
<div className="absolute inset-0 opacity-8 bg-[radial-gradient(circle_at_1px_1px,#d4af37_1px,transparent_0)] bg-[length:40px_40px]"></div>
<div className="relative grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-6 lg:gap-8">
{pageContent.stats.map((stat, index) => (
<div key={`stat-${index}-${stat.label || index}`} className="text-center group relative">
{stat?.icon && (
<div className="mb-3 md:mb-4 group-hover:scale-110 transition-transform duration-300 flex items-center justify-center">
{stat.icon && (LucideIcons as any)[stat.icon] ? (
React.createElement((LucideIcons as any)[stat.icon], {
className: 'w-8 h-8 md:w-10 md:h-10 text-[#d4af37] drop-shadow-md'
})
) : (
<span className="text-3xl md:text-4xl">{stat.icon}</span>
)}
</div>
)}
{stat?.number && (
<div className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold text-[#d4af37] mb-1 md:mb-2 font-serif tracking-tight">
{stat.number}
</div>
)}
{stat?.label && (
<div className="text-gray-300 text-xs sm:text-sm md:text-base font-light tracking-wider uppercase">
{stat.label}
</div>
)}
</div>
))}
</div>
</div>
</section>
)}
{}
{pageContent?.amenities && pageContent.amenities.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div className="text-center mb-8 md:mb-10 animate-fade-in">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
{pageContent.amenities_section_title || 'Luxury Amenities'}
</h2>
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
{pageContent.amenities_section_subtitle || 'Experience world-class amenities designed for your comfort'}
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
{pageContent.amenities.map((amenity, index) => (
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
{amenity.image ? (
<div className="w-full h-40 md:h-48 mb-4 md:mb-5 rounded-lg md:rounded-xl overflow-hidden shadow-lg group-hover:scale-105 transition-transform duration-300 border border-gray-100 group-hover:border-[#d4af37]/25">
<img src={amenity.image} alt={amenity.title} className="w-full h-full object-cover" />
</div>
) : (
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
{amenity.icon && (LucideIcons as any)[amenity.icon] ? (
React.createElement((LucideIcons as any)[amenity.icon], {
className: 'w-7 h-7 md:w-8 md:h-8 text-[#d4af37] drop-shadow-md'
})
) : (
<span className="text-2xl md:text-3xl">✨</span>
)}
</div>
)}
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight">
{amenity.title}
</h3>
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide">
{amenity.description}
</p>
</div>
))}
</div>
</section>
)}
{}
{pageContent?.testimonials && pageContent.testimonials.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
<div className="text-center mb-8 md:mb-10 animate-fade-in">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
{pageContent.testimonials_section_title || 'Guest Testimonials'}
</h2>
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
{pageContent.testimonials_section_subtitle || 'See what our guests say about their experience'}
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
{pageContent.testimonials.map((testimonial, index) => (
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 shadow-lg shadow-gray-900/5 hover:shadow-xl hover:shadow-[#d4af37]/8 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
<div className="flex items-center mb-4 md:mb-5">
{testimonial.image ? (
<div className="relative">
<img src={testimonial.image} alt={testimonial.name} className="w-12 h-12 md:w-14 md:h-14 rounded-full object-cover mr-3 md:mr-4 border-2 border-[#d4af37]/20 shadow-md" />
<div className="absolute inset-0 rounded-full border border-[#d4af37]/40"></div>
</div>
) : (
<div className="w-12 h-12 md:w-14 md:h-14 rounded-full bg-gradient-to-br from-[#d4af37] to-[#f5d76e] flex items-center justify-center text-white font-bold text-base md:text-lg mr-3 md:mr-4 shadow-md border-2 border-white">
{testimonial.name.charAt(0).toUpperCase()}
</div>
)}
<div>
<h4 className="font-semibold text-gray-900 text-base md:text-lg">{testimonial.name}</h4>
<p className="text-xs md:text-sm text-gray-500 font-light">{testimonial.role}</p>
</div>
</div>
<div className="flex mb-3 md:mb-4 justify-center md:justify-start">
{[...Array(5)].map((_, i) => (
<span key={i} className={`text-base md:text-lg ${i < testimonial.rating ? 'text-[#d4af37]' : 'text-gray-300'}`}>★</span>
))}
</div>
<div className="relative">
<div className="absolute -top-1 -left-1 text-4xl md:text-5xl text-[#d4af37]/8 font-serif">"</div>
<p className="text-sm md:text-base text-gray-700 leading-relaxed italic relative z-10 font-light">"{testimonial.comment}"</p>
</div>
</div>
))}
</div>
</section>
)}
{}
{(pageContent?.about_preview_title || pageContent?.about_preview_content) && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div className="relative bg-white rounded-xl md:rounded-2xl shadow-xl shadow-[#d4af37]/5 overflow-hidden animate-fade-in border border-gray-100/50">
{}
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-[#d4af37] via-[#f5d76e] to-[#d4af37]"></div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-0">
{pageContent.about_preview_image && (
<div className="relative h-56 sm:h-64 md:h-80 lg:h-auto lg:min-h-[400px] overflow-hidden group">
<img
src={pageContent.about_preview_image}
alt="About us"
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-r from-black/20 via-transparent to-transparent lg:hidden"></div>
</div>
)}
<div className="p-6 md:p-8 lg:p-10 xl:p-12 flex flex-col justify-center bg-gradient-to-br from-white to-gray-50/20">
<div className="inline-block mb-3 md:mb-4">
<div className="h-0.5 w-12 md:w-16 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 mb-3 md:mb-4 tracking-tight leading-tight">
{pageContent.about_preview_title || 'About Our Hotel'}
</h2>
{pageContent.about_preview_subtitle && (
<p className="text-sm sm:text-base md:text-lg text-gray-600 leading-relaxed font-light mb-3 md:mb-4 tracking-wide">
{pageContent.about_preview_subtitle}
</p>
)}
{pageContent.about_preview_content && (
<p className="text-sm sm:text-base md:text-lg text-gray-600 leading-relaxed font-light mb-5 md:mb-6 tracking-wide">
{pageContent.about_preview_content}
</p>
)}
<Link
to="/about"
className="group relative inline-flex items-center gap-2 px-6 py-2.5 md:px-8 md:py-3 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg font-semibold tracking-wide text-sm md:text-base shadow-md shadow-[#d4af37]/20 hover:shadow-lg hover:shadow-[#d4af37]/30 hover:-translate-y-0.5 transition-all duration-300 overflow-hidden w-fit"
>
<span className="absolute inset-0 bg-gradient-to-r from-[#f5d76e] to-[#d4af37] opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
<span className="relative z-10">Learn More</span>
<ArrowRight className="w-4 h-4 md:w-5 md:h-5 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
</Link>
</div>
</div>
</div>
</section>
)}
{}
{pageContent?.luxury_services && pageContent.luxury_services.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div className="text-center mb-8 md:mb-10 animate-fade-in">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
{pageContent.luxury_services_section_title || 'Luxury Services'}
</h2>
{pageContent.luxury_services_section_subtitle && (
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
{pageContent.luxury_services_section_subtitle}
</p>
)}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
{pageContent.luxury_services.map((service: any, index: number) => (
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
{service.image ? (
<div className="w-full h-40 md:h-48 mb-4 md:mb-5 rounded-lg md:rounded-xl overflow-hidden shadow-lg group-hover:scale-105 transition-transform duration-300 border border-gray-100 group-hover:border-[#d4af37]/25">
<img src={service.image} alt={service.title} className="w-full h-full object-cover" />
</div>
) : (
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
{service.icon && (LucideIcons as any)[service.icon] ? (
React.createElement((LucideIcons as any)[service.icon], {
className: 'w-7 h-7 md:w-8 md:h-8 text-[#d4af37] drop-shadow-md'
})
) : (
<span className="text-2xl md:text-3xl"></span>
)}
</div>
)}
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight">
{service.title}
</h3>
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide">
{service.description}
</p>
</div>
))}
</div>
</section>
)}
{}
{pageContent?.luxury_experiences && pageContent.luxury_experiences.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
<div className="text-center mb-8 md:mb-10 animate-fade-in">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
{pageContent.luxury_experiences_section_title || 'Unique Experiences'}
</h2>
{pageContent.luxury_experiences_section_subtitle && (
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
{pageContent.luxury_experiences_section_subtitle}
</p>
)}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6 px-4">
{pageContent.luxury_experiences.map((experience: any, index: number) => (
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1" style={{ animationDelay: `${index * 0.1}s` }}>
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
{experience.image ? (
<div className="w-full h-40 md:h-48 mb-4 md:mb-5 rounded-lg md:rounded-xl overflow-hidden shadow-lg group-hover:scale-105 transition-transform duration-300 border border-gray-100 group-hover:border-[#d4af37]/25">
<img src={experience.image} alt={experience.title} className="w-full h-full object-cover" />
</div>
) : (
<div className="w-14 h-14 md:w-16 md:h-16 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mb-4 md:mb-5 mx-auto group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
{experience.icon && (LucideIcons as any)[experience.icon] ? (
React.createElement((LucideIcons as any)[experience.icon], {
className: 'w-7 h-7 md:w-8 md:h-8 text-[#d4af37] drop-shadow-md'
})
) : (
<span className="text-2xl md:text-3xl"></span>
)}
</div>
)}
<h3 className="text-lg md:text-xl font-serif font-semibold mb-2 md:mb-3 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight">
{experience.title}
</h3>
<p className="text-sm md:text-base text-gray-600 leading-relaxed font-light tracking-wide">
{experience.description}
</p>
</div>
))}
</div>
</section>
)}
{}
{pageContent?.awards && pageContent.awards.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div className="text-center mb-8 md:mb-10 animate-fade-in">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
{pageContent.awards_section_title || 'Awards & Recognition'}
</h2>
{pageContent.awards_section_subtitle && (
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
{pageContent.awards_section_subtitle}
</p>
)}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 md:gap-6 px-4">
{pageContent.awards.map((award: any, index: number) => (
<div key={index} className="relative bg-white rounded-lg md:rounded-xl p-5 md:p-6 group hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 animate-fade-in border border-gray-100/50 hover:border-[#d4af37]/25 hover:-translate-y-1 text-center" style={{ animationDelay: `${index * 0.1}s` }}>
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-t-lg md:rounded-t-xl"></div>
{award.image ? (
<div className="w-20 h-20 md:w-24 md:h-24 mx-auto mb-4 md:mb-5 rounded-lg overflow-hidden shadow-lg">
<img src={award.image} alt={award.title} className="w-full h-full object-cover" />
</div>
) : (
<div className="w-16 h-16 md:w-20 md:h-20 bg-gradient-to-br from-[#d4af37]/15 via-[#f5d76e]/10 to-[#d4af37]/15 rounded-lg flex items-center justify-center mx-auto mb-4 md:mb-5 group-hover:scale-110 group-hover:shadow-lg group-hover:shadow-[#d4af37]/20 transition-all duration-300 border border-[#d4af37]/25 group-hover:border-[#d4af37]/40">
{award.icon && (LucideIcons as any)[award.icon] ? (
React.createElement((LucideIcons as any)[award.icon], {
className: 'w-8 h-8 md:w-10 md:h-10 text-[#d4af37] drop-shadow-md'
})
) : (
<span className="text-3xl">🏆</span>
)}
</div>
)}
{award.year && (
<div className="text-xs md:text-sm text-[#d4af37] font-semibold mb-2">{award.year}</div>
)}
<h3 className="text-base md:text-lg font-serif font-semibold mb-2 text-gray-900 group-hover:text-[#d4af37] transition-colors duration-300 tracking-tight">
{award.title}
</h3>
{award.description && (
<p className="text-xs md:text-sm text-gray-600 leading-relaxed font-light tracking-wide">
{award.description}
</p>
)}
</div>
))}
</div>
</section>
)}
{}
{(pageContent?.cta_title || pageContent?.cta_subtitle) && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div className="relative bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 rounded-xl md:rounded-2xl p-8 md:p-12 lg:p-16 shadow-xl shadow-black/30 animate-fade-in overflow-hidden border border-[#d4af37]/15">
{pageContent.cta_image && (
<div className="absolute inset-0 opacity-20">
<img src={pageContent.cta_image} alt="CTA Background" className="w-full h-full object-cover" />
</div>
)}
<div className="absolute inset-0 bg-gradient-to-r from-black/60 via-black/40 to-black/60"></div>
<div className="relative z-10 text-center">
<div className="inline-block mb-4">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-serif font-bold text-white mb-4 md:mb-6 tracking-tight">
{pageContent.cta_title}
</h2>
{pageContent.cta_subtitle && (
<p className="text-base sm:text-lg md:text-xl text-gray-200 font-light mb-6 md:mb-8 max-w-2xl mx-auto">
{pageContent.cta_subtitle}
</p>
)}
{pageContent.cta_button_text && pageContent.cta_button_link && (
<Link
to={pageContent.cta_button_link}
className="group relative inline-flex items-center gap-2 px-8 py-3 md:px-10 md:py-4 bg-gradient-to-r from-[#d4af37] to-[#c9a227] text-[#0f0f0f] rounded-lg font-semibold tracking-wide text-base md:text-lg shadow-lg shadow-[#d4af37]/30 hover:shadow-xl hover:shadow-[#d4af37]/40 hover:-translate-y-1 transition-all duration-300 overflow-hidden"
>
<span className="absolute inset-0 bg-gradient-to-r from-[#f5d76e] to-[#d4af37] opacity-0 group-hover:opacity-100 transition-opacity duration-300"></span>
<span className="relative z-10">{pageContent.cta_button_text}</span>
<ArrowRight className="w-5 h-5 md:w-6 md:h-6 relative z-10 group-hover:translate-x-1 transition-transform duration-300" />
</Link>
)}
</div>
</div>
</section>
)}
{}
{pageContent?.partners && pageContent.partners.length > 0 && (
<section className="container mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16 bg-gradient-to-b from-white via-gray-50/20 to-white">
<div className="text-center mb-8 md:mb-10 animate-fade-in">
<div className="inline-block mb-3">
<div className="h-0.5 w-16 md:w-20 bg-gradient-to-r from-[#d4af37] to-[#f5d76e] mx-auto rounded-full"></div>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-serif font-bold text-gray-900 tracking-tight mb-3 md:mb-4 px-4">
{pageContent.partners_section_title || 'Our Partners'}
</h2>
{pageContent.partners_section_subtitle && (
<p className="text-sm sm:text-base md:text-lg text-gray-600 font-light tracking-wide max-w-2xl mx-auto mt-3 md:mt-4 px-4 leading-relaxed">
{pageContent.partners_section_subtitle}
</p>
)}
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-6 md:gap-8 px-4">
{pageContent.partners.map((partner: any, index: number) => (
<div key={index} className="flex items-center justify-center p-4 md:p-6 bg-white rounded-lg md:rounded-xl shadow-md hover:shadow-xl hover:shadow-[#d4af37]/10 transition-all duration-300 border border-gray-100/50 hover:border-[#d4af37]/25 group" style={{ animationDelay: `${index * 0.05}s` }}>
{partner.link ? (
<a href={partner.link} target="_blank" rel="noopener noreferrer" className="w-full h-full flex items-center justify-center">
{partner.logo ? (
<img src={partner.logo} alt={partner.name} className="max-w-full max-h-16 md:max-h-20 object-contain opacity-70 group-hover:opacity-100 transition-opacity duration-300" />
) : (
<span className="text-gray-600 font-semibold text-sm md:text-base">{partner.name}</span>
)}
</a>
) : (
<>
{partner.logo ? (
<img src={partner.logo} alt={partner.name} className="max-w-full max-h-16 md:max-h-20 object-contain opacity-70 group-hover:opacity-100 transition-opacity duration-300" />
) : (
<span className="text-gray-600 font-semibold text-sm md:text-base">{partner.name}</span>
)}
</>
)}
</div>
))}
</div>
</section>
)}
</div>
</div>
{}
{lightboxOpen && lightboxImages.length > 0 && (
<div
className="fixed inset-0 z-[9999] bg-black/95 backdrop-blur-md flex items-center justify-center animate-fade-in"
onClick={() => setLightboxOpen(false)}
>
{}
<button
onClick={(e) => {
e.stopPropagation();
setLightboxOpen(false);
}}
className="absolute top-4 right-4 md:top-6 md:right-6 z-50 p-3 bg-black/50 hover:bg-black/70 rounded-full text-white hover:text-[#d4af37] transition-all duration-300 backdrop-blur-sm border border-white/10 hover:border-[#d4af37]/50"
aria-label="Close"
>
<X className="w-6 h-6 md:w-7 md:h-7" />
</button>
{}
{lightboxImages.length > 1 && (
<button
onClick={(e) => {
e.stopPropagation();
setLightboxIndex((prev) => (prev === 0 ? lightboxImages.length - 1 : prev - 1));
}}
className="absolute left-4 md:left-6 z-50 p-3 bg-black/50 hover:bg-black/70 rounded-full text-white hover:text-[#d4af37] transition-all duration-300 backdrop-blur-sm border border-white/10 hover:border-[#d4af37]/50"
aria-label="Previous image"
>
<ChevronLeft className="w-6 h-6 md:w-7 md:h-7" />
</button>
)}
{}
{lightboxImages.length > 1 && (
<button
onClick={(e) => {
e.stopPropagation();
setLightboxIndex((prev) => (prev === lightboxImages.length - 1 ? 0 : prev + 1));
}}
className="absolute right-4 md:right-6 z-50 p-3 bg-black/50 hover:bg-black/70 rounded-full text-white hover:text-[#d4af37] transition-all duration-300 backdrop-blur-sm border border-white/10 hover:border-[#d4af37]/50"
aria-label="Next image"
>
<ChevronRight className="w-6 h-6 md:w-7 md:h-7" />
</button>
)}
{}
<div
className="max-w-7xl max-h-[95vh] w-full h-full flex items-center justify-center p-4 md:p-8"
onClick={(e) => e.stopPropagation()}
>
<div className="relative w-full h-full flex items-center justify-center">
<img
src={lightboxImages[lightboxIndex]}
alt={`Luxury gallery image ${lightboxIndex + 1}`}
className="max-w-full max-h-full object-contain rounded-lg shadow-2xl animate-scale-in"
onError={() => {
console.error(`Failed to load lightbox image: ${lightboxImages[lightboxIndex]}`);
}}
/>
{}
{lightboxImages.length > 1 && (
<div className="absolute bottom-4 md:bottom-6 left-1/2 transform -translate-x-1/2 bg-black/60 backdrop-blur-md text-white px-4 py-2 rounded-full text-sm md:text-base font-medium border border-white/20 shadow-lg">
{lightboxIndex + 1} / {lightboxImages.length}
</div>
)}
{}
{lightboxImages.length > 1 && lightboxImages.length <= 10 && (
<div className="absolute bottom-20 md:bottom-24 left-1/2 transform -translate-x-1/2 flex gap-2 max-w-full overflow-x-auto px-4">
{lightboxImages.map((thumb, idx) => (
<button
key={idx}
onClick={(e) => {
e.stopPropagation();
setLightboxIndex(idx);
}}
className={`flex-shrink-0 w-16 h-16 md:w-20 md:h-20 rounded-lg overflow-hidden border-2 transition-all duration-300 ${
idx === lightboxIndex
? 'border-[#d4af37] shadow-lg shadow-[#d4af37]/50 scale-110'
: 'border-white/30 hover:border-white/60 opacity-70 hover:opacity-100'
}`}
>
<img
src={thumb}
alt={`Thumbnail ${idx + 1}`}
className="w-full h-full object-cover"
/>
</button>
))}
</div>
)}
</div>
</div>
</div>
)}
</>
);
};
export default HomePage;