1598 lines
71 KiB
TypeScript
1598 lines
71 KiB
TypeScript
import React, { useState, useEffect, useRef } from 'react';
|
|
import { motion } from 'motion/react';
|
|
import { Button } from '../ui/button';
|
|
import { Badge } from '../ui/badge';
|
|
import { Card, CardContent } from '../ui/card';
|
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '../ui/dialog';
|
|
import { Input } from '../ui/input';
|
|
import { Label } from '../ui/label';
|
|
import { Textarea } from '../ui/textarea';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';
|
|
import { ImageWithFallback } from '../figma/ImageWithFallback';
|
|
import { navigateTo } from '../Router';
|
|
import { BrandedTag } from '../about/BrandedTag';
|
|
import { PrimaryCTAButton } from '../PrimaryCTAButton';
|
|
import { TestimonialsSection } from '../TestimonialsSection';
|
|
import { CTABannerSection } from '../CTABannerSection';
|
|
// import ImageWithFallback from "./ImageWithFallback"; // your custom component
|
|
|
|
import {
|
|
ArrowRight,
|
|
CheckCircle,
|
|
Calendar,
|
|
Download,
|
|
Building,
|
|
Monitor,
|
|
BookOpen,
|
|
Heart,
|
|
Briefcase,
|
|
Users,
|
|
Target,
|
|
Network,
|
|
MapPin,
|
|
Coffee,
|
|
Wifi,
|
|
Car,
|
|
Shield,
|
|
Play,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
Eye,
|
|
X,
|
|
Clock,
|
|
Star,
|
|
Mail,
|
|
Phone,
|
|
Globe,
|
|
Maximize2,
|
|
TreePine,
|
|
Bed
|
|
} from 'lucide-react';
|
|
const images = [
|
|
"https://images.unsplash.com/photo-1590490360182-c33d57733427?w=1920&h=1080&fit=crop",
|
|
"https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=1920&h=1080&fit=crop",
|
|
"https://images.unsplash.com/photo-1557682224-5b8590cd9ec5?w=1920&h=1080&fit=crop",
|
|
];
|
|
const settings = {
|
|
dots: false,
|
|
infinite: true,
|
|
speed: 2000,
|
|
autoplay: true,
|
|
autoplaySpeed: 4000,
|
|
slidesToShow: 1,
|
|
slidesToScroll: 1,
|
|
fade: true, // smooth fade transition
|
|
arrows: false,
|
|
};
|
|
const offerings = [
|
|
{
|
|
title: "Training Halls & Spaces",
|
|
description: "State-of-the-art training facilities designed for optimal learning experiences",
|
|
icon: Building,
|
|
features: ["4 fully-equipped halls", "Flexible seating arrangements", "Advanced AV systems", "Natural lighting"]
|
|
},
|
|
{
|
|
title: "Technology Infrastructure",
|
|
description: "Cutting-edge technology to support modern learning and collaboration",
|
|
icon: Monitor,
|
|
features: ["High-speed connectivity", "Interactive displays", "Video conferencing", "Digital whiteboards"]
|
|
},
|
|
{
|
|
title: "Learning Resources",
|
|
description: "Comprehensive resources and materials to enhance the learning experience",
|
|
icon: BookOpen,
|
|
features: ["Digital library access", "Learning materials", "Resource centers", "Study spaces"]
|
|
},
|
|
{
|
|
title: "Wellness & Recreation",
|
|
description: "Facilities focused on participant well-being and relaxation during programs",
|
|
icon: Heart,
|
|
features: ["Wellness areas", "Recreation spaces", "Quiet zones", "Outdoor areas"]
|
|
}
|
|
];
|
|
|
|
const targetAudience = [
|
|
{
|
|
title: "Corporate Teams",
|
|
description: "Organizations seeking immersive learning experiences for their leadership development programs",
|
|
icon: Briefcase,
|
|
characteristics: ["Team retreats", "Leadership programs", "Skills workshops", "Strategic planning"]
|
|
},
|
|
{
|
|
title: "Learning Groups",
|
|
description: "Educational institutions and learning organizations hosting intensive development sessions",
|
|
icon: Users,
|
|
characteristics: ["Educational programs", "Intensive workshops", "Collaborative learning", "Group development"]
|
|
},
|
|
{
|
|
title: "Professional Networks",
|
|
description: "Professional associations and networks organizing development events and conferences",
|
|
icon: Network,
|
|
characteristics: ["Professional development", "Networking events", "Industry conferences", "Skill building"]
|
|
}
|
|
];
|
|
|
|
const facilityFeatures = [
|
|
{
|
|
title: "Prime Location",
|
|
description: "Strategically located campus with easy accessibility and beautiful surroundings",
|
|
icon: MapPin,
|
|
details: ["Central location", "Easy transport access", "Scenic environment", "Quiet setting"]
|
|
},
|
|
{
|
|
title: "Accommodation",
|
|
description: "Comfortable residential facilities for extended learning programs",
|
|
icon: Building,
|
|
details: ["On-site accommodation", "Private rooms", "Shared facilities", "24/7 security"]
|
|
},
|
|
{
|
|
title: "Catering Services",
|
|
description: "Professional catering services providing nutritious and delicious meals",
|
|
icon: Coffee,
|
|
details: ["Breakfast included", "Lunch & dinner options", "Dietary accommodations", "Coffee & refreshments"]
|
|
},
|
|
{
|
|
title: "Support Services",
|
|
description: "Comprehensive support services to ensure smooth program delivery",
|
|
icon: Shield,
|
|
details: ["Technical support", "Event coordination", "Housekeeping", "Medical assistance"]
|
|
},
|
|
{
|
|
title: "Technology Infrastructure",
|
|
description: "State-of-the-art technology to support modern learning experiences",
|
|
icon: Monitor,
|
|
details: ["High-speed Wi-Fi", "Smart boards", "Video conferencing", "Audio-visual systems"]
|
|
},
|
|
{
|
|
title: "Wellness Facilities",
|
|
description: "Dedicated spaces for relaxation and well-being during intensive programs",
|
|
icon: Heart,
|
|
details: ["Meditation rooms", "Fitness area", "Outdoor spaces", "Quiet zones"]
|
|
},
|
|
{
|
|
title: "Meeting Spaces",
|
|
description: "Flexible spaces designed for collaboration and group discussions",
|
|
icon: Users,
|
|
details: ["Breakout rooms", "Collaboration areas", "Board rooms", "Informal spaces"]
|
|
},
|
|
{
|
|
title: "Learning Resources",
|
|
description: "Comprehensive resources and materials to enhance the learning experience",
|
|
icon: BookOpen,
|
|
details: ["Digital library", "Resource center", "Study materials", "Reference books"]
|
|
}
|
|
];
|
|
|
|
const amenities = [
|
|
{ name: "High-Speed Wi-Fi", icon: Wifi },
|
|
{ name: "Parking Facilities", icon: Car },
|
|
{ name: "Coffee Lounge", icon: Coffee },
|
|
{ name: "Security Services", icon: Shield },
|
|
{ name: "Recreation Areas", icon: Heart },
|
|
{ name: "Study Spaces", icon: BookOpen },
|
|
{ name: "Meeting Rooms", icon: Users },
|
|
{ name: "Tech Support", icon: Monitor }
|
|
];
|
|
|
|
// Virtual Tour Data
|
|
const virtualTourStops = [
|
|
{
|
|
id: 'training-amphitheater',
|
|
title: 'Training Amphitheater',
|
|
description: 'Inspire your audience in our largest training space featuring tiered seating and state-of-the-art presentation technology.',
|
|
image: 'https://images.unsplash.com/photo-1526045004414-3e7ed02f9ca1?w=1080&h=720&fit=crop',
|
|
capacity: '80-120 attendees',
|
|
keyHighlight: 'Large-Scale Training Excellence',
|
|
zone: 1,
|
|
features: [
|
|
'Tiered amphitheater seating',
|
|
'Professional stage',
|
|
'Advanced acoustics',
|
|
'Multiple screens',
|
|
'Live streaming capability'
|
|
]
|
|
},
|
|
{
|
|
id: 'collaboration-suites',
|
|
title: 'Collaboration Suites',
|
|
description: 'Foster deep connections and intensive learning in our intimate spaces perfect for small group work and coaching sessions.',
|
|
image: 'https://images.unsplash.com/photo-1592565276431-c27e6ad5f46a?w=1080&h=720&fit=crop',
|
|
capacity: '4-8 participants',
|
|
keyHighlight: 'Intimate Learning Environment',
|
|
zone: 2,
|
|
features: [
|
|
'Comfortable seating',
|
|
'Natural light',
|
|
'Privacy glass',
|
|
'Dedicated workspace',
|
|
'Tea/coffee station'
|
|
]
|
|
},
|
|
{
|
|
id: 'outdoor-pavilion',
|
|
title: 'Outdoor Learning Pavilion',
|
|
description: 'Connect with nature while learning in our unique outdoor space that combines fresh air with professional learning environments.',
|
|
image: 'https://images.unsplash.com/photo-1571624436279-b272adf752b5?w=800&h=600&fit=crop',
|
|
capacity: '20-40 participants',
|
|
keyHighlight: 'Nature-Connected Learning',
|
|
zone: 3,
|
|
features: [
|
|
'Weather-protected pavilion',
|
|
'Garden views',
|
|
'Fresh air environment',
|
|
'Flexible seating',
|
|
'Natural acoustics'
|
|
]
|
|
},
|
|
{
|
|
id: 'residential-quarters',
|
|
title: 'Residential Quarters',
|
|
description: 'Extend your learning journey with comfortable accommodation for multi-day programs featuring hotel-standard amenities.',
|
|
image: 'https://images.unsplash.com/photo-1590490360182-c33d57733427?w=800&h=600&fit=crop',
|
|
capacity: '40 single occupancy rooms',
|
|
keyHighlight: 'Premium Residential Experience',
|
|
zone: 4,
|
|
features: [
|
|
'Private en-suite rooms',
|
|
'Work desk and chair',
|
|
'High-speed WiFi',
|
|
'Air conditioning',
|
|
'Garden/courtyard views'
|
|
]
|
|
},
|
|
{
|
|
id: 'executive-boardroom',
|
|
title: 'Executive Boardroom',
|
|
description: 'Host high-level strategic discussions in our sophisticated boardroom designed for executive leadership meetings.',
|
|
image: 'https://images.unsplash.com/photo-1497366216548-37526070297c?w=1080&h=720&fit=crop',
|
|
capacity: '12-16 executives',
|
|
keyHighlight: 'Strategic Leadership Hub',
|
|
zone: 5,
|
|
features: [
|
|
'Premium boardroom table',
|
|
'Executive seating',
|
|
'4K presentation displays',
|
|
'Secure video conferencing',
|
|
'Integrated sound system'
|
|
]
|
|
},
|
|
{
|
|
id: 'innovation-lab',
|
|
title: 'Innovation Lab',
|
|
description: 'Spark creativity and innovation in our flexible workspace designed for design thinking and collaborative problem-solving.',
|
|
image: 'https://images.unsplash.com/photo-1497366412874-3415097a27e7?w=1080&h=720&fit=crop',
|
|
capacity: '15-25 participants',
|
|
keyHighlight: 'Creative Collaboration Space',
|
|
zone: 6,
|
|
features: [
|
|
'Modular furniture setup',
|
|
'Whiteboard walls',
|
|
'Breakout alcoves',
|
|
'Technology integration',
|
|
'Flexible lighting'
|
|
]
|
|
},
|
|
{
|
|
id: 'wellness-retreat',
|
|
title: 'Wellness & Reflection Area',
|
|
description: 'Recharge and reflect in our tranquil wellness space designed for mindfulness and personal development sessions.',
|
|
image: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?w=1080&h=720&fit=crop',
|
|
capacity: '8-15 participants',
|
|
keyHighlight: 'Mindful Leadership Development',
|
|
zone: 7,
|
|
features: [
|
|
'Meditation seating area',
|
|
'Natural lighting',
|
|
'Acoustic privacy',
|
|
'Aromatherapy system',
|
|
'Garden views'
|
|
]
|
|
},
|
|
{
|
|
id: 'digital-learning-studio',
|
|
title: 'Digital Learning Studio',
|
|
description: 'Experience cutting-edge digital learning with our high-tech studio equipped for virtual reality and interactive sessions.',
|
|
image: 'https://images.unsplash.com/photo-1560472354-b33ff0c44a43?w=1080&h=720&fit=crop',
|
|
capacity: '10-20 participants',
|
|
keyHighlight: 'Future of Learning Technology',
|
|
zone: 8,
|
|
features: [
|
|
'VR/AR capabilities',
|
|
'Interactive displays',
|
|
'Recording equipment',
|
|
'Livestream setup',
|
|
'Digital collaboration tools'
|
|
]
|
|
}
|
|
];
|
|
|
|
// Booking Form Interface
|
|
interface BookingForm {
|
|
companyName: string;
|
|
contactName: string;
|
|
email: string;
|
|
phone: string;
|
|
role: string;
|
|
teamSize: string;
|
|
facilityZone: string;
|
|
additionalRequirements: string;
|
|
}
|
|
|
|
export function LearningFacility() {
|
|
// Facility Features Carousel state and functionality
|
|
const [currentSlide, setCurrentSlide] = useState(0);
|
|
const carouselRef = useRef<HTMLDivElement>(null);
|
|
|
|
// Virtual Tour Carousel state and functionality
|
|
const [currentTourSlide, setCurrentTourSlide] = useState(0);
|
|
const tourCarouselRef = useRef<HTMLDivElement>(null);
|
|
|
|
// Responsive carousel settings
|
|
const getCarouselSettings = () => {
|
|
if (typeof window !== 'undefined') {
|
|
if (window.innerWidth >= 1200) {
|
|
return { cardsPerView: 3, cardWidth: 33.333, slideWidth: 33.333 };
|
|
} else if (window.innerWidth >= 768) {
|
|
return { cardsPerView: 2, cardWidth: 50, slideWidth: 50 };
|
|
} else {
|
|
return { cardsPerView: 1, cardWidth: 100, slideWidth: 100 };
|
|
}
|
|
}
|
|
return { cardsPerView: 3, cardWidth: 33.333, slideWidth: 33.333 };
|
|
};
|
|
|
|
// Virtual tour carousel settings
|
|
const getTourCarouselSettings = () => {
|
|
if (typeof window !== 'undefined') {
|
|
if (window.innerWidth >= 1200) {
|
|
return { cardsPerView: 5, cardWidth: 20, slideWidth: 20 };
|
|
} else if (window.innerWidth >= 768) {
|
|
return { cardsPerView: 3, cardWidth: 33.333, slideWidth: 33.333 };
|
|
} else {
|
|
return { cardsPerView: 1, cardWidth: 100, slideWidth: 100 };
|
|
}
|
|
}
|
|
return { cardsPerView: 5, cardWidth: 20, slideWidth: 20 };
|
|
};
|
|
|
|
const [carouselSettings, setCarouselSettings] = useState(getCarouselSettings());
|
|
const [tourCarouselSettings, setTourCarouselSettings] = useState(getTourCarouselSettings());
|
|
const { cardsPerView, cardWidth, slideWidth } = carouselSettings;
|
|
const { cardsPerView: tourCardsPerView, cardWidth: tourCardWidth, slideWidth: tourSlideWidth } = tourCarouselSettings;
|
|
const maxSlide = Math.max(0, facilityFeatures.length - cardsPerView);
|
|
const maxTourSlide = Math.max(0, virtualTourStops.length - tourCardsPerView);
|
|
|
|
// Handle window resize
|
|
useEffect(() => {
|
|
const handleResize = () => {
|
|
setCarouselSettings(getCarouselSettings());
|
|
setTourCarouselSettings(getTourCarouselSettings());
|
|
setCurrentSlide(0); // Reset to first slide on resize
|
|
setCurrentTourSlide(0); // Reset tour carousel on resize
|
|
};
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
return () => window.removeEventListener('resize', handleResize);
|
|
}, []);
|
|
|
|
const scrollLeft = () => {
|
|
if (currentSlide > 0) {
|
|
setCurrentSlide(currentSlide - 1);
|
|
}
|
|
};
|
|
|
|
const scrollRight = () => {
|
|
if (currentSlide < maxSlide) {
|
|
setCurrentSlide(currentSlide + 1);
|
|
}
|
|
};
|
|
|
|
// Virtual Tour Carousel navigation
|
|
const scrollTourLeft = () => {
|
|
if (currentTourSlide > 0) {
|
|
setCurrentTourSlide(currentTourSlide - 1);
|
|
}
|
|
};
|
|
|
|
const scrollTourRight = () => {
|
|
if (currentTourSlide < maxTourSlide) {
|
|
setCurrentTourSlide(currentTourSlide + 1);
|
|
}
|
|
};
|
|
const [currentTourStop, setCurrentTourStop] = useState(0);
|
|
const [isVirtualTourActive, setIsVirtualTourActive] = useState(false);
|
|
const [isBookingModalOpen, setIsBookingModalOpen] = useState(false);
|
|
const [expandedTourCard, setExpandedTourCard] = useState<string | null>(null);
|
|
const [bookingForm, setBookingForm] = useState<BookingForm>({
|
|
companyName: '',
|
|
contactName: '',
|
|
email: '',
|
|
phone: '',
|
|
role: '',
|
|
teamSize: '',
|
|
facilityZone: '',
|
|
additionalRequirements: ''
|
|
});
|
|
|
|
useEffect(() => {
|
|
console.log('LearningFacility component mounted'); // Debug log
|
|
window.scrollTo(0, 0);
|
|
|
|
// Listen for custom booking modal event from CTAPopupModal
|
|
const handleOpenBookingModal = () => {
|
|
console.log('Custom booking modal event received'); // Debug log
|
|
setIsBookingModalOpen(true);
|
|
};
|
|
|
|
window.addEventListener('openBookingModal', handleOpenBookingModal);
|
|
|
|
// Also check if we should auto-open the booking modal based on URL parameters
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
if (urlParams.get('autoBooking') === 'true') {
|
|
console.log('Auto-opening booking modal from URL parameter'); // Debug log
|
|
setTimeout(() => setIsBookingModalOpen(true), 100);
|
|
}
|
|
|
|
return () => {
|
|
window.removeEventListener('openBookingModal', handleOpenBookingModal);
|
|
};
|
|
}, []);
|
|
|
|
const handleStartTour = () => {
|
|
setIsVirtualTourActive(true);
|
|
setCurrentTourStop(0);
|
|
};
|
|
|
|
const handleNextStop = () => {
|
|
if (currentTourStop < virtualTourStops.length - 1) {
|
|
setCurrentTourStop(currentTourStop + 1);
|
|
}
|
|
};
|
|
|
|
const handlePrevStop = () => {
|
|
if (currentTourStop > 0) {
|
|
setCurrentTourStop(currentTourStop - 1);
|
|
}
|
|
};
|
|
|
|
const handleJumpToStop = (index: number) => {
|
|
setCurrentTourStop(index);
|
|
};
|
|
|
|
const handleBookingSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
console.log('Booking submitted:', bookingForm);
|
|
setIsBookingModalOpen(false);
|
|
setBookingForm({
|
|
companyName: '',
|
|
contactName: '',
|
|
email: '',
|
|
phone: '',
|
|
role: '',
|
|
teamSize: '',
|
|
facilityZone: '',
|
|
additionalRequirements: ''
|
|
});
|
|
|
|
// Clean up URL parameter
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.delete('autoBooking');
|
|
window.history.replaceState({}, '', url.toString());
|
|
|
|
// Here you would typically send the booking request to your backend
|
|
alert('Booking request submitted successfully! We will contact you within 24 hours.');
|
|
};
|
|
|
|
const handleBookingModalClose = (open: boolean) => {
|
|
console.log('Booking modal close triggered, open:', open); // Debug log
|
|
setIsBookingModalOpen(open);
|
|
|
|
if (!open) {
|
|
// Clean up URL parameter when closing
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.delete('autoBooking');
|
|
window.history.replaceState({}, '', url.toString());
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
|
|
|
{/* Hero Section */}
|
|
{/* Service-style hero: full-bleed image with dark gradient, centered content, and stat strip */}
|
|
<section className="relative min-h-[90vh] flex flex-col" style={{ backgroundColor: '#FFFFFF' }}>
|
|
|
|
{/* Background image + overlay */}
|
|
<div className="absolute inset-0 z-0">
|
|
<ImageWithFallback
|
|
src="https://images.unsplash.com/photo-1590490360182-c33d57733427?w=1920&h=1080&fit=crop"
|
|
alt="KLC Learning Facility - campus and interiors"
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
|
|
<div className="absolute inset-0 bg-gradient-to-r from-black/80 via-black/70 to-black/60"></div>
|
|
</div>
|
|
|
|
|
|
{/* Centered content */}
|
|
<div className="relative z-10 flex-1 flex items-center">
|
|
<div className="mx-auto section-margin-x">
|
|
<div className="text-center max-w-5xl mx-auto">
|
|
<div className="branded-tag-system-white mb-8 justify-center">
|
|
<div className="dot"></div>
|
|
<span className="text">World-Class Facility</span>
|
|
</div>
|
|
|
|
<h1 className="text-h1-white mb-6">Learning Facility</h1>
|
|
|
|
<p className="text-body-lg-white mb-8 max-w-2xl mx-auto">
|
|
A purpose-built campus for immersive leadership learning. State-of-the-art facilities
|
|
designed to foster transformation, collaboration, and growth.
|
|
</p>
|
|
|
|
<div className="flex flex-col sm:flex-row gap-4 mb-12 justify-center">
|
|
<PrimaryCTAButton
|
|
text="Take Virtual Tour"
|
|
onClick={handleStartTour}
|
|
ariaLabel="Take virtual tour"
|
|
className="primary-cta-button-blue cta-text-white"
|
|
/>
|
|
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => {
|
|
console.log('Book Facility button clicked');
|
|
setIsBookingModalOpen(true);
|
|
}}
|
|
data-booking-trigger
|
|
className="management-dev-glassmorphic-btn text-body px-8 py-4 min-h-[52px] border transition-all duration-300"
|
|
style={{
|
|
fontFamily: 'var(--font-family-base)'
|
|
}}
|
|
aria-label="Book facility"
|
|
>
|
|
<Calendar className="w-5 h-5 mr-2" />
|
|
Book Facility
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stat strip */}
|
|
<div className="relative z-10 border-t border-white/20 backdrop-blur-sm bg-black/20">
|
|
<div className="mx-auto section-margin-x py-8">
|
|
<div className="grid grid-cols-3 gap-8 text-center max-w-4xl mx-auto">
|
|
<div>
|
|
<div className="text-h2-white mb-2">2 Acres</div>
|
|
<div className="text-small-white opacity-90">Campus Size</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-h2-white mb-2">80</div>
|
|
<div className="text-small-white opacity-90">Max Capacity</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-h2-white mb-2">4</div>
|
|
<div className="text-small-white opacity-90">Training Halls</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* What We Offer Section */}
|
|
<section className="py-20" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<div className="mx-auto section-margin-x">
|
|
<div className="text-center mb-16 max-w-4xl mx-auto">
|
|
<h2 className="text-h2 mb-6">
|
|
What We <span className="text-primary">Offer</span>
|
|
</h2>
|
|
<p className="text-body-lg text-muted">
|
|
Comprehensive executive leadership development solutions designed to transform
|
|
senior leadership capabilities and drive organizational excellence.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Match Services page layout: sticky left content + vertical scroll-in cards on the right */}
|
|
<div className="grid grid-cols-12 gap-12 max-w-7xl mx-auto">
|
|
{/* Left: Sticky intro */}
|
|
<div className="col-span-12 lg:col-span-5 sticky top-24 self-start">
|
|
<div className="recognition-header pr-0 lg:pr-8">
|
|
<BrandedTag text="Facility Offerings" />
|
|
<h3 className="text-h2 mb-6">Spaces and Services that Enable Transformation</h3>
|
|
<p className="text-body-lg text-muted">
|
|
Purpose-built environments, technology, resources, and wellness support that make
|
|
immersive leadership programs seamless and effective.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right: Stacked cards with staggered on-scroll animation */}
|
|
<div className="col-span-12 lg:col-span-7">
|
|
<div className="space-y-6" role="list" aria-label="Learning facility offerings">
|
|
{offerings.map((offering, index) => {
|
|
const Icon = offering.icon;
|
|
return (
|
|
<motion.div
|
|
key={offering.title}
|
|
role="listitem"
|
|
initial={{ opacity: 0, y: 40 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.6, delay: index * 0.15, ease: [0.25, 0.46, 0.45, 0.94] }}
|
|
viewport={{ once: true, amount: 0.2 }}
|
|
className="group focus-ring"
|
|
style={{ willChange: 'transform, opacity' }}
|
|
>
|
|
<div
|
|
className="p-8 transition-all duration-300 hover:shadow-xl hover:-translate-y-1 border bg-white"
|
|
style={{ borderColor: 'var(--color-border)', borderRadius: '12px' }}
|
|
>
|
|
<div className="flex items-start justify-between mb-6">
|
|
<div
|
|
className="w-14 h-14 flex items-center justify-center transition-transform duration-300 group-hover:scale-110"
|
|
style={{ backgroundColor: 'var(--color-primary)', borderRadius: '12px', color: 'white' }}
|
|
aria-hidden="true"
|
|
>
|
|
<Icon className="w-7 h-7" />
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="text-h4 mb-3" id={`offer-title-${index}`}>{offering.title}</h4>
|
|
<p className="text-small text-muted leading-relaxed mb-5">{offering.description}</p>
|
|
<div className="space-y-2">
|
|
{offering.features.map((feature, featureIndex) => (
|
|
<div key={featureIndex} className="flex items-center gap-2">
|
|
<CheckCircle className="w-4 h-4 text-primary flex-shrink-0" />
|
|
<span className="text-small text-muted">{feature}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Who It's For Section - match Services page (left intro + right accordion list) */}
|
|
<section className="py-20" style={{ backgroundColor: '#FFFFFF' }}>
|
|
<div className="mx-auto section-margin-x">
|
|
{/* Our Expertise "Our Services" style - horizontal carousel cards */}
|
|
<div className="max-w-7xl mx-auto">
|
|
{/* Header with eyebrow and controls */}
|
|
<div className="mb-12 relative">
|
|
<div className="flex-1 max-w-2xl">
|
|
<div className="branded-tag-system mb-6">
|
|
<div className="dot"></div>
|
|
<span className="text">LEARNING FACILITY</span>
|
|
</div>
|
|
<h2 className="text-h2 mb-4 leading-tight">Who It's <span className="text-primary">For</span></h2>
|
|
<p className="text-body-lg text-muted leading-relaxed">
|
|
Our learning facility supports immersive leadership programs for corporate teams, learning groups,
|
|
and professional networks seeking high-impact environments.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Simple controls that scroll the container; no state changes outside this section */}
|
|
<div className="absolute bottom-0 right-0 flex items-center gap-4">
|
|
<span id="lf-who-counter" className="text-body text-muted font-medium">01 / {String(targetAudience.length).padStart(2, '0')}</span>
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={() => {
|
|
const el = document.getElementById('lf-who-carousel');
|
|
if (!el) return;
|
|
el.scrollBy({ left: -384, behavior: 'smooth' });
|
|
setTimeout(() => {
|
|
const idx = Math.round((el.scrollLeft + el.clientWidth * 0.1) / (el.clientWidth * 0.75));
|
|
const num = Math.max(1, Math.min(targetAudience.length, idx + 1));
|
|
const counter = document.getElementById('lf-who-counter');
|
|
if (counter) counter.textContent = `${String(num).padStart(2, '0')} / ${String(targetAudience.length).padStart(2, '0')}`;
|
|
}, 300);
|
|
}}
|
|
className="w-12 h-12 rounded-lg border-2 border-gray-200 flex items-center justify-center hover:border-primary hover:bg-primary hover:text-white transition-all duration-300"
|
|
aria-label="Previous audience"
|
|
>
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" /></svg>
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
const el = document.getElementById('lf-who-carousel');
|
|
if (!el) return;
|
|
el.scrollBy({ left: 384, behavior: 'smooth' });
|
|
setTimeout(() => {
|
|
const idx = Math.round((el.scrollLeft + el.clientWidth * 0.1) / (el.clientWidth * 0.75));
|
|
const num = Math.max(1, Math.min(targetAudience.length, idx + 1));
|
|
const counter = document.getElementById('lf-who-counter');
|
|
if (counter) counter.textContent = `${String(num).padStart(2, '0')} / ${String(targetAudience.length).padStart(2, '0')}`;
|
|
}, 300);
|
|
}}
|
|
className="w-12 h-12 rounded-lg border-2 border-gray-200 flex items-center justify-center hover:border-primary hover:bg-primary hover:text-white transition-all duration-300"
|
|
aria-label="Next audience"
|
|
>
|
|
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path strokeLinecap="round" strokeLinejoin="round" d="M9 5l7 7-7 7" /></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Carousel */}
|
|
<div className="relative">
|
|
<div className="overflow-hidden">
|
|
<div
|
|
id="lf-who-carousel"
|
|
className="flex transition-transform duration-700 ease-in-out gap-4 overflow-x-auto scroll-smooth services-cards-carousel"
|
|
style={{ scrollSnapType: 'x mandatory', msOverflowStyle: 'none', scrollbarWidth: 'none' }}
|
|
>
|
|
{targetAudience.map((audience, index) => (
|
|
<div
|
|
key={`who-${audience.title}-${index}`}
|
|
className="flex-shrink-0 w-[70%] mr-7"
|
|
style={{ scrollSnapAlign: 'start' }}
|
|
>
|
|
<div className="border border-gray-200 h-full overflow-hidden rounded-xl bg-white card-shadow-elevated">
|
|
<div className="p-6">
|
|
<div className="flex items-stretch">
|
|
{/* Left: Image with 10px top/left/bottom spacing and 5px radius */}
|
|
<div
|
|
className="flex-shrink-0 self-stretch"
|
|
style={{ margin: '6px 0 6px 10px', borderRadius: '5px', overflow: 'hidden', width: '270px' }}
|
|
>
|
|
<ImageWithFallback
|
|
src={
|
|
audience.title.includes('Corporate')
|
|
? 'https://images.unsplash.com/photo-1553877522-43269d4ea984?w=640&h=640&fit=crop'
|
|
: audience.title.includes('Learning')
|
|
? 'https://images.unsplash.com/photo-1523580846011-d3a5bc25702b?w=640&h=640&fit=crop'
|
|
: audience.title.includes('Professional')
|
|
? 'https://images.unsplash.com/photo-1551836022-d5d88e9218df?w=640&h=640&fit=crop'
|
|
: 'https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=640&h=640&fit=crop'
|
|
}
|
|
alt={`${audience.title} visual`}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
|
|
{/* Right: Existing text content aligned neatly */}
|
|
<div className="flex-1 pl-7 flex flex-col">
|
|
{/* Header */}
|
|
<div className="flex items-center gap-4 mb-4">
|
|
<div
|
|
className="w-14 h-14 rounded-xl flex items-center justify-center flex-shrink-0"
|
|
style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}
|
|
>
|
|
{React.createElement(audience.icon, { className: 'w-7 h-7 text-primary' })}
|
|
</div>
|
|
<h3 className="text-h4 text-black leading-tight">{audience.title}</h3>
|
|
</div>
|
|
|
|
{/* Description */}
|
|
<p className="text-body text-muted leading-relaxed mb-6">{audience.description}</p>
|
|
|
|
{/* Key Features (Characteristics) */}
|
|
<div className="mb-6">
|
|
<h4 className="text-subhead text-black mb-4 font-medium">Key Characteristics:</h4>
|
|
<div className="grid md:grid-cols-2 gap-3">
|
|
{audience.characteristics.map((feature: string, featureIndex: number) => (
|
|
<div key={featureIndex} className="flex items-start gap-2">
|
|
<svg className="w-4 h-4 flex-shrink-0 mt-0.5 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M20 6L9 17l-5-5" /></svg>
|
|
<span className="text-small text-muted leading-relaxed">{feature}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Expected Outcomes */}
|
|
<div className="rounded-lg p-4" style={{ backgroundColor: 'rgba(247, 247, 253, 0.5)' }}>
|
|
<h4 className="text-subhead text-black mb-3 font-medium">Expected Outcomes:</h4>
|
|
<div className="flex items-start gap-3">
|
|
<svg className="w-4 h-4 flex-shrink-0 mt-0.5 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M12 19V6m0 0l-7 7m7-7l7 7" /></svg>
|
|
<span className="text-small text-muted leading-relaxed">
|
|
Ideal for {audience.title.toLowerCase()} to achieve focused learning outcomes and stronger alignment.
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Facility Features Section */}
|
|
<section className="py-20" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<div className="mx-auto section-margin-x">
|
|
<div className="mb-12 relative max-w-7xl mx-auto">
|
|
<div className="flex-1 max-w-3xl">
|
|
<BrandedTag text="FACILITY FEATURES" />
|
|
<h2 className="text-h2 mb-4 leading-tight">
|
|
Facility <span className="text-primary">Features</span>
|
|
</h2>
|
|
<p className="text-body-lg text-muted leading-relaxed">
|
|
Every aspect of our facility is designed to enhance learning outcomes
|
|
and provide an exceptional experience for participants and facilitators.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Navigation Controls - Bottom right positioning */}
|
|
<div className="absolute bottom-0 right-0 flex items-center gap-4">
|
|
<span className="text-body text-muted font-medium">
|
|
{String(currentSlide + 1).padStart(2, '0')} / {String(Math.ceil(facilityFeatures.length / cardsPerView)).padStart(2, '0')}
|
|
</span>
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={scrollLeft}
|
|
disabled={currentSlide === 0}
|
|
className="w-12 h-12 rounded-lg border-2 border-gray-200 flex items-center justify-center hover:border-primary hover:bg-primary hover:text-white disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white disabled:hover:text-black transition-all duration-300"
|
|
aria-label="Previous facility features"
|
|
>
|
|
<ChevronLeft className="w-5 h-5" />
|
|
</button>
|
|
<button
|
|
onClick={scrollRight}
|
|
disabled={currentSlide >= maxSlide}
|
|
className="w-12 h-12 rounded-lg border-2 border-gray-200 flex items-center justify-center hover:border-primary hover:bg-primary hover:text-white disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white disabled:hover:text-black transition-all duration-300"
|
|
aria-label="Next facility features"
|
|
>
|
|
<ChevronRight className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Carousel Container */}
|
|
<div className="relative max-w-7xl mx-auto">
|
|
{/* Navigation Controls - Positioned at bottom right */}
|
|
|
|
|
|
{/* Carousel Container */}
|
|
<div className="overflow-hidden" style={{ paddingBottom: '80px' }}>
|
|
<div
|
|
ref={carouselRef}
|
|
className="flex transition-transform duration-500 ease-in-out gap-6"
|
|
style={{ transform: `translateX(-${currentSlide * slideWidth}%)` }}
|
|
>
|
|
{facilityFeatures.map((feature, index) => {
|
|
const Icon = feature.icon;
|
|
return (
|
|
<Card key={index} className="flex-shrink-0 border hover:shadow-lg transition-shadow duration-300" style={{ backgroundColor: 'var(--color-bg-white)', width: `${cardWidth}%` }}>
|
|
<CardContent className="p-8 text-center">
|
|
<div className="w-full h-[180px] overflow-hidden rounded-lg mx-auto mb-6">
|
|
<ImageWithFallback
|
|
src={
|
|
feature.title.toLowerCase().includes('wifi')
|
|
? 'https://images.unsplash.com/photo-1518770660439-4636190af475?w=480&h=320&fit=crop&auto=format&dpr=2'
|
|
: feature.title.toLowerCase().includes('coffee') || feature.title.toLowerCase().includes('café') || feature.title.toLowerCase().includes('cafe')
|
|
? 'https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=480&h=320&fit=crop&auto=format&dpr=2'
|
|
: feature.title.toLowerCase().includes('parking') || feature.title.toLowerCase().includes('car')
|
|
? 'https://images.unsplash.com/photo-1518306727298-4c17e1bf8cff?w=480&h=320&fit=crop&auto=format&dpr=2'
|
|
: feature.title.toLowerCase().includes('security') || feature.title.toLowerCase().includes('safety')
|
|
? 'https://images.unsplash.com/photo-1583511655857-d19b40a7a54e?w=480&h=320&fit=crop&auto=format&dpr=2'
|
|
: feature.title.toLowerCase().includes('bed') || feature.title.toLowerCase().includes('accommodation') || feature.title.toLowerCase().includes('stay')
|
|
? 'https://images.unsplash.com/photo-1505691938895-1758d7feb511?w=480&h=320&fit=crop&auto=format&dpr=2'
|
|
: feature.title.toLowerCase().includes('nature') || feature.title.toLowerCase().includes('outdoor')
|
|
? 'https://images.unsplash.com/photo-1501785888041-af3ef285b470?w=480&h=320&fit=crop&auto=format&dpr=2'
|
|
: feature.title.toLowerCase().includes('meeting') || feature.title.toLowerCase().includes('room') || feature.title.toLowerCase().includes('facility')
|
|
? 'https://images.unsplash.com/photo-1557800636-894a64c1696f?w=480&h=320&fit=crop&auto=format&dpr=2'
|
|
: 'https://images.unsplash.com/photo-1524758631624-e2822e304c36?w=480&h=320&fit=crop&auto=format&dpr=2'
|
|
}
|
|
alt={`${feature.title} image`}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
|
|
<h3 className="text-h4 mb-4">{feature.title}</h3>
|
|
|
|
<p className="text-body text-muted mb-6">{feature.description}</p>
|
|
|
|
<div className="space-y-2">
|
|
{feature.details.map((detail, detailIndex) => (
|
|
<div key={detailIndex} className="flex items-center gap-2 text-body">
|
|
<CheckCircle className="w-4 h-4 flex-shrink-0 text-primary" />
|
|
<span className="text-left text-muted">{detail}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Amenities Section */}
|
|
<section className="py-20" style={{ backgroundColor: 'rgba(0, 0, 0, 0.03)' }}>
|
|
<div className="mx-auto section-margin-x">
|
|
<div className="text-center mb-16 max-w-4xl mx-auto">
|
|
<h2 className="text-h2 mb-6">
|
|
World-Class <span className="text-primary">Amenities</span>
|
|
</h2>
|
|
<p className="text-body-lg text-muted">
|
|
Premium amenities and services to ensure comfort, productivity, and an
|
|
exceptional learning environment for all participants.
|
|
</p>
|
|
</div>
|
|
|
|
</div>
|
|
<div className="relative w-full">
|
|
<div className="relative overflow-hidden">
|
|
<div className="flex items-stretch gap-4 whitespace-nowrap scroll-left" style={{ animationDuration: '35s' }}>
|
|
{[0, 1].map((loop) => (
|
|
<div key={`amenities-loop-${loop}`} className="flex items-stretch gap-4">
|
|
{amenities.map((amenity, index) => {
|
|
const Icon = amenity.icon;
|
|
return (
|
|
<div
|
|
key={`amenity-row1-${loop}-${index}`}
|
|
className="flex items-center gap-4 rounded-lg p-4 shadow-sm border"
|
|
style={{ backgroundColor: 'var(--color-bg-white)' }}
|
|
>
|
|
<div
|
|
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
|
style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}
|
|
>
|
|
<Icon className="w-6 h-6 text-primary" />
|
|
</div>
|
|
<span className="text-body" style={{ fontWeight: 'var(--font-weight-subhead)' }}>
|
|
{amenity.name}
|
|
</span>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="flex items-stretch gap-4 whitespace-nowrap scroll-right mt-4" style={{ animationDuration: '30s' }}>
|
|
{[0, 1].map((loop) => (
|
|
<div key={`amenities-loop-bottom-${loop}`} className="flex items-stretch gap-4">
|
|
{amenities.map((amenity, index) => {
|
|
const Icon = amenity.icon;
|
|
return (
|
|
<div
|
|
key={`amenity-row2-${loop}-${index}`}
|
|
className="flex items-center gap-4 rounded-lg p-4 shadow-sm border"
|
|
style={{ backgroundColor: 'var(--color-bg-white)' }}
|
|
>
|
|
<div
|
|
className="w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0"
|
|
style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}
|
|
>
|
|
<Icon className="w-6 h-6 text-primary" />
|
|
</div>
|
|
<span className="text-body" style={{ fontWeight: 'var(--font-weight-subhead)' }}>
|
|
{amenity.name}
|
|
</span>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Virtual Tour Section */}
|
|
<section className="py-20" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<div className="mx-auto section-margin-x">
|
|
{!isVirtualTourActive ? (
|
|
/* Tour Preview */
|
|
<div className="max-w-7xl mx-auto space-y-12">
|
|
{/* Header Section - left aligned with navigation controls */}
|
|
<div className="mb-12 relative max-w-7xl mx-auto">
|
|
<div className="flex-1 max-w-3xl">
|
|
<BrandedTag text="VIRTUAL EXPERIENCE" />
|
|
<h2 className="text-h2 mb-4 leading-tight">
|
|
Explore Our Learning Spaces
|
|
</h2>
|
|
<p className="text-body-lg text-muted leading-relaxed">
|
|
Take an interactive tour of our eight distinct learning environments, each designed to inspire transformation and foster collaboration.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Navigation Controls - Bottom right positioning */}
|
|
<div className="absolute bottom-0 right-0 flex items-center gap-4">
|
|
<span className="text-body text-muted font-medium">
|
|
{String(currentTourSlide + 1).padStart(2, '0')} / {String(Math.ceil(virtualTourStops.length / tourCardsPerView)).padStart(2, '0')}
|
|
</span>
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={scrollTourLeft}
|
|
disabled={currentTourSlide === 0}
|
|
className="w-12 h-12 rounded-lg border-2 border-gray-200 flex items-center justify-center hover:border-primary hover:bg-primary hover:text-white disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white disabled:hover:text-black transition-all duration-300"
|
|
aria-label="Previous learning spaces"
|
|
>
|
|
<ChevronLeft className="w-5 h-5" />
|
|
</button>
|
|
<button
|
|
onClick={scrollTourRight}
|
|
disabled={currentTourSlide >= maxTourSlide}
|
|
className="w-12 h-12 rounded-lg border-2 border-gray-200 flex items-center justify-center hover:border-primary hover:bg-primary hover:text-white disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-white disabled:hover:text-black transition-all duration-300"
|
|
aria-label="Next learning spaces"
|
|
>
|
|
<ChevronRight className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Carousel Container */}
|
|
<div className="relative overflow-hidden">
|
|
<div
|
|
ref={tourCarouselRef}
|
|
className="flex transition-transform duration-700 ease-in-out"
|
|
style={{
|
|
transform: `translateX(-${currentTourSlide * tourSlideWidth}%)`,
|
|
width: `${(virtualTourStops.length / tourCardsPerView) * 100}%`
|
|
}}
|
|
>
|
|
{virtualTourStops.map((stop) => (
|
|
<div
|
|
key={stop.id}
|
|
className="flex-shrink-0 px-3"
|
|
style={{ width: `${tourCardWidth}%` }}
|
|
>
|
|
<Card
|
|
className="group relative cursor-pointer overflow-hidden border-0 rounded-xl h-full"
|
|
onClick={() => setExpandedTourCard(stop.id)}
|
|
>
|
|
{/* Image background */}
|
|
<div className="relative h-72 sm:h-80 lg:h-[360px]">
|
|
<ImageWithFallback
|
|
src={stop.image}
|
|
alt={stop.title}
|
|
className="absolute inset-0 w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
|
/>
|
|
{/* Gradient overlay for legibility */}
|
|
<div className="absolute inset-0 bg-gradient-to-b from-black/10 via-black/50 to-black/80"></div>
|
|
|
|
{/* Glass badge (Zone) */}
|
|
<div className="absolute top-4 left-4 flex items-center gap-2">
|
|
<div
|
|
className="w-10 h-10 rounded-lg flex items-center justify-center"
|
|
style={{ backgroundColor: 'rgba(255, 255, 255, 0.18)', backdropFilter: 'blur(10px)' }}
|
|
>
|
|
<Building className="w-5 h-5 text-white" />
|
|
</div>
|
|
<Badge
|
|
className="text-small px-2 py-1"
|
|
style={{ backgroundColor: 'var(--color-accent)', color: 'var(--color-black)', fontFamily: 'var(--font-family-base)' }}
|
|
>
|
|
Zone {stop.zone}
|
|
</Badge>
|
|
</div>
|
|
|
|
{/* Text overlay inside the image */}
|
|
<div className="absolute inset-x-0 bottom-0 p-5">
|
|
<h3 className="text-h4-white mb-2">{stop.title}</h3>
|
|
<p className="text-small-white opacity-90 line-clamp-2">{stop.description}</p>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Start Tour CTA */}
|
|
<div className="pt-2 flex items-center justify-between gap-4 flex-wrap">
|
|
<div className="text-muted text-small">
|
|
Preview complete? Launch the full interactive tour to navigate each zone.
|
|
</div>
|
|
<div className="hero-slide-button">
|
|
<PrimaryCTAButton
|
|
text="Start Interactive Tour"
|
|
onClick={handleStartTour}
|
|
ariaLabel="Start interactive virtual tour"
|
|
className="primary-cta-button-blue cta-text-black"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
/* Active Virtual Tour */
|
|
<div className="max-w-6xl mx-auto">
|
|
{/* Tour Header */}
|
|
<div className="text-center mb-8">
|
|
<Badge
|
|
className="text-small px-4 py-2 mb-4"
|
|
style={{
|
|
backgroundColor: 'rgba(4, 4, 91, 0.1)',
|
|
color: 'var(--color-primary)',
|
|
fontFamily: 'var(--font-family-base)'
|
|
}}
|
|
>
|
|
Interactive Virtual Tour
|
|
</Badge>
|
|
<h2 className="text-h2 mb-4">
|
|
{virtualTourStops[currentTourStop].title}
|
|
</h2>
|
|
<p className="text-body-lg text-muted max-w-3xl mx-auto">
|
|
{virtualTourStops[currentTourStop].description}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Tour Image */}
|
|
<div className="relative mb-8">
|
|
<div className="aspect-video rounded-2xl overflow-hidden">
|
|
<ImageWithFallback
|
|
src={virtualTourStops[currentTourStop].image}
|
|
alt={virtualTourStops[currentTourStop].title}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
|
|
{/* Navigation Arrows */}
|
|
<button
|
|
onClick={handlePrevStop}
|
|
disabled={currentTourStop === 0}
|
|
className="absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/90 backdrop-blur-sm rounded-full flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed hover:bg-white transition-all"
|
|
>
|
|
<ChevronLeft className="w-6 h-6" />
|
|
</button>
|
|
|
|
<button
|
|
onClick={handleNextStop}
|
|
disabled={currentTourStop === virtualTourStops.length - 1}
|
|
className="absolute right-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/90 backdrop-blur-sm rounded-full flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed hover:bg-white transition-all"
|
|
>
|
|
<ChevronRight className="w-6 h-6" />
|
|
</button>
|
|
|
|
{/* Stop Indicator */}
|
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2">
|
|
<div className="bg-white/90 backdrop-blur-sm rounded-full px-4 py-2">
|
|
<span className="text-small font-medium">
|
|
{currentTourStop + 1} of {virtualTourStops.length}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tour Features */}
|
|
<div className="grid md:grid-cols-2 gap-8 mb-8">
|
|
<div>
|
|
<h3 className="text-h4 mb-4">Key Features</h3>
|
|
<div className="space-y-3">
|
|
{virtualTourStops[currentTourStop].features.map((feature, index) => (
|
|
<div key={index} className="flex items-center gap-3">
|
|
<CheckCircle className="w-5 h-5 text-green-600 flex-shrink-0" />
|
|
<span className="text-body">{feature}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="text-h4 mb-4">Space Details</h3>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center gap-3">
|
|
<MapPin className="w-5 h-5 text-muted flex-shrink-0" />
|
|
<div>
|
|
<div className="text-body">Zone {virtualTourStops[currentTourStop].zone}</div>
|
|
<div className="text-small text-muted">Campus Location</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Users className="w-5 h-5 text-muted flex-shrink-0" />
|
|
<div>
|
|
<div className="text-body">{virtualTourStops[currentTourStop].capacity}</div>
|
|
<div className="text-small text-muted">Capacity</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tour Navigation */}
|
|
<div className="flex justify-center gap-2 mb-8">
|
|
{virtualTourStops.map((_, index) => (
|
|
<button
|
|
key={index}
|
|
onClick={() => handleJumpToStop(index)}
|
|
className={`w-3 h-3 rounded-full transition-all ${index === currentTourStop
|
|
? 'bg-primary w-8'
|
|
: 'bg-gray-300 hover:bg-gray-400'
|
|
}`}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
{/* End Tour Button */}
|
|
<div className="text-center">
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => setIsVirtualTourActive(false)}
|
|
className="text-body"
|
|
>
|
|
<X className="w-5 h-5 mr-2" />
|
|
End Tour
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Tour Card Modal */}
|
|
<Dialog open={!!expandedTourCard} onOpenChange={() => setExpandedTourCard(null)}>
|
|
<DialogContent className="max-w-2xl">
|
|
{expandedTourCard && (
|
|
<>
|
|
<DialogHeader className="pb-6">
|
|
<div className="text-center space-y-4">
|
|
<div
|
|
className="w-20 h-20 rounded-2xl flex items-center justify-center mx-auto"
|
|
style={{ backgroundColor: 'rgba(4, 4, 91, 0.1)' }}
|
|
>
|
|
<Building className="w-10 h-10 text-primary" />
|
|
</div>
|
|
<DialogTitle className="text-h3">
|
|
{virtualTourStops.find(stop => stop.id === expandedTourCard)?.title}
|
|
</DialogTitle>
|
|
<DialogDescription>
|
|
Explore this learning space in detail. You can start an interactive tour from this location or book this specific area for your next event.
|
|
</DialogDescription>
|
|
</div>
|
|
</DialogHeader>
|
|
|
|
<div className="py-6 space-y-6">
|
|
{/* Featured Image */}
|
|
<div className="relative h-64 rounded-2xl overflow-hidden">
|
|
<ImageWithFallback
|
|
src={virtualTourStops.find(stop => stop.id === expandedTourCard)?.image || ''}
|
|
alt={virtualTourStops.find(stop => stop.id === expandedTourCard)?.title || ''}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
<div className="absolute top-6 left-6">
|
|
<Badge
|
|
className="text-body px-4 py-2"
|
|
style={{
|
|
backgroundColor: 'var(--color-accent)',
|
|
color: 'var(--color-black)',
|
|
fontFamily: 'var(--font-family-base)'
|
|
}}
|
|
>
|
|
{virtualTourStops.find(stop => stop.id === expandedTourCard)?.keyHighlight}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Description */}
|
|
<p className="text-body-lg text-muted text-center">
|
|
{virtualTourStops.find(stop => stop.id === expandedTourCard)?.description}
|
|
</p>
|
|
|
|
{/* Action Buttons */}
|
|
<div className="flex gap-4 justify-center">
|
|
<Button
|
|
onClick={() => {
|
|
setExpandedTourCard(null);
|
|
handleStartTour();
|
|
}}
|
|
className="text-body"
|
|
style={{
|
|
backgroundColor: 'var(--color-primary)',
|
|
color: 'white'
|
|
}}
|
|
>
|
|
<Play className="w-5 h-5 mr-2" />
|
|
Start Tour Here
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => setIsBookingModalOpen(true)}
|
|
className="text-body"
|
|
>
|
|
<Calendar className="w-5 h-5 mr-2" />
|
|
Book This Space
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Booking Modal */}
|
|
<Dialog open={isBookingModalOpen} onOpenChange={setIsBookingModalOpen}>
|
|
<DialogContent className="max-w-md">
|
|
<DialogHeader>
|
|
<DialogTitle className="text-h3">Book Learning Facility</DialogTitle>
|
|
<DialogDescription>
|
|
Submit your facility booking request with your preferred dates, team size, and specific requirements. We'll contact you within 24 hours to confirm availability and discuss details.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<form onSubmit={handleBookingSubmit} className="space-y-4 pt-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<Label htmlFor="companyName">Company Name</Label>
|
|
<Input
|
|
id="companyName"
|
|
value={bookingForm.companyName}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, companyName: e.target.value })}
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="contactName">Contact Name</Label>
|
|
<Input
|
|
id="contactName"
|
|
value={bookingForm.contactName}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, contactName: e.target.value })}
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<Label htmlFor="email">Email</Label>
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
value={bookingForm.email}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, email: e.target.value })}
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="phone">Phone</Label>
|
|
<Input
|
|
id="phone"
|
|
value={bookingForm.phone}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, phone: e.target.value })}
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<Label htmlFor="role">Your Role</Label>
|
|
<Input
|
|
id="role"
|
|
value={bookingForm.role}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, role: e.target.value })}
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="teamSize">Team Size</Label>
|
|
<Select value={bookingForm.teamSize} onValueChange={(value: any) => setBookingForm({ ...bookingForm, teamSize: value })}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select size" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="1-10">1-10 people</SelectItem>
|
|
<SelectItem value="11-25">11-25 people</SelectItem>
|
|
<SelectItem value="26-50">26-50 people</SelectItem>
|
|
<SelectItem value="51-80">51-80 people</SelectItem>
|
|
<SelectItem value="80+">80+ people</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="facilityZone">Preferred Zone</Label>
|
|
<Select value={bookingForm.facilityZone} onValueChange={(value: any) => setBookingForm({ ...bookingForm, facilityZone: value })}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select zone" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="training-amphitheater">Training Amphitheater</SelectItem>
|
|
<SelectItem value="collaboration-suites">Collaboration Suites</SelectItem>
|
|
<SelectItem value="outdoor-pavilion">Outdoor Pavilion</SelectItem>
|
|
<SelectItem value="residential-quarters">Residential Quarters</SelectItem>
|
|
<SelectItem value="flexible">Flexible / Any Zone</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="requirements">Additional Requirements</Label>
|
|
<Textarea
|
|
id="requirements"
|
|
value={bookingForm.additionalRequirements}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, additionalRequirements: e.target.value })}
|
|
placeholder="Tell us about your event, special requirements, catering needs, etc."
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex gap-3 pt-4">
|
|
<Button
|
|
type="submit"
|
|
className="flex-1"
|
|
style={{
|
|
backgroundColor: 'var(--color-primary)',
|
|
color: 'white'
|
|
}}
|
|
>
|
|
Submit Booking Request
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={() => setIsBookingModalOpen(false)}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
{/* Booking Modal */}
|
|
<Dialog open={isBookingModalOpen} onOpenChange={setIsBookingModalOpen}>
|
|
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle className="text-h3 mb-2">Book Our Learning Facility</DialogTitle>
|
|
<DialogDescription className="text-body text-muted">
|
|
Submit your booking request and we'll get back to you within 24 hours to confirm availability and discuss your requirements.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<form onSubmit={handleBookingSubmit} className="space-y-6 mt-6">
|
|
<div className="grid md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="companyName" className="text-body font-medium">Company/Organization Name *</Label>
|
|
<Input
|
|
id="companyName"
|
|
value={bookingForm.companyName}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, companyName: e.target.value })}
|
|
placeholder="Enter company name"
|
|
required
|
|
className="text-body"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="contactName" className="text-body font-medium">Contact Person *</Label>
|
|
<Input
|
|
id="contactName"
|
|
value={bookingForm.contactName}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, contactName: e.target.value })}
|
|
placeholder="Enter your full name"
|
|
required
|
|
className="text-body"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="email" className="text-body font-medium">Email Address *</Label>
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
value={bookingForm.email}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, email: e.target.value })}
|
|
placeholder="your.email@company.com"
|
|
required
|
|
className="text-body"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="phone" className="text-body font-medium">Phone Number</Label>
|
|
<Input
|
|
id="phone"
|
|
value={bookingForm.phone}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, phone: e.target.value })}
|
|
placeholder="+1 (555) 123-4567"
|
|
className="text-body"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="role" className="text-body font-medium">Your Role/Position</Label>
|
|
<Input
|
|
id="role"
|
|
value={bookingForm.role}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, role: e.target.value })}
|
|
placeholder="e.g., Training Manager, HR Director"
|
|
className="text-body"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="teamSize" className="text-body font-medium">Expected Group Size *</Label>
|
|
<Select value={bookingForm.teamSize} onValueChange={(value: any) => setBookingForm({ ...bookingForm, teamSize: value })}>
|
|
<SelectTrigger className="text-body">
|
|
<SelectValue placeholder="Select group size" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="1-10">1-10 participants</SelectItem>
|
|
<SelectItem value="11-25">11-25 participants</SelectItem>
|
|
<SelectItem value="26-50">26-50 participants</SelectItem>
|
|
<SelectItem value="51-80">51-80 participants</SelectItem>
|
|
<SelectItem value="80+">80+ participants</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="facilityZone" className="text-body font-medium">Preferred Learning Zone</Label>
|
|
<Select value={bookingForm.facilityZone} onValueChange={(value: any) => setBookingForm({ ...bookingForm, facilityZone: value })}>
|
|
<SelectTrigger className="text-body">
|
|
<SelectValue placeholder="Select preferred zone (optional)" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="amphitheater">Training Amphitheater (80-120 people)</SelectItem>
|
|
<SelectItem value="collaboration">Collaboration Suites (4-8 people)</SelectItem>
|
|
<SelectItem value="outdoor">Outdoor Learning Pavilion (20-40 people)</SelectItem>
|
|
<SelectItem value="multiple">Multiple zones needed</SelectItem>
|
|
<SelectItem value="flexible">Flexible - advise best option</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="requirements" className="text-body font-medium">Additional Requirements</Label>
|
|
<Textarea
|
|
id="requirements"
|
|
value={bookingForm.additionalRequirements}
|
|
onChange={(e) => setBookingForm({ ...bookingForm, additionalRequirements: e.target.value })}
|
|
placeholder="Please share any specific requirements, preferred dates, catering needs, accommodation requirements, etc."
|
|
rows={4}
|
|
className="text-body resize-none"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex flex-col sm:flex-row gap-3 justify-end pt-4">
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={() => setIsBookingModalOpen(false)}
|
|
className="text-body"
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
className="text-body px-8"
|
|
style={{
|
|
backgroundColor: 'var(--color-primary)',
|
|
color: 'white',
|
|
fontFamily: 'var(--font-family-base)'
|
|
}}
|
|
>
|
|
<Calendar className="w-4 h-4 mr-2" />
|
|
Submit Booking Request
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
{/* Testimonials Section - Using home page testimonials with custom headers */}
|
|
<TestimonialsSection
|
|
title="Guest Experiences"
|
|
subtitle="Hear from clients and partners who have experienced the transformational power of our purpose-built learning environment."
|
|
tagText="Facility Excellence"
|
|
/>
|
|
|
|
{/* CTA Section - Using standardized home page CTA */}
|
|
<CTABannerSection />
|
|
</div>
|
|
);
|
|
} |