"use client"; import { useEffect, useState, type ComponentType } from "react"; import dynamic from "next/dynamic"; import { api } from "@/lib/api"; import type { Destination, BlogPost, FAQ, Testimonial, Tool, Visa, Stats, ChartData } from "@/lib/types"; import Loader from "./Loader"; import ParticleCanvas from "./ParticleCanvas"; import Navbar from "./Navbar"; import TickerBar from "./TickerBar"; import Hero from "./Hero"; import WorldMap from "./WorldMap"; import Destinations from "./Destinations"; import GlobalSearch from "./GlobalSearch"; import Footer from "./Footer"; import CompareModal from "./CompareModal"; import DestinationMatcher, { MatcherTrigger } from "./DestinationMatcher"; import ScrollProgress from "./ScrollProgress"; import SectionNav from "./SectionNav"; import CookieConsent from "./CookieConsent"; import NomadTips from "./NomadTips"; import DailyQuote from "./DailyQuote"; import ToolsStrip from "./ToolsStrip"; import FeedbackWidget from "./FeedbackWidget"; import OnboardingTour from "./OnboardingTour"; import KeyboardShortcuts from "./KeyboardShortcuts"; import BackToTop from "./BackToTop"; import TripPlanner from "./TripPlanner"; import Calculator from "./Calculator"; import SpinGlobe from "./SpinGlobe"; const lazy =

(loader: () => Promise<{ default: ComponentType

}>) => dynamic(loader, { ssr: false, loading: () => null }); const TimezoneBoard = lazy(() => import("./TimezoneBoard")); const JetLagEstimator = lazy(() => import("./JetLagEstimator")); const CostCompare = lazy(() => import("./CostCompare")); const SavingsGoal = lazy(() => import("./SavingsGoal")); const RunwayCalculator = lazy(() => import("./RunwayCalculator")); const ExpenseTracker = lazy(() => import("./ExpenseTracker")); const TaxDayCounter = lazy(() => import("./TaxDayCounter")); const FlightCost = lazy(() => import("./FlightCost")); const CurrencyConverter = lazy(() => import("./CurrencyConverter")); const MeetingWindow = lazy(() => import("./MeetingWindow")); const WifiWorkScore = lazy(() => import("./WifiWorkScore")); const FocusTimer = lazy(() => import("./FocusTimer")); const NomadScore = lazy(() => import("./NomadScore")); const Lifestyle = lazy(() => import("./Lifestyle")); const Charts = lazy(() => import("./Charts")); const VisaSection = lazy(() => import("./VisaSection")); const InsuranceGuide = lazy(() => import("./InsuranceGuide")); const EmergencyContacts = lazy(() => import("./EmergencyContacts")); const AtmCashGuide = lazy(() => import("./AtmCashGuide")); const FoodWaterTips = lazy(() => import("./FoodWaterTips")); const CoworkingGuide = lazy(() => import("./CoworkingGuide")); const HousingGuide = lazy(() => import("./HousingGuide")); const SimGuide = lazy(() => import("./SimGuide")); const PowerPlugGuide = lazy(() => import("./PowerPlugGuide")); const CafeGuide = lazy(() => import("./CafeGuide")); const SeasonGuide = lazy(() => import("./SeasonGuide")); const PackingChecklist = lazy(() => import("./PackingChecklist")); const ArrivalChecklist = lazy(() => import("./ArrivalChecklist")); const Phrasebook = lazy(() => import("./Phrasebook")); const CityNotes = lazy(() => import("./CityNotes")); const NomadEvents = lazy(() => import("./NomadEvents")); const Tools = lazy(() => import("./Tools")); const BlogSection = lazy(() => import("./BlogSection")); const FAQSection = lazy(() => import("./FAQSection")); const Community = lazy(() => import("./Community")); interface Props { stats: Stats; ticker: string[]; destinations: Destination[]; visas: Visa[]; faqs: FAQ[]; testimonials: Testimonial[]; tools: Tool[]; blog: BlogPost[]; charts: { cost: ChartData; speed: ChartData; growth: ChartData; radar: ChartData }; } export default function HomeClient(props: Props) { const [compareList, setCompareList] = useState([]); const [compareResults, setCompareResults] = useState(null); const [compareOpen, setCompareOpen] = useState(false); const [matcherOpen, setMatcherOpen] = useState(false); useEffect(() => { const onMatcher = () => setMatcherOpen(true); window.addEventListener("open-matcher", onMatcher); return () => window.removeEventListener("open-matcher", onMatcher); }, []); useEffect(() => { const observer = new IntersectionObserver( (entries) => entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("visible"); observer.unobserve(e.target); } }), { threshold: 0.1, rootMargin: "0px 0px -50px 0px" } ); let scheduled = 0; const observeAll = () => { document.querySelectorAll(".reveal:not(.visible)").forEach((el, i) => { (el as HTMLElement).style.transitionDelay = `${(i % 6) * 0.1}s`; observer.observe(el); }); }; const schedule = () => { if (scheduled) return; scheduled = window.setTimeout(() => { scheduled = 0; observeAll(); }, 80); }; observeAll(); const mo = new MutationObserver(schedule); mo.observe(document.body, { childList: true, subtree: true }); return () => { observer.disconnect(); mo.disconnect(); if (scheduled) clearTimeout(scheduled); }; }, []); const toggleCompare = (slug: string) => { setCompareList((prev) => { if (prev.includes(slug)) return prev.filter((s) => s !== slug); if (prev.length >= 4) return prev; return [...prev, slug]; }); setCompareResults(null); }; const runCompare = async () => { if (compareList.length < 2) return; try { const data = await api.compareDestinations(compareList); setCompareResults(data); setCompareOpen(true); } catch { /* ignore */ } }; return ( <> setMatcherOpen(true)} />