Files
CityCards-Website/src/pages/CheckoutPage.tsx

515 lines
30 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import {
ArrowLeft, Check, Minus, Plus, ChevronDown
} from 'lucide-react';
import Navbar from '../components/Navbar';
import { Footer } from '../components/Footer';
import { ImageWithFallback } from '../components/figma/ImageWithFallback';
import { useNavigate } from 'react-router-dom';
import { useAddCardToCartMutation, useGetCheckoutPageDataQuery } from '../Redux/services/cards.service';
import LoadingSpinner from '../components/LoadingSpinner';
import { toast } from 'sonner';
/* ─── Types ─── */
export interface CartItem {
id: string;
city: string;
cardType: 'Flexi' | 'Unlimited';
days: number;
adults: number;
children: number;
quantity: number;
pricePerUnit: number;
image: string;
}
interface Attraction {
id: string;
name: string;
image: string;
category: string;
included: boolean;
}
/* ─── FIGMA CARD PREVIEWS (Exact Copy) ─── */
function FlexiCardPreview({ city, adultPrice, childPrice, isSelected, image }: { city: string; adultPrice: number; childPrice: number; isSelected: boolean, image: string; }) {
return (
<div className={`relative h-[160px] w-full rounded-lg transition-all duration-200 ${isSelected ? 'ring-2 ring-[#F95F62] shadow-md shadow-[#F95F62]/10' : 'hover:shadow-md'
}`}>
{/* Card bg */}
<div className="absolute inset-0 bg-white border border-[rgba(249,95,175,0.2)] rounded-lg shadow-[0px_4px_20px_0px_rgba(0,0,0,0.06)]" />
{/* City image */}
<div className="absolute h-[158px] left-[1px] top-[1px] w-[103px] rounded-bl-[7px] rounded-tl-[7px] overflow-hidden">
<img alt="" className="absolute inset-0 w-full h-full object-cover" src={image} />
</div>
{/* City name - left aligned */}
<div className="absolute left-[112px] top-[12px]">
<p className="font-['Poppins',sans-serif] font-medium text-[16px] text-[#2a2a2a] leading-[22px] whitespace-nowrap">{city}</p>
</div>
{/* Pricing */}
<div className="absolute left-[112px] top-[40px] flex flex-col gap-[6px]">
<div className="flex gap-[2px] items-center">
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.6)] tracking-[0.06px]">From</span>
<span className="font-['Poppins',sans-serif] font-medium text-[24px] text-[#f95f62] tracking-[-0.96px] leading-[1.3]">${adultPrice}</span>
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px]">/Adult</span>
</div>
<div className="flex gap-[2px] items-center">
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.6)] tracking-[0.06px]">and</span>
<span className="font-['Poppins',sans-serif] font-medium text-[24px] text-[#f95f62] tracking-[-0.96px] leading-[1.3]">${childPrice}</span>
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px]">/Child</span>
</div>
</div>
{/* Description */}
<div className="absolute left-[112px] top-[112px] right-[44px]">
<p className="font-['Poppins',sans-serif] text-[11px] text-left text-[rgba(0,0,0,0.4)] tracking-[0.06px] leading-[14px]">
Dive into an extensive selection of thrilling destinations!
</p>
</div>
{/* Side tab - Flexi (pink) */}
<div className="absolute bg-[#f95faf] h-full right-0 top-0 w-[35px] rounded-br-lg rounded-tr-lg flex flex-col items-center justify-center gap-[2px]">
<span className="font-['Poppins',sans-serif] text-[12px] text-white/70 [writing-mode:vertical-rl] rotate-180">Card</span>
<span className="font-['Poppins',sans-serif] text-[16px] text-white [writing-mode:vertical-rl] rotate-180">Flexi</span>
</div>
{/* Selected checkmark */}
{isSelected && (
<div className="absolute top-2 right-[44px] w-6 h-6 rounded-full bg-[#F95F62] flex items-center justify-center z-10">
<Check className="w-3.5 h-3.5 text-white" strokeWidth={3} />
</div>
)}
</div>
);
}
function UnlimitedCardPreview({ city, adultPrice, childPrice, isSelected, image }: { city: string; adultPrice: number; childPrice: number; isSelected: boolean, image: string; }) {
return (
<div className={`relative h-[160px] w-full rounded-lg transition-all duration-200 ${isSelected ? 'ring-2 ring-[#F95F62] shadow-md shadow-[#F95F62]/10' : 'hover:shadow-md'
}`}>
{/* Card bg */}
<div className="absolute inset-0 bg-white border border-[rgba(0,0,0,0.2)] rounded-lg" />
{/* City image */}
<div className="absolute h-[158px] left-[1px] top-[1px] w-[103px] rounded-bl-[7px] rounded-tl-[7px] overflow-hidden">
<img alt="" className="absolute inset-0 w-full h-full object-cover" src={image} />
</div>
{/* City name - left aligned */}
<div className="absolute left-[112px] top-[12px]">
<p className="font-['Poppins',sans-serif] font-medium text-[16px] text-[#2a2a2a] leading-[20px] whitespace-nowrap">{city}</p>
</div>
{/* Pricing */}
<div className="absolute left-[112px] top-[40px] flex flex-col gap-[6px]">
<div className="flex gap-[2px] items-center">
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.6)] tracking-[0.06px]">From</span>
<span className="font-['Poppins',sans-serif] font-medium text-[24px] text-[#f95f62] tracking-[-0.96px] leading-[1.3]">${adultPrice}</span>
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px]">/Adult</span>
</div>
<div className="flex gap-[2px] items-center">
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.6)] tracking-[0.06px]">and</span>
<span className="font-['Poppins',sans-serif] font-medium text-[24px] text-[#f95f62] tracking-[-0.96px] leading-[1.3]">${childPrice}</span>
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px]">/Child</span>
</div>
</div>
{/* Description */}
<div className="absolute left-[112px] top-[112px] right-[44px]">
<p className="font-['Poppins',sans-serif] text-[11px] text-left text-[rgba(0,0,0,0.4)] tracking-[0.06px] leading-[14px]">
Dive into an extensive selection of thrilling destinations!
</p>
</div>
{/* Side tab - Unlimited (coral) */}
<div className="absolute bg-[#f95f62] h-full right-0 top-0 w-[35px] rounded-br-lg rounded-tr-lg flex flex-col items-center justify-center gap-[2px]">
<span className="font-['Poppins',sans-serif] text-[12px] text-white/70 [writing-mode:vertical-rl] rotate-180">Card</span>
<span className="font-['Poppins',sans-serif] text-[16px] text-white [writing-mode:vertical-rl] rotate-180">Unlimited</span>
</div>
{/* Selected checkmark */}
{isSelected && (
<div className="absolute top-2 right-[44px] w-6 h-6 rounded-full bg-[#F95F62] flex items-center justify-center z-10">
<Check className="w-3.5 h-3.5 text-white" strokeWidth={3} />
</div>
)}
</div>
);
}
/* ─── CheckoutConfigCard (Exact Copy) ─── */
function CheckoutConfigCard({
item,
onProceed,
}: {
item: any;
onProceed: () => void;
}) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const [noOfAdults, setNoOfAdults] = useState(1)
const [noOfChildren, setNoOfChildren] = useState(0)
const [noOfAttractions, setNoOfAttractions] = useState(item?.minNumber);
const [noOfDays, setNoOfDays] = useState(item?.minNumber)
const cityId = localStorage.getItem("cityId")
const cityName = localStorage.getItem("cityName")
const cardTypeId = item?.cardType?.id
const cardId = item?.id
const cardMode = item?.cardType?.name === "selective_pass" ? "flexi" : "unlimited"
const adultPrice = item?.adultPrice * noOfAdults
const childPrice = item?.childPrice * noOfChildren
const basePrice = adultPrice + childPrice
const taxAmount = basePrice * 0.1
const strikedPrice = basePrice + 20
const [addCardToCart] = useAddCardToCartMutation()
useEffect(() => {
setNoOfAttractions(item?.minNumber)
setNoOfDays(item?.minNumber)
}, [item])
const numberArray = Array.from(
{ length: item?.maxNumber - item?.minNumber + 1 },
(_, i) => item?.minNumber + i
);
const navigate = useNavigate();
const cardBookingDetails = {
cityXid: cityId,
cardTypeXid: cardTypeId,
cardXid: cardId,
cardMode, // stays as-is
totalAdult: noOfAdults,
baseAmount: basePrice, // static value
taxAmount,
totalChild: noOfChildren,
noOfAttractions,
noOfDays
};
const handleProceedToPayment = async () => {
try {
const response = await addCardToCart(cardBookingDetails);
const bookingId = response?.data?.id
if (bookingId) {
navigate(`/payment/${bookingId}`)
} else {
throw new Error(response?.error?.data?.message)
}
} catch (error:any) {
toast.error(error.message);
}
}
return (
<div className="bg-white rounded-2xl shadow-[0px_4px_24px_0px_rgba(0,0,0,0.06)] overflow-hidden w-full max-w-[400px]">
<div className="pt-6 pb-2 text-center">
<h4 className="font-poppins text-lg leading-snug font-medium text-[#2a2a2a]">{cityName}</h4>
<div className="mt-2 flex justify-center">
<span className={`inline-flex items-center px-4 py-1 rounded-full font-poppins text-xs font-medium ${item?.cardType?.name === 'selective_pass' ? 'bg-[#f95faf]/10 text-[#f95faf]' : 'bg-[#f95f62]/10 text-[#f95f62]'}`}>
{item?.cardType?.displayName}
</span>
</div>
</div>
<div className="px-6 py-4 space-y-0">
<div className="flex items-center justify-between py-4 border-b border-gray-100">
<span className="font-poppins text-sm font-normal text-[#2a2a2a]">No. of Adults</span>
<div className="flex items-center gap-3">
<button onClick={() => noOfAdults > 1 && setNoOfAdults((prev) => prev - 1)} disabled={noOfAdults <= 1} className={`w-8 h-8 rounded-full flex items-center justify-center transition-colors ${noOfAdults <= 1 ? 'bg-gray-100 text-gray-300 cursor-not-allowed' : 'bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20'}`}>
<Minus className="w-4 h-4" />
</button>
<span className="font-poppins text-base font-medium text-[#2a2a2a] w-5 text-center tabular-nums">{noOfAdults}</span>
<button onClick={() => setNoOfAdults((prev) => prev + 1)} disabled={noOfAdults >= 15} className={`w-8 h-8 rounded-full flex items-center justify-center transition-colors ${noOfAdults >= 15 ? 'bg-gray-100 text-gray-300 cursor-not-allowed' : 'bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20'}`}>
<Plus className="w-4 h-4" />
</button>
</div>
</div>
<div className="flex items-center justify-between py-4 border-b border-gray-100">
<span className="font-poppins text-sm font-normal text-[#2a2a2a]">No. of Children</span>
<div className="flex items-center gap-3">
<button onClick={() => noOfChildren > 0 && setNoOfChildren((prev) => prev - 1)} disabled={noOfChildren <= 0} className={`w-8 h-8 rounded-full flex items-center justify-center transition-colors ${noOfChildren <= 0 ? 'bg-gray-100 text-gray-300 cursor-not-allowed' : 'bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20'}`}>
<Minus className="w-4 h-4" />
</button>
<span className="font-poppins text-base font-medium text-[#2a2a2a] w-5 text-center tabular-nums">{noOfChildren}</span>
<button onClick={() => setNoOfChildren((prev) => prev + 1)} disabled={noOfChildren >= 10} className={`w-8 h-8 rounded-full flex items-center justify-center transition-colors ${noOfChildren >= 10 ? 'bg-gray-100 text-gray-300 cursor-not-allowed' : 'bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20'}`}>
<Plus className="w-4 h-4" />
</button>
</div>
</div>
<div className="flex items-center justify-between py-4 border-b border-gray-100">
<span className="font-poppins text-sm font-normal text-[#2a2a2a]">
{item?.cardType?.name === 'selective_pass' ? 'No. of Attractions' : 'No. of Days'}
</span>
<div className="relative">
<button onClick={() => setDropdownOpen(!dropdownOpen)} className="flex items-center gap-2 border border-[#f95f62]/30 rounded-lg px-3 py-1.5 min-w-[72px] justify-between hover:border-[#f95f62] transition-colors">
<span className="font-poppins text-base font-medium text-[#f95f62] tabular-nums">{cardMode === "flexi" ? noOfAttractions : noOfDays}</span>
<ChevronDown className={`w-4 h-4 text-[#f95f62] transition-transform ${dropdownOpen ? 'rotate-180' : ''}`} />
</button>
<AnimatePresence>
{dropdownOpen && (
<motion.div
initial={{ opacity: 0, y: -4, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: -4, scale: 0.95 }}
className="absolute right-0 top-full mt-1 bg-white rounded-lg shadow-lg border border-gray-100 z-30 min-w-[72px]
max-h-48 overflow-y-auto"
>
{numberArray.map((i) => (
<button
key={i}
onClick={() => {
cardMode === "flexi" ? setNoOfAttractions(i) : setNoOfDays(i);
setDropdownOpen(false);
}}
className={`w-full px-3 py-2 text-left font-poppins text-sm transition-colors ${(cardMode === "flexi" ? noOfAttractions === i : noOfDays === i)
? "bg-[#f95f62]/10 text-[#f95f62] font-medium"
: "text-[#2a2a2a] hover:bg-gray-50 font-normal"
}`}
>
{i}
</button>
))}
</motion.div>
)}
</AnimatePresence>
</div>
</div>
<div className="flex items-center justify-between py-5">
<span className="font-poppins text-sm font-normal text-[#2a2a2a]">You Pay</span>
<div className="flex items-center gap-2">
<span className="font-poppins text-sm font-normal text-[#aaa] line-through">${strikedPrice}</span>
<span className="font-poppins text-2xl font-medium text-[#f95f62] tracking-tight">${basePrice}</span>
</div>
</div>
</div>
<div className="px-6 pb-6">
<motion.button whileHover={{ scale: 1.01 }} whileTap={{ scale: 0.98 }} onClick={handleProceedToPayment} className="w-full py-4 rounded-full bg-[#f95f62] text-white font-poppins text-base font-medium hover:bg-[#e8545a] transition-colors shadow-lg shadow-[#f95f62]/20 cursor-pointer">
Proceed to Pay
</motion.button>
</div>
</div>
);
}
/* ─── MAIN CHECKOUT PAGE 2 ─── */
export function CheckoutPage({
onHomeClick,
onPassesClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onContactUsClick,
onSignInClick,
onSignOutClick,
onProfileClick,
user,
currentPage,
}: any) {
const navigate = useNavigate();
// Default item (you can pass via props later)
const baseUrl = import.meta.env.VITE_BASE_URL;
const cityId = localStorage.getItem("cityId")
const { data: checkoutPageData, isLoading } = useGetCheckoutPageDataQuery(cityId)
const cityName = checkoutPageData?.city?.name ?? ""
const cityImage = checkoutPageData?.city?.heroBanner?.image ?? ""
const cards = checkoutPageData?.cards ?? []
const flexiCard = checkoutPageData?.cards[0] ?? null
const unlimitedCard = checkoutPageData?.cards[1] ?? null
const attractions = checkoutPageData?.attractions ?? [];
const [checkoutItem, setCheckoutItem] = useState(flexiCard);
useEffect(() => {
setCheckoutItem(flexiCard)
}, [cards])
if (isLoading) {
return <LoadingSpinner />
}
const handleCheckoutItemChange = (cardObject: any) => {
setCheckoutItem(cardObject);
};
return (
<div className="min-h-screen bg-[#fafafa] font-poppins">
<Navbar
activeCity="Melbourne"
onCityChange={() => { }}
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onPassesClick={onPassesClick}
onCheckoutClick={() => { }}
onHomeClick={onHomeClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={() => { }}
onMagicItineraryClick={() => { }}
onPostCardsClick={() => { }}
onOffersClick={() => { }}
onSuperSavingsClick={() => { }}
onEsimsClick={() => { }}
onHotelDiscountsClick={() => { }}
onCartClick={() => { }}
currentPage={currentPage}
user={user}
/>
<div className="w-full px-4 sm:px-6 lg:px-10 xl:px-16 pt-32 pb-24 max-w-[1440px] mx-auto">
<button onClick={() => navigate(-1)} className="flex items-center gap-2 text-[#8e8e8e] hover:text-[#2a2a2a] transition-colors font-poppins text-sm font-normal mb-8">
<ArrowLeft className="w-4 h-4" />Back
</button>
<div className="mb-10">
<h2 className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-tight">
<span className="font-light">Checkout</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-[#F95F62] to-[#F95FAF] bg-clip-text text-transparent pr-2">{cityName}</span>
</h2>
</div>
<div className="flex flex-col lg:flex-row gap-10">
{/* Left Column */}
<div className="flex-1 space-y-8">
{/* Card Type Selection */}
<div>
<h3 className="font-poppins text-xl md:text-2xl leading-snug font-medium text-[#2a2a2a]">Choose Your Card</h3>
<p className="font-poppins text-sm leading-relaxed font-normal text-[#8e8e8e] mt-1 mb-4">Select the card type that best suits your travel style</p>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-[16px]">
<button onClick={() => handleCheckoutItemChange(flexiCard)}>
<FlexiCardPreview city={cityName} image={cityImage} adultPrice={flexiCard.adultPrice} childPrice={flexiCard.childPrice} isSelected={checkoutItem?.cardType.name === 'selective_pass'} />
</button>
<button onClick={() => handleCheckoutItemChange(unlimitedCard)}>
<UnlimitedCardPreview city={cityName} image={cityImage} adultPrice={unlimitedCard.adultPrice} childPrice={unlimitedCard.childPrice} isSelected={checkoutItem?.cardType.name === 'unlimited_card'} />
</button>
</div>
{/* Features Comparison (Exact Copy) */}
<div className="mt-6 bg-[#f5f5f5] rounded-xl p-4">
<div className="grid grid-cols-[1fr_70px_70px] gap-y-0 items-center">
<p className="font-poppins font-medium text-sm text-[#2a2a2a] py-3">Features</p>
<p className="font-poppins font-medium text-sm text-[#2a2a2a] text-center py-3">Flexi</p>
<p className="font-poppins font-medium text-sm text-[#2a2a2a] text-center py-3">Unlimited</p>
{[
{ feature: 'Access to attractions', flexi: true, unlimited: true },
{ feature: 'Entry to attractions', flexi: true, unlimited: true },
{ feature: 'Access to experiences', flexi: true, unlimited: true },
{ feature: 'Entry to sites', flexi: false, unlimited: true },
{ feature: 'Access to venues', flexi: true, unlimited: true },
{ feature: 'Entry to events', flexi: true, unlimited: true },
{ feature: 'Access to experiences', flexi: false, unlimited: true },
{ feature: 'Access to Itinerary creation', flexi: false, unlimited: true },
{ feature: 'Access to postcard creation', flexi: false, unlimited: true },
].map((row, i) => (
<React.Fragment key={i}>
<p className="font-poppins font-normal text-[13px] text-[#2a2a2a] py-2.5 border-t border-[rgba(0,0,0,0.08)] flex items-center gap-1.5">
<span className="text-[#2a2a2a]"></span> {row.feature}
</p>
<div className="flex justify-center py-2.5 border-t border-[rgba(0,0,0,0.08)]">
{row.flexi ? <div className="w-5 h-5 rounded-full bg-[#F95F62] flex items-center justify-center"><Check className="w-3 h-3 text-white" strokeWidth={3} /></div> : <span className="font-poppins text-[13px] text-[rgba(0,0,0,0.3)]"></span>}
</div>
<div className="flex justify-center py-2.5 border-t border-[rgba(0,0,0,0.08)]">
{row.unlimited ? <div className="w-5 h-5 rounded-full bg-[#F95F62] flex items-center justify-center"><Check className="w-3 h-3 text-white" strokeWidth={3} /></div> : <span className="font-poppins text-[13px] text-[rgba(0,0,0,0.3)]"></span>}
</div>
</React.Fragment>
))}
</div>
</div>
</div>
{/* Offers Section (Exact) */}
<div>
<h3 className="font-poppins text-xl md:text-2xl leading-snug font-medium text-[#2a2a2a]">{checkoutItem?.cardType?.displayName} Offers</h3>
<p className="font-poppins text-sm leading-relaxed font-normal text-[#8e8e8e] mt-1 mb-4">Exclusive deals and discounts included with your {checkoutItem?.cardType.displayName} pass</p>
<div className="flex gap-3 overflow-x-auto pb-2 -mx-4 px-4 snap-x snap-mandatory scrollbar-hide">
{checkoutItem?.offers.map((offer: any) => (
<div key={offer.id} className="relative bg-white rounded-xl shrink-0 w-[180px] h-[260px] snap-start">
<div className="flex flex-col gap-2 items-start overflow-hidden p-3 rounded-xl h-full">
<div className="h-[120px] w-full rounded-lg overflow-hidden shrink-0 relative">
<ImageWithFallback src={`${baseUrl}/${offer.websiteBannerImage}`} alt={offer.title} className="absolute inset-0 w-full h-full object-cover rounded-lg" />
</div>
<div className="w-full h-[44px] overflow-hidden">
<p className="font-['Poppins',sans-serif] font-normal text-[18px] text-black tracking-[-0.72px] leading-[22px] line-clamp-2">{offer.title}</p>
</div>
<div className="w-full flex-1">
<p className="font-['Poppins',sans-serif] font-normal text-[12px] text-[rgba(0,0,0,0.6)] leading-[16px] line-clamp-3">{offer.description}</p>
</div>
</div>
<div className="absolute inset-0 border border-[rgba(249,95,98,0.24)] rounded-xl pointer-events-none" />
</div>
))}
</div>
</div>
{/* Attractions Section (Exact) */}
<div>
<div className="flex items-center justify-between">
<h3 className="font-poppins text-xl md:text-2xl leading-snug font-medium text-[#2a2a2a]">Available Attractions</h3>
<span className="font-poppins text-xs font-medium text-[#F95F62] bg-[#F95F62]/10 px-3 py-1 rounded-full">{attractions.length} included</span>
</div>
<p className="font-poppins text-sm leading-relaxed font-normal text-[#8e8e8e] mt-1 mb-4">Explore all the experiences you can enjoy with your pass</p>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
{attractions.map((a: any) => (
<div key={a.id} className="group relative rounded-xl overflow-hidden">
<div className="aspect-[4/3] relative">
<ImageWithFallback src={a.thumbnail} alt={a.title} className="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-105" />
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-black/10 to-transparent" />
<div className="absolute top-2 right-2">
<span className="inline-flex px-2 py-0.5 rounded-full bg-white/90 backdrop-blur-sm text-[10px] font-poppins font-medium text-[#555]">{a.category}</span>
</div>
<div className="absolute bottom-2 left-2 right-2">
<h6 className="font-poppins text-sm leading-snug font-medium text-white drop-shadow-sm">{a.title}</h6>
</div>
</div>
</div>
))}
</div>
</div>
</div>
{/* Right Column - Config Card */}
<div className="hidden lg:block lg:w-[420px] flex-shrink-0">
<div className="lg:sticky lg:top-28">
<CheckoutConfigCard
item={checkoutItem}
onChange={handleCheckoutItemChange}
// onProceed={() => navigate("/payment")}
/>
</div>
</div>
{/* Mobile Config Card */}
<div className="lg:hidden mt-6">
<CheckoutConfigCard
item={checkoutItem}
onChange={handleCheckoutItemChange}
// onProceed={() => navigate("/payment")}
/>
</div>
</div>
</div>
<Footer
onHomeClick={onHomeClick}
onPassesClick={onPassesClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onContactUsClick={onContactUsClick}
/>
</div>
);
}