LUX VIP

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>LuxHarbor Weather</title>

    

    <!-- 1. Tailwind CSS -->

    <script src="https://cdn.tailwindcss.com"></script>


    <!-- 2. Import Map for React modules -->

    <script type="importmap">

    {

      "imports": {

        "react": "https://esm.sh/[email protected]",

        "react-dom/client": "https://esm.sh/[email protected]/client",

        "lucide-react": "https://esm.sh/[email protected]"

      }

    }

    </script>


    <!-- 3. Babel for JSX compilation -->

    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>


    <style>

        /* Custom scrollbar hiding utility */

        .scrollbar-hide::-webkit-scrollbar {

            display: none;

        }

        .scrollbar-hide {

            -ms-overflow-style: none;

            scrollbar-width: none;

        }

    </style>

</head>

<body class="bg-slate-950 text-slate-200">

    <div id="root"></div>


    <!-- 4. Your Application Code -->

    <script type="text/babel" data-type="module">

        import React, { useState, useEffect, useMemo } from 'react';

        import { createRoot } from 'react-dom/client';

        import { 

            Search, MapPin, Cloud, Sun, CloudSun, CloudRain, Wind, 

            Droplets, Thermometer, ArrowRight, Copy, Check, Menu, X, 

            ChevronDown, ChevronUp, Eye, Sunrise, Sunset, Umbrella, 

            Navigation, Share2, Code, Layout, Palette, Settings, MoreHorizontal 

        } from 'lucide-react';


        /**

         * --- MOCK DATA GENERATORS & MODELS ---

         */


        const CONDITIONS = [

            { text: "Sunny", icon: Sun, color: "text-yellow-400" },

            { text: "Partly Cloudy", icon: CloudSun, color: "text-yellow-200" },

            { text: "Cloudy", icon: Cloud, color: "text-gray-400" },

            { text: "Rain Shower", icon: CloudRain, color: "text-blue-400" },

            { text: "Heavy Rain", icon: CloudRain, color: "text-blue-600" },

        ];


        const generateWeather = (city) => {

            const currentCond = CONDITIONS[Math.floor(Math.random() * CONDITIONS.length)];

            

            // Current Data

            const current = {

                temp: 72,

                feels_like: 75,

                condition_text: currentCond.text,

                icon: currentCond.icon,

                iconColor: currentCond.color,

                high: 78,

                low: 65,

                wind_speed: "8 mph",

                wind_dir: "NW",

                humidity: "45%",

                dew_point: "58°",

                pressure: "29.92 in",

                uv_index: "6 of 10",

                visibility: "10 mi",

                sunrise: "6:23 am",

                sunset: "8:14 pm",

                moon_phase: "Waxing Gibbous",

                outlook_text: `Expect ${currentCond.text.toLowerCase()} conditions to continue until late afternoon. Wind gusts may increase slightly.`

            };


            // Hourly Data (Next 24h)

            const hourly = Array.from({ length: 24 }, (_, i) => {

                const hour = (new Date().getHours() + i) % 24;

                const timeLabel = hour === 0 ? "12 am" : hour > 12 ? `${hour - 12} pm` : `${hour} am`;

                const cond = CONDITIONS[Math.floor(Math.random() * CONDITIONS.length)];

                return {

                timestamp: i,

                time: timeLabel,

                temp: 72 - Math.floor(Math.random() * 10) + (i > 6 && i < 18 ? 10 : 0),

                condition_text: cond.text,

                icon: cond.icon,

                precip_chance: Math.floor(Math.random() * 30) + "%",

                wind: `${5 + Math.floor(Math.random() * 10)} mph ${['N','NE','E','SE','S','SW','W','NW'][Math.floor(Math.random()*8)]}`,

                humidity: `${40 + Math.floor(Math.random() * 40)}%`,

                feels_like: `${70 + Math.floor(Math.random() * 10)}°`,

                uv_index: i > 6 && i < 18 ? Math.floor(Math.random() * 8) : 0,

                cloud_cover: `${Math.floor(Math.random() * 100)}%`

                };

            });


            // Daily Data (10 Days)

            const days = ["Today", "Fri 12", "Sat 13", "Sun 14", "Mon 15", "Tue 16", "Wed 17", "Thu 18", "Fri 19", "Sat 20"];

            const daily = days.map((day, i) => {

                const cond = CONDITIONS[Math.floor(Math.random() * CONDITIONS.length)];

                return {

                date: day,

                high: 70 + Math.floor(Math.random() * 15),

                low: 55 + Math.floor(Math.random() * 10),

                condition_text: cond.text,

                icon: cond.icon,

                precip_chance: Math.floor(Math.random() * 60) + "%",

                wind: `${5 + Math.floor(Math.random() * 15)} mph`,

                humidity: `${50 + Math.floor(Math.random() * 30)}%`,

                sunrise: "6:25 am",

                sunset: "8:10 pm"

                };

            });


            return { location: { name: city, id: city.toLowerCase() }, current, hourly, daily };

        };


        const SAMPLE_PROMPTS = [

            {

                id: 'p1',

                title: 'Morning Walk Planner',

                description: 'Determine the best time for a walk based on conditions.',

                category: 'Daily Planning',

                tags: ['health', 'active'],

                template: "Analyze the forecast for {{city}}. Given the current temp of {{temp}} and {{condition}}, suggest the best 1-hour window for a walk today.",

            },

            {

                id: 'p2',

                title: 'Travel Commute Risk',

                description: 'Assess driving risks for the next 24 hours.',

                category: 'Travel',

                tags: ['safety', 'commute'],

                template: "I am driving in {{city}}. Review the hourly forecast. Are there any visibility ({{visibility}}) or precipitation risks I should know about?",

            },

            {

                id: 'p3',

                title: 'Event Go/No-Go',

                description: 'Decision helper for outdoor events.',

                category: 'Events',

                tags: ['planning', 'outdoor'],

                template: "I have an outdoor event planned in {{city}}. With a high of {{high}} and precip chance of {{precip}}, should I move it indoors?",

            },

            {

                id: 'p4',

                title: 'Allergy & Air Quality',

                description: 'Check air quality impact on health.',

                category: 'Health',

                tags: ['medical', 'wellness'],

                template: "Check the current humidity ({{humidity}}) and wind ({{wind}}) in {{city}}. Advise on allergy risks for today.",

            },

        ];


        /**

         * --- UTILITIES ---

         */

        const cn = (...classes) => classes.filter(Boolean).join(' ');


        /**

         * --- COMPONENTS ---

         */


        // 1. HEADER

        const Header = ({ activeTab, onTabChange, onSearch, searchQuery, setSearchQuery, onOpenWidget }) => (

            <header className="bg-slate-900 text-white shadow-lg sticky top-0 z-50">

                <div className="max-w-7xl mx-auto px-4">

                {/* Top Bar */}

                <div className="flex flex-col md:flex-row items-center justify-between py-3 gap-4">

                    {/* Logo */}

                    <div className="flex items-center gap-2">

                    <div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center text-white shadow-blue-500/20 shadow-lg">

                        <CloudSun size={20} strokeWidth={2.5} />

                    </div>

                    <span className="text-xl font-bold tracking-tight">LuxHarbor <span className="text-blue-400 font-light">Weather</span></span>

                    </div>


                    {/* Search */}

                    <form onSubmit={(e) => { e.preventDefault(); onSearch(searchQuery); }} className="relative w-full md:w-96 group">

                    <input 

                        type="text" 

                        placeholder="Search city or ZIP..." 

                        value={searchQuery}

                        onChange={(e) => setSearchQuery(e.target.value)}

                        className="w-full bg-slate-800 border border-slate-700 text-slate-100 rounded-full py-2 pl-10 pr-4 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"

                    />

                    <Search className="absolute left-3 top-2.5 text-slate-400 h-4 w-4" />

                    </form>


                    {/* Actions - Hidden for visitor */}

                    <div className="hidden md:flex w-8"></div> 

                </div>


                {/* Navigation Tabs */}

                <nav className="flex items-center gap-1 md:gap-6 overflow-x-auto scrollbar-hide -mb-px pt-2">

                    {['Today', 'Hourly', '10-Day', 'Radar', 'Prompt Library'].map(tab => (

                    <button

                        key={tab}

                        onClick={() => onTabChange(tab)}

                        className={cn(

                        "px-2 md:px-0 py-3 text-sm font-medium border-b-2 transition-colors whitespace-nowrap",

                        activeTab === tab 

                            ? "border-blue-500 text-white" 

                            : "border-transparent text-slate-400 hover:text-slate-200 hover:border-slate-700"

                        )}

                    >

                        {tab}

                    </button>

                    ))}

                </nav>

                </div>

            </header>

        );


        // 2. TODAY VIEW

        const TodayView = ({ data, onNavigate }) => {

            const { current, location } = data;

            const Icon = current.icon;


            return (

                <div className="space-y-6 animate-in fade-in duration-500">

                {/* Location Header */}

                <div className="flex justify-between items-baseline">

                    <h2 className="text-2xl font-bold text-slate-100 flex items-center gap-2">

                    <MapPin size={24} className="text-blue-500" />

                    {location.name}, US

                    </h2>

                    <span className="text-slate-400 text-sm">As of {new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} EST</span>

                </div>


                {/* Main Current Conditions Card */}

                <div className="bg-slate-800 rounded-xl p-6 md:p-8 shadow-xl border border-slate-700/50">

                    <div className="flex flex-col md:flex-row justify-between">

                    

                    {/* Left: Big Temp & Condition */}

                    <div className="flex-1 mb-6 md:mb-0">

                        <div className="flex items-center gap-4 mb-2">

                        <span className={`text-4xl md:text-5xl font-bold ${current.iconColor}`}>{current.temp}°</span>

                        <div className="flex flex-col">

                            <span className="text-xl font-medium text-slate-100">{current.condition_text}</span>

                            <span className="text-slate-400">Day {current.high}° • Night {current.low}°</span>

                        </div>

                        </div>

                        <div className="mt-4 inline-block bg-slate-700/50 px-3 py-1 rounded-full text-sm font-medium text-blue-200">

                        Feels Like {current.feels_like}

                        </div>

                    </div>


                    {/* Right: Grid of Metrics */}

                    <div className="flex-1 grid grid-cols-2 gap-y-4 gap-x-8 text-sm border-t md:border-t-0 md:border-l border-slate-700 pt-6 md:pt-0 md:pl-8">

                        <MetricRow icon={Wind} label="Wind" value={`${current.wind_dir} ${current.wind_speed}`} />

                        <MetricRow icon={Droplets} label="Humidity" value={current.humidity} />

                        <MetricRow icon={Sun} label="UV Index" value={current.uv_index} />

                        <MetricRow icon={Eye} label="Visibility" value={current.visibility} />

                        <MetricRow icon={Thermometer} label="Dew Point" value={current.dew_point} />

                        <MetricRow icon={Navigation} label="Pressure" value={current.pressure} />

                        <MetricRow icon={Sunrise} label="Sunrise" value={current.sunrise} />

                        <MetricRow icon={Sunset} label="Sunset" value={current.sunset} />

                    </div>

                    </div>

                </div>


                {/* Outlook Strip */}

                <div className="bg-blue-900/20 border border-blue-900/50 p-4 rounded-lg flex items-start gap-3">

                    <InfoIcon className="text-blue-400 shrink-0 mt-0.5" size={18} />

                    <div>

                    <h4 className="font-semibold text-blue-100 text-sm uppercase tracking-wide mb-1">Outlook</h4>

                    <p className="text-blue-200 text-sm">{current.outlook_text}</p>

                    </div>

                </div>


                <button 

                    onClick={() => onNavigate('Hourly')}

                    className="w-full py-3 bg-slate-800 hover:bg-slate-700 text-blue-400 font-medium rounded-lg border border-slate-700 transition-colors flex items-center justify-center gap-2"

                >

                    View Hourly Forecast <ArrowRight size={16} />

                </button>


                {/* Content Blocks */}

                <div className="grid grid-cols-1 md:grid-cols-2 gap-6 pt-4">

                    <ContentCard title="Travel & Commute" type="travel">

                    Roads are dry with good visibility. No major weather-related delays expected on I-95 corridor this evening.

                    </ContentCard>

                    <ContentCard title="Health & Allergies" type="health">

                    Air quality is Moderate. Pollen counts are low, but sensitive groups should limit prolonged outdoor exertion.

                    </ContentCard>

                </div>


                <FooterNote />

                </div>

            );

        };


        // 3. HOURLY VIEW

        const HourlyView = ({ data }) => {

            const [expandedRow, setExpandedRow] = useState(null);


            return (

                <div className="space-y-6 animate-in fade-in slide-in-from-bottom-2 duration-500">

                <div className="flex justify-between items-center">

                    <h2 className="text-xl font-bold text-slate-100">Hourly Forecast</h2>

                    <span className="text-slate-400 text-sm">Next 24 Hours</span>

                </div>


                <div className="bg-slate-800 border border-slate-700 rounded-xl overflow-hidden shadow-xl">

                    {data.hourly.map((hour, idx) => {

                    const isExpanded = expandedRow === idx;

                    const Icon = hour.icon;

                    return (

                        <div key={idx} className="border-b border-slate-700/50 last:border-0">

                        {/* Summary Row */}

                        <div 

                            onClick={() => setExpandedRow(isExpanded ? null : idx)}

                            className="flex items-center justify-between p-4 cursor-pointer hover:bg-slate-700/30 transition-colors"

                        >

                            <div className="w-20 font-medium text-slate-200">{hour.time}</div>

                            <div className="flex items-center gap-2 w-16 text-2xl font-bold text-slate-100">{hour.temp}°</div>

                            <div className="flex items-center gap-3 flex-1 px-4">

                            <Icon size={20} className="text-slate-400" />

                            <span className="text-slate-300 text-sm hidden md:block">{hour.condition_text}</span>

                            </div>

                            <div className="hidden md:flex items-center gap-2 w-24 text-blue-400 text-sm">

                            <Umbrella size={14} /> {hour.precip_chance}

                            </div>

                            <div className="hidden md:flex items-center gap-2 w-24 text-slate-400 text-sm">

                            <Wind size={14} /> {hour.wind}

                            </div>

                            <div className="text-slate-500">

                            {isExpanded ? <ChevronUp size={20} /> : <ChevronDown size={20} />}

                            </div>

                        </div>


                        {/* Detailed Expansion */}

                        {isExpanded && (

                            <div className="bg-slate-900/50 p-4 grid grid-cols-2 md:grid-cols-4 gap-4 text-sm border-t border-slate-700/50 animate-in slide-in-from-top-1">

                            <DetailBox label="Feels Like" value={hour.feels_like} />

                            <DetailBox label="Humidity" value={hour.humidity} />

                            <DetailBox label="UV Index" value={hour.uv_index} />

                            <DetailBox label="Cloud Cover" value={hour.cloud_cover} />

                            <DetailBox label="Wind" value={hour.wind} />

                            <DetailBox label="Rain Chance" value={hour.precip_chance} />

                            </div>

                        )}

                        </div>

                    );

                    })}

                </div>

                <FooterNote />

                </div>

            );

        };


        // 4. 10-DAY VIEW

        const TenDayView = ({ data }) => {

            const [expandedDay, setExpandedDay] = useState(null);


            return (

                <div className="space-y-6 animate-in fade-in slide-in-from-bottom-2 duration-500">

                <h2 className="text-xl font-bold text-slate-100">10-Day Forecast</h2>

                

                <div className="bg-slate-800 border border-slate-700 rounded-xl overflow-hidden shadow-xl">

                    {data.daily.map((day, idx) => {

                    const isExpanded = expandedDay === idx;

                    const Icon = day.icon;

                    return (

                        <div key={idx} className="border-b border-slate-700/50 last:border-0">

                        <div 

                            onClick={() => setExpandedDay(isExpanded ? null : idx)}

                            className="flex items-center justify-between p-4 cursor-pointer hover:bg-slate-700/30 transition-colors"

                        >

                            <div className="w-24 font-medium text-slate-200">{day.date}</div>

                            <div className="w-20 text-center">

                            <span className="text-xl font-bold text-slate-100">{day.high}°</span>

                            <span className="text-slate-500 text-sm ml-2">{day.low}°</span>

                            </div>

                            <div className="flex items-center gap-3 flex-1 px-4 justify-center md:justify-start">

                            <Icon size={20} className="text-slate-400" />

                            <span className="text-slate-300 text-sm hidden md:block">{day.condition_text}</span>

                            </div>

                            <div className="hidden md:flex items-center gap-2 w-24 text-blue-400 text-sm">

                            <Umbrella size={14} /> {day.precip_chance}

                            </div>

                            <div className="text-slate-500">

                            {isExpanded ? <ChevronUp size={20} /> : <ChevronDown size={20} />}

                            </div>

                        </div>


                        {isExpanded && (

                            <div className="bg-slate-900/50 p-4 grid grid-cols-2 gap-4 text-sm border-t border-slate-700/50">

                            <DetailBox label="Humidity" value={day.humidity} />

                            <DetailBox label="Wind" value={day.wind} />

                            <DetailBox label="Sunrise" value={day.sunrise} />

                            <DetailBox label="Sunset" value={day.sunset} />

                            <div className="col-span-2 text-slate-400 text-xs mt-2 italic">

                                LuxHarbor Tip: A great day for outdoor activities in the morning before the heat sets in.

                            </div>

                            </div>

                        )}

                        </div>

                    );

                    })}

                </div>

                <FooterNote />

                </div>

            );

        };


        // 5. PROMPT LIBRARY

        const PromptLibrary = ({ prompts, onUse }) => {

            const [filter, setFilter] = useState('All');

            const filtered = filter === 'All' ? prompts : prompts.filter(p => p.category === filter);


            return (

                <div className="animate-in fade-in zoom-in duration-300 space-y-6">

                <div className="bg-gradient-to-r from-blue-900/50 to-slate-800 p-8 rounded-xl border border-slate-700 text-center">

                    <h2 className="text-2xl font-bold text-white mb-2">Weather Prompt Library</h2>

                    <p className="text-blue-200 max-w-xl mx-auto">

                    AI-ready templates that automatically adapt to the current forecast. Select a prompt to inject live data.

                    </p>

                </div>


                <div className="flex gap-2 overflow-x-auto pb-2 scrollbar-hide">

                    {['All', 'Daily Planning', 'Travel', 'Events', 'Health'].map(cat => (

                    <button

                        key={cat}

                        onClick={() => setFilter(cat)}

                        className={cn(

                        "px-4 py-2 rounded-full text-sm font-medium border transition-colors whitespace-nowrap",

                        filter === cat

                            ? "bg-blue-600 border-blue-600 text-white shadow-lg shadow-blue-900/20"

                            : "bg-slate-800 border-slate-700 text-slate-300 hover:bg-slate-700"

                        )}

                    >

                        {cat}

                    </button>

                    ))}

                </div>


                <div className="grid grid-cols-1 md:grid-cols-2 gap-6">

                    {filtered.map(prompt => (

                    <div key={prompt.id} className="bg-slate-800 border border-slate-700 p-6 rounded-xl hover:border-blue-500/50 transition-colors flex flex-col">

                        <div className="flex justify-between items-start mb-4">

                        <span className="text-xs font-bold text-blue-400 bg-blue-900/30 px-2 py-1 rounded uppercase tracking-wide">

                            {prompt.category}

                        </span>

                        <Settings size={16} className="text-slate-600" />

                        </div>

                        <h3 className="text-lg font-bold text-slate-100 mb-2">{prompt.title}</h3>

                        <p className="text-slate-400 text-sm mb-6 flex-1">{prompt.description}</p>

                        <button

                        onClick={() => onUse(prompt)}

                        className="w-full py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg font-medium transition-colors flex items-center justify-center gap-2"

                        >

                        <Share2 size={16} /> Use with Current Forecast

                        </button>

                    </div>

                    ))}

                </div>

                <FooterNote />

                </div>

            );

        };


        // 6. WIDGET BUILDER

        const WidgetBuilder = ({ weather }) => {

            const [config, setConfig] = useState({

                theme: 'dark', // dark, light, transparent

                layout: 'sidebar', // sidebar, compact

                units: 'F'

            });


            const embedCode = `<iframe src="https://luxharborweather.com/widget?id=${weather.location.id}&theme=${config.theme}&layout=${config.layout}" width="100%" height="400" frameborder="0"></iframe>`;


            return (

                <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 animate-in fade-in slide-in-from-bottom-4">

                {/* Controls */}

                <div className="space-y-6">

                    <div>

                    <h2 className="text-2xl font-bold text-slate-100 mb-2">Widget Builder</h2>

                    <p className="text-slate-400">Customize and embed a free weather widget for your site.</p>

                    </div>


                    <div className="bg-slate-800 border border-slate-700 rounded-xl p-6 space-y-6">

                    <div>

                        <label className="block text-sm font-medium text-slate-300 mb-3 flex items-center gap-2">

                        <Palette size={16} /> Theme

                        </label>

                        <div className="grid grid-cols-3 gap-3">

                        {['dark', 'light', 'transparent'].map(t => (

                            <button

                            key={t}

                            onClick={() => setConfig({...config, theme: t})}

                            className={cn(

                                "py-2 px-4 rounded-lg border text-sm capitalize transition-all",

                                config.theme === t 

                                ? "border-blue-500 bg-blue-500/10 text-blue-400 ring-1 ring-blue-500" 

                                : "border-slate-600 bg-slate-700 text-slate-300 hover:bg-slate-600"

                            )}

                            >

                            {t}

                            </button>

                        ))}

                        </div>

                    </div>


                    <div>

                        <label className="block text-sm font-medium text-slate-300 mb-3 flex items-center gap-2">

                        <Layout size={16} /> Layout

                        </label>

                        <div className="grid grid-cols-2 gap-3">

                        {['sidebar', 'compact'].map(l => (

                            <button

                            key={l}

                            onClick={() => setConfig({...config, layout: l})}

                            className={cn(

                                "py-2 px-4 rounded-lg border text-sm capitalize transition-all",

                                config.layout === l

                                ? "border-blue-500 bg-blue-500/10 text-blue-400 ring-1 ring-blue-500" 

                                : "border-slate-600 bg-slate-700 text-slate-300 hover:bg-slate-600"

                            )}

                            >

                            {l}

                            </button>

                        ))}

                        </div>

                    </div>

                    </div>


                    <div className="bg-slate-900 rounded-xl border border-slate-800 p-4 relative group">

                    <div className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity">

                        <button 

                        onClick={() => navigator.clipboard.writeText(embedCode)}

                        className="bg-blue-600 text-white text-xs px-2 py-1 rounded hover:bg-blue-500"

                        >

                        Copy

                        </button>

                    </div>

                    <pre className="text-slate-400 font-mono text-xs whitespace-pre-wrap break-all">

                        {embedCode}

                    </pre>

                    </div>

                </div>


                {/* Live Preview */}

                <div className="bg-slate-900/50 rounded-xl border-2 border-dashed border-slate-700 p-8 flex items-center justify-center">

                    {/* Mock Widget Iframe */}

                    <div className={cn(

                    "rounded-lg overflow-hidden shadow-2xl transition-all duration-300",

                    config.layout === 'sidebar' ? "w-64" : "w-full max-w-sm",

                    config.theme === 'light' ? "bg-white text-slate-900" : config.theme === 'transparent' ? "bg-black/40 text-white backdrop-blur-md border border-white/10" : "bg-slate-800 text-white"

                    )}>

                    <div className="p-4 border-b border-current/10 flex justify-between items-center">

                        <span className="font-bold text-sm">Baltimore, US</span>

                        <CloudSun size={16} />

                    </div>

                    <div className="p-6 text-center">

                        <div className="text-4xl font-bold mb-1">72°</div>

                        <div className="text-sm opacity-80">Partly Cloudy</div>

                    </div>

                    {config.layout === 'sidebar' && (

                        <div className="bg-current/5 p-3 text-xs space-y-2">

                        <div className="flex justify-between"><span>Tomorrow</span> <span>76°/62°</span></div>

                        <div className="flex justify-between"><span>Saturday</span> <span>70°/55°</span></div>

                        <div className="flex justify-between"><span>Sunday</span> <span>68°/54°</span></div>

                        </div>

                    )}

                    <div className="p-2 text-[10px] text-center opacity-50 bg-current/5">

                        Powered by LuxHarbor

                    </div>

                    </div>

                </div>

                </div>

            );

        };


        // HELPER COMPONENTS

        const MetricRow = ({ icon: Icon, label, value }) => (

            <div className="flex items-center gap-3 py-2 border-b border-slate-700/50 last:border-0">

                <Icon className="text-blue-500 shrink-0" size={18} />

                <span className="text-slate-400 flex-1">{label}</span>

                <span className="text-slate-200 font-medium">{value}</span>

            </div>

        );


        const DetailBox = ({ label, value }) => (

            <div className="bg-slate-800/50 p-2 rounded border border-slate-700/50">

                <span className="block text-slate-500 text-xs uppercase mb-1">{label}</span>

                <span className="block text-slate-200 font-medium">{value}</span>

            </div>

        );


        const ContentCard = ({ title, children, type }) => (

            <div className="bg-slate-800 border border-slate-700 p-5 rounded-xl">

                <h3 className="font-bold text-slate-100 mb-3 flex items-center gap-2">

                {type === 'travel' ? <Navigation size={18} className="text-blue-400" /> : <Thermometer size={18} className="text-red-400" />}

                {title}

                </h3>

                <p className="text-slate-400 text-sm leading-relaxed">{children}</p>

            </div>

        );


        const FooterNote = () => (

            <div className="text-center text-xs text-slate-600 pt-8 pb-4">

                Demo data only; connect to your preferred weather API for production use. <br/>

                &copy; 2025 LuxHarbor Weather.

            </div>

        );


        const InfoIcon = ({ className, size }) => <div className={className}><svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg></div>;


        const PromptModal = ({ isOpen, onClose, prompt, weather }) => {

            if (!isOpen) return null;

            const interpolated = prompt.template

                .replace('{{city}}', weather.location.name)

                .replace('{{temp}}', weather.current.temp + '°')

                .replace('{{condition}}', weather.current.condition_text)

                .replace('{{humidity}}', weather.current.humidity)

                .replace('{{wind}}', weather.current.wind_speed)

                .replace('{{high}}', weather.current.high + '°')

                .replace('{{precip}}', weather.daily[0].precip_chance)

                .replace('{{visibility}}', weather.current.visibility);


            return (

                <div className="fixed inset-0 z-[60] flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm">

                <div className="bg-slate-900 border border-slate-700 w-full max-w-lg rounded-xl shadow-2xl overflow-hidden animate-in zoom-in-95 duration-200">

                    <div className="p-4 border-b border-slate-700 flex justify-between items-center bg-slate-800/50">

                    <h3 className="text-slate-100 font-bold">Use Prompt</h3>

                    <button onClick={onClose}><X size={20} className="text-slate-400 hover:text-white" /></button>

                    </div>

                    <div className="p-6 space-y-4">

                    <div className="bg-blue-900/20 border border-blue-900/50 p-3 rounded text-xs text-blue-300">

                        Current weather data for {weather.location.name} has been injected.

                    </div>

                    <textarea 

                        readOnly 

                        className="w-full h-32 bg-slate-950 border border-slate-800 rounded-lg p-3 text-slate-300 font-mono text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none resize-none"

                        value={interpolated}

                    />

                    <div className="flex gap-3 justify-end">

                        <button onClick={onClose} className="px-4 py-2 text-slate-400 hover:text-white text-sm">Cancel</button>

                        <button 

                        onClick={() => {

                            navigator.clipboard.writeText(interpolated);

                            onClose();

                        }} 

                        className="px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg text-sm font-medium flex items-center gap-2"

                        >

                        <Copy size={16} /> Copy Prompt

                        </button>

                    </div>

                    </div>

                </div>

                </div>

            );

        };


        /**

         * --- MAIN APP ---

         */

        function LuxHarborApp() {

            const [activeTab, setActiveTab] = useState('Today');

            const [weatherData, setWeatherData] = useState(null);

            const [searchQuery, setSearchQuery] = useState('Baltimore');

            const [loading, setLoading] = useState(true);

            

            // Modal State

            const [modalOpen, setModalOpen] = useState(false);

            const [selectedPrompt, setSelectedPrompt] = useState(null);


            // Initial Load

            useEffect(() => {

                handleSearch('Baltimore');

            }, []);


            const handleSearch = (city) => {

                setLoading(true);

                // Simulate API Fetch

                setTimeout(() => {

                setWeatherData(generateWeather(city));

                setLoading(false);

                }, 600);

            };


            const handleOpenPrompt = (prompt) => {

                setSelectedPrompt(prompt);

                setModalOpen(true);

            };


            const renderContent = () => {

                if (loading) return (

                <div className="flex flex-col items-center justify-center py-32">

                    <div className="w-10 h-10 border-4 border-blue-500 border-t-transparent rounded-full animate-spin mb-4"></div>

                    <div className="text-slate-400 animate-pulse">Fetching forecast data...</div>

                </div>

                );


                switch(activeTab) {

                case 'Today': return <TodayView data={weatherData} onNavigate={setActiveTab} />;

                case 'Hourly': return <HourlyView data={weatherData} />;

                case '10-Day': return <TenDayView data={weatherData} />;

                case 'Radar': return (

                    <div className="bg-slate-800 border border-slate-700 rounded-xl h-96 flex flex-col items-center justify-center text-slate-500 animate-in fade-in">

                    <MapPin size={48} className="mb-4 opacity-50" />

                    <h3 className="text-xl font-bold text-slate-300">Interactive Radar</h3>

                    <p className="text-sm">Map integration coming soon.</p>

                    </div>

                );

                case 'Prompt Library': return <PromptLibrary prompts={SAMPLE_PROMPTS} onUse={handleOpenPrompt} />;

                case 'Widget': return <WidgetBuilder weather={weatherData} />;

                default: return null;

                }

            };


            return (

                <div className="min-h-screen bg-slate-950 text-slate-200 font-sans selection:bg-blue-500/30">

                <Header 

                    activeTab={activeTab === 'Widget' ? 'Today' : activeTab} // Keep main tab active if widget modal

                    onTabChange={setActiveTab} 

                    onSearch={handleSearch} 

                    searchQuery={searchQuery} 

                    setSearchQuery={setSearchQuery}

                    onOpenWidget={() => setActiveTab('Widget')}

                />


                <main className="max-w-4xl mx-auto px-4 py-8">

                    {renderContent()}

                </main>


                <PromptModal 

                    isOpen={modalOpen} 

                    onClose={() => setModalOpen(false)} 

                    prompt={selectedPrompt} 

                    weather={weatherData} 

                />

                </div>

            );

        }


        // --- RENDER APP ---

        const root = createRoot(document.getElementById('root'));

        root.render(<LuxHarborApp />);

    </script>

</body>

</html>