Files
CityCards-Website/src/components/MelbournePage.tsx

808 lines
38 KiB
TypeScript

import { motion, useAnimationControls, AnimatePresence } from 'motion/react';
import { Button } from './ui/button';
import { ArrowRight, Calendar, Thermometer, Eye, MapPin, Clock, Users, Ticket, Wand2, Plane, Sparkles } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import Navbar from './Navbar';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { MelbourneAttractions } from './MelbourneAttractions';
import { MelbourneCardComparison } from './MelbourneCardComparison';
import { MelbourneTourOverview } from './MelbourneTourOverview';
import { MelbourneBlogs } from './MelbourneBlogs';
import { CustomPostcards } from './CustomPostcards';
import { EnhancedTestimonials } from './EnhancedTestimonials';
import { MobileAppPromotion } from './MobileAppPromotion';
import { MelbourneFAQ } from './MelbourneFAQ';
import { Footer } from './Footer';
// import { MinimalHeroBanner } from './MinimalHeroBanner';
import { Layout } from '../Layout';
import { HeroBannerCarousel } from './HeroBannerCarousel';
import { HotelEsimOffers } from './HotelEsimOffers';
interface User {
email: string;
name: string;
}
interface MelbournePageProps {
onBackClick: () => void;
onHomeClick: () => void;
onAttractionsClick: () => void;
onPassesClick: () => void;
onCheckoutClick?: () => void;
onSignInClick: () => void;
onSignOutClick?: () => void;
onBlogsClick: () => void;
onHowItWorksClick: () => void;
onFAQClick: () => void;
onPrivacyPolicyClick: () => void;
onAboutUsClick: () => void;
onProfileClick?: () => void;
onCityCardsClick?: () => void;
onMagicItineraryClick?: () => void;
onSuperSavingsClick?: () => void;
onPostCardsClick?: () => void;
onOffersClick?: () => void;
onEsimsClick?: () => void;
onHotelDiscountsClick?: () => void;
onContactUsClick?: () => void;
onCreateItineraryClick?: () => void;
currentPage?: string;
user?: User | null;
}
interface ItineraryCard {
id: number;
city: string;
country: string;
days: number;
image: string;
highlights: string[];
activities: { name: string; time: string }[];
}
const itineraryCards: ItineraryCard[] = [
{
id: 1,
city: 'Melbourne',
country: 'Australia',
days: 3,
image: 'https://images.unsplash.com/photo-1720044282356-e319c686d209?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjBjaXR5JTIwbGFuZG1hcmt8ZW58MXx8fHwxNzY5MTUwMzA5fDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral',
highlights: ['Coffee Culture', 'Street Art', 'Rooftop Bars'],
activities: [
{ name: 'Breakfast at Higher Ground', time: '09:00 AM' },
{ name: 'Explore Hosier Lane', time: '11:00 AM' },
{ name: 'Royal Botanic Gardens', time: '02:00 PM' },
{ name: 'Sunset at Eureka Skydeck', time: '06:00 PM' }
]
},
{
id: 2,
city: 'Sydney',
country: 'Australia',
days: 4,
image: 'https://images.unsplash.com/photo-1523059623039-a9ed027e7fad?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxTeWRuZXklMjBvcGVyYSUyMGhvdXNlfGVufDF8fHx8MTc2OTE1MDMwOXww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral',
highlights: ['Opera House', 'Bondi Beach', 'Harbour Bridge'],
activities: [
{ name: 'Opera House Tour', time: '10:00 AM' },
{ name: 'Walk the Harbour Bridge', time: '01:00 PM' },
{ name: 'Ferry to Manly', time: '03:30 PM' },
{ name: 'Dinner at The Rocks', time: '07:00 PM' }
]
},
{
id: 3,
city: 'Gold Coast',
country: 'Australia',
days: 5,
image: 'https://images.unsplash.com/photo-1611473247871-8a4ef64d17ee?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxHb2xkJTIwQ29hc3QlMjBiZWFjaCUyMGF1c3RyYWxpYXxlbnwxfHx8fDE3NjkxNTAzMDl8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral',
highlights: ['Surfers Paradise', 'Theme Parks', 'Rainforest'],
activities: [
{ name: 'Surf Lesson', time: '08:00 AM' },
{ name: 'Q1 Observation Deck', time: '12:00 PM' },
{ name: 'Currumbin Sanctuary', time: '02:00 PM' },
{ name: 'Sunset Cruise', time: '05:30 PM' }
]
},
{
id: 4,
city: 'Cairns',
country: 'Australia',
days: 4,
image: 'https://images.unsplash.com/photo-1710171781154-678ede6f39a3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxDYWlybnMlMjBncmVhdCUyMGJhcnJpZXIlMjByZWVmfGVufDF8fHx8MTc2OTE1MDMwOXww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral',
highlights: ['Great Barrier Reef', 'Daintree', 'Esplanade'],
activities: [
{ name: 'Reef Snorkeling', time: '09:00 AM' },
{ name: 'Lunch on the Boat', time: '01:00 PM' },
{ name: 'Esplanade Lagoon', time: '04:00 PM' },
{ name: 'Night Market', time: '07:00 PM' }
]
}
];
export function MelbournePage({
onBackClick,
onHomeClick,
onAttractionsClick,
onPassesClick,
onCheckoutClick,
onSignInClick,
onSignOutClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onProfileClick,
onCityCardsClick,
onMagicItineraryClick,
onSuperSavingsClick,
onPostCardsClick,
onOffersClick,
onEsimsClick,
onHotelDiscountsClick,
onContactUsClick,
onCreateItineraryClick,
currentPage,
user
}: MelbournePageProps) {
// Magic Itinerary state
const [currentCardIndex, setCurrentCardIndex] = useState(0);
const [isAnimating, setIsAnimating] = useState(false);
const currentCard = itineraryCards[currentCardIndex];
const nextCard = itineraryCards[(currentCardIndex + 1) % itineraryCards.length];
const thirdCard = itineraryCards[(currentCardIndex + 2) % itineraryCards.length];
return (
<div className="min-h-screen bg-white">
{/* Navigation */}
<Layout
activeCity="Melbourne"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
>
{/* Hero Banner Carousel */}
<HeroBannerCarousel
onCheckoutClick={onCheckoutClick}
onPassesClick={onPassesClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/>
{/* Main Content */}
<main className="bg-gray-50/30 min-h-screen relative">
{/* Sticky Page Navigation */}
<div className="sticky top-[78px] lg:top-[94px] z-40 bg-white/80 backdrop-blur-xl border-b border-gray-100 shadow-sm">
<div className="container mx-auto px-4">
{/* horizontal scroll wrapper */}
<div className="overflow-x-auto no-scrollbar">
{/* actual flex row */}
<div className="flex items-center justify-center gap-2 py-3 min-w-max">
{[
{ id: 'overview', label: 'Overview', icon: MapPin },
{ id: 'attractions', label: 'Attractions', icon: Eye },
{ id: 'passes', label: 'City Pass', icon: Ticket },
{ id: 'tours', label: 'Tours', icon: Plane },
{ id: 'itinerary', label: 'Itinerary', icon: Wand2 },
{ id: 'faq', label: 'FAQ', icon: Sparkles }
].map((item) => (
<button
key={item.id}
onClick={() =>
document
.getElementById(item.id)
?.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
className="flex items-center gap-2 px-5 py-2.5 rounded-full text-sm font-poppins font-medium
text-gray-600 hover:text-primary hover:bg-primary/5 transition-all whitespace-nowrap
group border border-transparent hover:border-primary/10"
>
<item.icon className="w-4 h-4 text-gray-400 group-hover:text-primary transition-colors" />
{item.label}
</button>
))}
</div>
</div>
</div>
</div>
<div className="container mx-auto px-4 py-12 space-y-24">
{/* Features Grid */}
<motion.section
id="overview"
className="grid md:grid-cols-3 gap-8 scroll-mt-32"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
{[
{
title: "50+ Top Attractions",
description: "Unlimited access to Melbourne's finest museums, zoos, and observation decks.",
icon: MapPin,
color: "text-blue-500",
bg: "bg-blue-50"
},
{
title: "Instant Digital Entry",
description: "Skip the ticket lines with a simple scan of your mobile pass.",
icon: Ticket,
color: "text-primary",
bg: "bg-primary/10"
},
{
title: "Save over 50%",
description: "Enjoy massive savings compared to buying individual attraction tickets.",
icon: Sparkles,
color: "text-emerald-500",
bg: "bg-emerald-50"
}
].map((feature, index) => (
<motion.div
key={feature.title}
className="flex flex-col items-center text-center p-8 bg-white rounded-[2rem] shadow-[0_2px_20px_-4px_rgba(0,0,0,0.05)] border border-gray-100 hover:shadow-xl hover:border-primary/20 transition-all duration-300 group cursor-default"
whileHover={{ y: -5 }}
>
<div className={`w-20 h-20 rounded-2xl ${feature.bg} flex items-center justify-center mb-6 group-hover:scale-110 transition-transform duration-500 rotate-3 group-hover:rotate-6`}>
<feature.icon className={`w-8 h-8 ${feature.color}`} />
</div>
<h3 className="font-merchant text-3xl text-gray-900 mb-3">{feature.title}</h3>
<p className="font-poppins text-gray-500 font-light leading-relaxed">{feature.description}</p>
</motion.div>
))}
</motion.section>
{/* Attractions Section */}
<div id="attractions" className="scroll-mt-32">
<MelbourneAttractions />
</div>
{/* Pass Comparison */}
<div id="passes" className="scroll-mt-32">
<MelbourneCardComparison />
</div>
{/* Tour Overview */}
<div id="tours" className="scroll-mt-32">
<MelbourneTourOverview />
</div>
{/* Hotel & eSIM Offers */}
<div id="offers" className="scroll-mt-32">
<HotelEsimOffers
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/>
</div>
{/* Blogs */}
<div id="blogs" className="scroll-mt-32">
<MelbourneBlogs />
</div>
{/* Custom Postcards */}
<CustomPostcards />
</div>
{/* Magic Itinerary Section */}
<div id="itinerary" className="scroll-mt-10">
<section className="relative py-20 lg:py-32 overflow-hidden -mt-20 pt-32 z-0">
{/* Dynamic City Background */}
<AnimatePresence mode="wait">
<motion.div
key={currentCard.id}
className="absolute inset-0 overflow-hidden pointer-events-none z-[5]"
initial={{ opacity: 0, scale: 1.05 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 1.05 }}
transition={{ duration: 0.8, ease: "easeInOut" }}
>
{/* City Background Image */}
<div className="absolute inset-0">
<ImageWithFallback
src={currentCard.image}
alt={`${currentCard.city} background`}
className="w-full h-full object-cover"
/>
</div>
{/* Lightened Multi-layer Gradient Overlay - Reduced opacity to show city */}
<div className="absolute inset-0 bg-gradient-to-br from-rose-50/50 via-orange-50/40 to-amber-50/50" />
<div className="absolute inset-0 backdrop-blur-lg" />
<div className="absolute inset-0 bg-gradient-to-t from-white/30 via-transparent to-white/30" />
</motion.div>
</AnimatePresence>
{/* White Readability Overlay - 42% Opacity */}
<div className="absolute inset-0 bg-white/42 pointer-events-none z-[10]" />
{/* Simplified Decorative Elements - Optimized */}
<div className="absolute inset-0 overflow-hidden pointer-events-none z-[15]">
{/* Single Coral Gradient Blob - Optimized */}
<motion.div
className="absolute top-20 -left-20 w-[500px] h-[500px] bg-gradient-to-br from-primary/20 via-orange-400/10 to-transparent rounded-full blur-3xl will-change-transform"
animate={{
scale: [1, 1.15, 1],
x: [0, 30, 0],
}}
transition={{ duration: 15, repeat: Infinity, ease: "easeInOut" }}
/>
{/* Simplified Floating Icons - Reduced from 8 to 4 */}
{[...Array(4)].map((_, i) => (
<motion.div
key={i}
className="absolute will-change-transform"
style={{
top: `${25 + (i * 20)}%`,
left: `${15 + (i * 20)}%`,
}}
animate={{
y: [0, -20, 0],
opacity: [0.15, 0.35, 0.15],
}}
transition={{
duration: 6 + i * 2,
repeat: Infinity,
ease: "easeInOut",
delay: i * 0.8,
}}
>
{i % 2 === 0 ? (
<Plane className="w-6 h-6 text-primary" />
) : (
<MapPin className="w-6 h-6 text-primary" />
)}
</motion.div>
))}
</div>
<div className="container mx-auto px-4 relative z-20 flex flex-col items-center">
{/* Header */}
<div className="text-center mb-20 max-w-5xl w-full">
<motion.div
className="inline-flex items-center gap-3 bg-gradient-to-r from-primary/10 to-orange-100/50 backdrop-blur-sm px-6 py-3 rounded-full border-2 border-primary/30 shadow-xl mb-8"
initial={{ opacity: 0, scale: 0.8, y: 20 }}
whileInView={{ opacity: 1, scale: 1, y: 0 }}
transition={{ duration: 0.7, ease: [0.34, 1.56, 0.64, 1] }}
viewport={{ once: true }}
>
<motion.div
animate={{
rotate: [0, 20, -20, 0],
scale: [1, 1.2, 1.2, 1],
}}
transition={{ duration: 2, repeat: Infinity }}
>
<Wand2 className="w-6 h-6 text-primary drop-shadow-lg" />
</motion.div>
<span className="font-poppins font-semibold text-gray-800">AI-Powered Magic Itinerary</span>
<motion.div
className="w-2 h-2 bg-primary rounded-full"
animate={{
scale: [1, 1.5, 1],
opacity: [1, 0.5, 1],
}}
transition={{ duration: 1.5, repeat: Infinity }}
/>
</motion.div>
<motion.h2
className="font-poppins text-4xl md:text-5xl lg:text-6xl mb-8 leading-tight"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.1, ease: [0.34, 1.56, 0.64, 1] }}
viewport={{ once: true }}
>
<span className="font-light">Plan Your</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary via-orange-500 to-rose-500 bg-clip-text text-transparent drop-shadow-lg pr-2">
Dream Journey
</span>
<br />
<span className="font-normal">in Just</span>{' '}
<span className="font-bold text-primary">3 Seconds</span>
<motion.span
className="inline-block ml-2"
animate={{
rotate: [0, 10, -10, 0],
y: [0, -5, 0],
}}
transition={{ duration: 2, repeat: Infinity, delay: 0.5 }}
>
</motion.span>
</motion.h2>
<motion.p
className="font-poppins text-xl md:text-2xl text-gray-700 leading-relaxed max-w-3xl mx-auto"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
>
Our AI creates <span className="font-semibold text-primary">personalized itineraries</span> with
perfectly timed activities, optimized routes, and <span className="font-semibold text-primary">curated experiences</span> tailored
just for you.
</motion.p>
</div>
{/* Card Stack Display */}
<div className="max-w-6xl w-full">
<div className="relative flex justify-center items-center min-h-[600px] lg:min-h-[650px] perspective-1000">
{/* Card Stack Container */}
<div className="relative w-full max-w-md lg:max-w-lg" style={{ transformStyle: 'preserve-3d' }}>
{/* Visible Card Stack - Third Card */}
<motion.div
className="absolute inset-0 bg-white rounded-3xl shadow-lg overflow-hidden"
style={{
zIndex: 1,
transformStyle: 'preserve-3d'
}}
animate={{
scale: 0.88,
y: 24,
opacity: 0.4,
}}
transition={{ duration: 0.3 }}
>
<div className="relative h-64 lg:h-80 overflow-hidden">
<ImageWithFallback
src={thirdCard.image}
alt={`${thirdCard.city}, ${thirdCard.country}`}
className="w-full h-full object-cover opacity-60"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent" />
<div className="absolute bottom-6 left-6 right-6">
<h3 className="font-poppins text-2xl lg:text-3xl font-bold text-white opacity-70">
{thirdCard.city}
</h3>
</div>
</div>
</motion.div>
{/* Visible Card Stack - Second Card */}
<motion.div
className="absolute inset-0 bg-white rounded-3xl shadow-xl overflow-hidden"
style={{
zIndex: 2,
transformStyle: 'preserve-3d'
}}
animate={{
scale: 0.94,
y: 12,
opacity: 0.7,
}}
transition={{ duration: 0.3 }}
>
<div className="relative h-64 lg:h-80 overflow-hidden">
<ImageWithFallback
src={nextCard.image}
alt={`${nextCard.city}, ${nextCard.country}`}
className="w-full h-full object-cover opacity-80"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent" />
<div className="absolute bottom-6 left-6 right-6">
<h3 className="font-poppins text-2xl lg:text-3xl font-bold text-white opacity-80">
{nextCard.city}
</h3>
</div>
</div>
</motion.div>
{/* Animated Front Card */}
<AnimatePresence mode="popLayout">
<motion.div
key={currentCard.id}
className="relative bg-white rounded-3xl shadow-2xl overflow-hidden"
style={{
zIndex: 3,
transformStyle: 'preserve-3d'
}}
initial={{
scale: 0.94,
y: 12,
opacity: 0.7,
}}
animate={{
scale: 1,
opacity: 1,
y: 0,
}}
exit={{
scale: 1.05,
opacity: 0,
x: -100,
rotateZ: -5,
}}
transition={{
duration: 0.6,
ease: [0.34, 1.56, 0.64, 1],
}}
>
{/* Card Image */}
<div className="relative h-64 lg:h-80 overflow-hidden">
<ImageWithFallback
src={currentCard.image}
alt={`${currentCard.city}, ${currentCard.country}`}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent" />
{/* City Info Overlay */}
<motion.div
className="absolute bottom-6 left-6 right-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2, duration: 0.5, ease: [0.34, 1.56, 0.64, 1] }}
>
<h3 className="font-poppins text-3xl lg:text-4xl font-bold text-white mb-2">
{currentCard.city}
</h3>
<p className="font-poppins text-white/90 text-lg flex items-center gap-2">
<MapPin className="w-5 h-5" />
{currentCard.country}
</p>
</motion.div>
{/* Duration Badge */}
<motion.div
className="absolute top-6 right-6 bg-gradient-to-r from-primary to-orange-500 px-4 py-2 rounded-full shadow-xl"
initial={{ scale: 0, opacity: 0, rotate: -180 }}
animate={{ scale: 1, opacity: 1, rotate: 0 }}
transition={{ delay: 0.3, duration: 0.6, type: "spring", stiffness: 200, damping: 12 }}
>
<div className="flex items-center gap-2">
<Clock className="w-4 h-4 text-white" />
<span className="font-poppins font-bold text-white">{currentCard.days} Days</span>
</div>
</motion.div>
{/* Top Left Journey Icon */}
<motion.div
className="absolute top-6 left-6 bg-white/95 backdrop-blur-sm p-3 rounded-full shadow-lg"
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ delay: 0.2, duration: 0.5, type: "spring" }}
>
<Plane className="w-5 h-5 text-primary" />
</motion.div>
</div>
{/* Card Content */}
<motion.div
className="p-4 lg:p-5"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3, duration: 0.5 }}
>
{/* Highlights - Compact */}
<motion.div
className="flex flex-wrap gap-1.5 mb-4"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3, duration: 0.4 }}
>
{currentCard.highlights.map((highlight, idx) => (
<span
key={idx}
className="font-poppins px-3 py-1 bg-gradient-to-r from-primary/15 to-orange-100 text-primary border border-primary/20 rounded-full text-xs font-semibold shadow-sm transition-transform hover:scale-105"
>
{highlight}
</span>
))}
</motion.div>
{/* Day 1 Header */}
<motion.div
className="flex items-center gap-2 mb-2.5"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.35, duration: 0.4 }}
>
<div className="flex items-center gap-1.5 px-3 py-1 bg-gradient-to-r from-primary to-orange-500 rounded-full shadow-md">
<Clock className="w-3 h-3 text-white" />
<span className="font-poppins text-xs font-bold text-white">Day 1</span>
</div>
<div className="flex-1 h-px bg-gradient-to-r from-primary/30 to-transparent" />
</motion.div>
{/* Day 1 Activities - Compact */}
<motion.div
className="space-y-2 mb-3"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4, duration: 0.4 }}
>
{currentCard.activities.slice(0, 2).map((activity, idx) => (
<div
key={idx}
className="relative flex items-center gap-2.5 p-2.5 bg-gradient-to-r from-white to-orange-50/30 rounded-xl border border-primary/10 hover:border-primary/30 hover:shadow-md transition-all group hover:translate-x-0.5"
>
{idx < 1 && (
<div className="absolute left-4 top-full h-2 w-0.5 bg-gradient-to-b from-primary/50 to-transparent" />
)}
<div className="relative flex-shrink-0">
<div className="w-7 h-7 bg-gradient-to-br from-primary to-orange-500 rounded-full flex items-center justify-center shadow-md">
<span className="font-poppins text-white font-bold text-xs">{idx + 1}</span>
</div>
<div className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-green-400 rounded-full border border-white" />
</div>
<div className="flex-1 min-w-0">
<p className="font-poppins text-xs font-semibold text-gray-900 group-hover:text-primary transition-colors leading-tight mb-0.5">{activity.name}</p>
<div className="flex items-center gap-1.5 text-xs text-gray-500">
<Clock className="w-2.5 h-2.5" />
<span className="font-poppins">{activity.time}</span>
</div>
</div>
</div>
))}
</motion.div>
{/* Day 2 Header */}
<motion.div
className="flex items-center gap-2 mb-2.5"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.45, duration: 0.4 }}
>
<div className="flex items-center gap-1.5 px-3 py-1 bg-gradient-to-r from-orange-500 to-rose-500 rounded-full shadow-md">
<Clock className="w-3 h-3 text-white" />
<span className="font-poppins text-xs font-bold text-white">Day 2</span>
</div>
<div className="flex-1 h-px bg-gradient-to-r from-orange-500/30 to-transparent" />
</motion.div>
{/* Day 2 Activities - Compact */}
<motion.div
className="space-y-2"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5, duration: 0.4 }}
>
{currentCard.activities.slice(2, 4).map((activity, idx) => (
<div
key={idx + 2}
className="relative flex items-center gap-2.5 p-2.5 bg-gradient-to-r from-white to-rose-50/30 rounded-xl border border-orange-500/10 hover:border-orange-500/30 hover:shadow-md transition-all group hover:translate-x-0.5"
>
{idx < 1 && (
<div className="absolute left-4 top-full h-2 w-0.5 bg-gradient-to-b from-orange-500/50 to-transparent" />
)}
<div className="relative flex-shrink-0">
<div className="w-7 h-7 bg-gradient-to-br from-orange-500 to-rose-500 rounded-full flex items-center justify-center shadow-md">
<span className="font-poppins text-white font-bold text-xs">{idx + 1}</span>
</div>
<div className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-green-400 rounded-full border border-white" />
</div>
<div className="flex-1 min-w-0">
<p className="font-poppins text-xs font-semibold text-gray-900 group-hover:text-orange-500 transition-colors leading-tight mb-0.5">{activity.name}</p>
<div className="flex items-center gap-1.5 text-xs text-gray-500">
<Clock className="w-2.5 h-2.5" />
<span className="font-poppins">{activity.time}</span>
</div>
</div>
</div>
))}
</motion.div>
</motion.div>
</motion.div>
</AnimatePresence>
{/* Optimized Floating Sparkles */}
{[...Array(3)].map((_, i) => (
<motion.div
key={i}
className="absolute pointer-events-none will-change-transform"
style={{
top: `${20 + i * 30}%`,
left: `${10 + i * 40}%`,
}}
animate={{
y: [0, -20, 0],
opacity: [0, 0.5, 0],
scale: [0, 1, 0],
}}
transition={{
duration: 3,
repeat: Infinity,
delay: i * 0.8,
ease: "easeInOut",
}}
>
<Sparkles className="w-5 h-5 text-primary" />
</motion.div>
))}
</div>
</div>
{/* Card Indicators with City Names */}
<motion.div
className="flex flex-wrap justify-center gap-3 mt-12"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 1 }}
viewport={{ once: true }}
>
{itineraryCards.map((card, idx) => (
<motion.button
key={card.id}
onClick={() => {
setIsAnimating(true);
setTimeout(() => {
setCurrentCardIndex(idx);
setIsAnimating(false);
}, 400);
}}
className={`font-poppins group relative transition-all duration-300 px-4 py-2 rounded-full font-medium ${idx === currentCardIndex
? 'bg-gradient-to-r from-primary to-orange-500 text-white shadow-lg scale-110'
: 'bg-white/80 backdrop-blur-sm text-gray-600 hover:text-primary hover:bg-white border border-gray-200 hover:border-primary/30 hover:scale-105'
}`}
whileHover={{ y: -2 }}
whileTap={{ scale: 0.95 }}
>
{card.city}
{idx === currentCardIndex && (
<motion.div
className="absolute -bottom-1 left-1/2 w-1.5 h-1.5 bg-white rounded-full"
layoutId="activeIndicator"
initial={false}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
style={{ x: '-50%' }}
/>
)}
</motion.button>
))}
</motion.div>
{/* CTA Button */}
<motion.div
className="flex flex-col items-center gap-6 mt-16"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.3 }}
viewport={{ once: true }}
>
<Button
onClick={onCreateItineraryClick}
className="font-poppins py-7 px-16 rounded-full text-xl font-bold bg-gradient-to-r from-primary via-orange-500 to-rose-500 hover:from-primary/90 hover:via-orange-500/90 hover:to-rose-500/90 shadow-2xl hover:shadow-primary/50 transition-all hover:scale-105 hover:-translate-y-1"
>
<span className="flex items-center gap-3">
<Wand2 className="w-6 h-6" />
Create My Perfect Itinerary
</span>
</Button>
<p className="font-poppins text-gray-600 text-sm flex items-center gap-2">
<Sparkles className="w-4 h-4 text-primary" />
<span>Free to use No credit card required</span>
<Sparkles className="w-4 h-4 text-primary" />
</p>
</motion.div>
</div>
</div>
</section>
</div>
<div className="container mx-auto px-4 py-12 space-y-24">
{/* Testimonials */}
<EnhancedTestimonials />
{/* Mobile App Promotion */}
<MobileAppPromotion />
{/* FAQ */}
<div id="faq" className="scroll-mt-32">
<MelbourneFAQ />
</div>
</div>
</main>
</Layout>
</div>
);
}