// Main React app — renders the iPhone-framed interactive itinerary
const { useState, useEffect, useRef, useMemo } = React;

const TRIP = window.TRIP;
const INFO = window.INFO || {};

function useRouteMilesText() {
  const [milesText, setMilesText] = useState(TRIP.totalMiles);
  useEffect(() => {
    const onMetrics = (e) => {
      const next = e && e.detail && e.detail.totalMilesText;
      if (next) setMilesText(next);
    };
    window.addEventListener("route-metrics-updated", onMetrics);
    return () => window.removeEventListener("route-metrics-updated", onMetrics);
  }, []);
  return milesText;
}

function useRouteDayMetrics() {
  const [perDay, setPerDay] = useState({});
  useEffect(() => {
    const onMetrics = (e) => {
      const next = e && e.detail && e.detail.perDay;
      if (next && typeof next === 'object') {
        setPerDay(next);
      }
    };
    window.addEventListener("route-metrics-updated", onMetrics);
    return () => window.removeEventListener("route-metrics-updated", onMetrics);
  }, []);
  return perDay;
}

function useRoadtripAuthState() {
  const [auth, setAuth] = useState(() => {
    if (window.RoadtripAuth && window.RoadtripAuth.getState) {
      return window.RoadtripAuth.getState();
    }
    return { ready: true, user: null, error: "Auth not initialized" };
  });
  useEffect(() => {
    if (!window.RoadtripAuth || !window.RoadtripAuth.subscribe) return;
    return window.RoadtripAuth.subscribe(setAuth);
  }, []);
  return auth;
}

// ===== Scene components (pure CSS illustrations) =====
// `dayN` (when supplied) replaces the static `temp` with live weather (high/low + condition).
function Scene({ kind, label, temp, dayN }) {
  return (
    <div className={`scene scene-${kind}`}>
      {label && <div className="scene-label">{label}</div>}
      {dayN && window.WeatherChip
        ? <window.WeatherChip dayN={dayN} />
        : (temp && <div className="scene-temp">{temp}</div>)}
      {kind === 'cover' && <CoverSceneBits />}
      {kind === 'd1' && <D1Bits />}
      {kind === 'd2' && <D2Bits />}
      {kind === 'd3' && <D3Bits />}
      {kind === 'd4' && <D4Bits />}
      {kind === 'd5' && <D5Bits />}
      {kind === 'd6' && <D6Bits />}
      {kind === 'd7' && <D7Bits />}
    </div>
  );
}

const CoverSceneBits = () => (<>
  <div className="sun" />
  <div className="sun-disc" />
  <div className="horizon" />
  <div className="cliff-l" /><div className="cliff-r" />
  <div className="ocean" /><div className="shimmer" />
  <div className="bird b1" /><div className="bird b2" /><div className="bird b3" />
</>);
const D1Bits = () => (<>
  <div className="sky-fog" /><div className="sun" />
  <div className="cable" />
  <div className="tower-l" /><div className="tower-r" />
  <div className="deck" />
  <div className="water" />
  <div className="wave w1" /><div className="wave w2" /><div className="wave w3" />
  <div className="sealion" />
</>);
const D2Bits = () => (<>
  <div className="mist" /><div className="cliff" />
  <div className="trunk" /><div className="canopy" />
  <div className="water" /><div className="otter" />
</>);
const D3Bits = () => (<>
  <div className="sky" /><div className="mountain-bg" /><div className="mountain" />
  <div className="road" /><div className="road r2" />
  <div className="pillar-l" /><div className="pillar-r" />
  <div className="arch" /><div className="bridge-deck" />
  <div className="water" />
</>);
const D4Bits = () => (<>
  <div className="hill-bg" /><div className="hill" />
  <div className="castle">
    <div className="tower1" /><div className="main" /><div className="tower2" /><div className="dome" />
  </div>
  <div className="windmill" />
  <div className="ground" />
</>);
const D5Bits = () => (<>
  <div className="sun" />
  <div className="water" /><div className="shimmer" />
  <div className="palm p1"><div className="l2"/><div className="l3"/></div>
  <div className="palm p2"><div className="l2"/><div className="l3"/></div>
  <div className="palm p3"><div className="l2"/><div className="l3"/></div>
</>);
const D6Bits = () => (<>
  <div className="sun" />
  <div className="hills" />
  <div className="skyline">
    <div className="bldg b1" /><div className="bldg b2" /><div className="bldg b3" />
    <div className="bldg b4" /><div className="bldg b5" /><div className="bldg b6" />
  </div>
  <div className="palm p1" /><div className="palm p2" />
  <div className="ground" />
</>);
const D7Bits = () => (<>
  <div className="sky" />
  <div className="water" />
  <div className="wave w1" /><div className="wave w2" /><div className="wave w3" />
  <div className="beach" />
  <div className="piling p1" /><div className="piling p2" /><div className="piling p3" />
  <div className="piling p4" /><div className="piling p5" />
  <div className="pier" /><div className="pier-end" />
</>);

// ===== iPhone status bar =====
function StatusBar() {
  const [time, setTime] = useState(() => {
    const d = new Date();
    return d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: false });
  });
  useEffect(() => {
    const t = setInterval(() => {
      const d = new Date();
      setTime(d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: false }));
    }, 30000);
    return () => clearInterval(t);
  }, []);
  return (
    <div className="statusbar">
      <span>{time}</span>
      <span className="statusbar-glyphs">
        <svg width="16" height="10" viewBox="0 0 16 10" fill="none"><path d="M1 8h1v1H1zM3 6h1v3H3zM5 4h1v5H5zM7 2h1v7H7zM9 1h1v8H9zM11 1h1v8H11zM13 1h1v8H13z" fill="currentColor"/></svg>
        <svg width="14" height="10" viewBox="0 0 14 10" fill="none"><path d="M7 9.5c1 0 1.8-.8 1.8-1.8S8 5.9 7 5.9s-1.8.8-1.8 1.8.8 1.8 1.8 1.8z M13 3.3c-3.4-3-8.6-3-12 0l1.2 1.2c2.6-2.3 6.9-2.3 9.5 0L13 3.3zM10.5 5.7c-2-1.7-5-1.7-7 0l1.2 1.2c1.4-1.2 3.3-1.2 4.7 0l1.1-1.2z" fill="currentColor"/></svg>
        <svg width="24" height="11" viewBox="0 0 24 11" fill="none"><rect x="0.5" y="0.5" width="20" height="10" rx="2.5" stroke="currentColor" fill="none"/><rect x="2" y="2" width="17" height="7" rx="1" fill="currentColor"/><rect x="21" y="3.5" width="2" height="4" rx="0.5" fill="currentColor"/></svg>
      </span>
    </div>
  );
}

// ===== Day navigation pills =====
function DayNav({ activeDay, onNav }) {
  const navRef = useRef();
  useEffect(() => {
    if (!navRef.current) return;
    const navEl = navRef.current;
    const active = navEl.querySelector('.day-pill.active');
    if (!active) return;
    // IMPORTANT: We deliberately set navEl.scrollLeft directly (NOT scrollIntoView).
    // scrollIntoView walks up ALL scroll ancestors and can issue a no-op scrollTo on
    // the parent .app, which cancels any in-flight smooth vertical scroll triggered
    // by clicking a day pill.
    const pillCenter = active.offsetLeft + active.offsetWidth / 2;
    const navCenter = navEl.clientWidth / 2;
    const targetLeft = pillCenter - navCenter;
    if (Math.abs(navEl.scrollLeft - targetLeft) > 4) {
      navEl.scrollLeft = targetLeft;
    }
  }, [activeDay]);

  return (
    <nav className="day-nav" ref={navRef}>
      <button className={`day-pill ${activeDay === 'cover' ? 'active' : ''}`} onClick={() => onNav('cover')}>
        <span className="pill-num">◈</span> Overview
      </button>
      {TRIP.days.map(d => (
        <button
          key={d.n}
          className={`day-pill ${activeDay === `day-${d.n}` ? 'active' : ''}`}
          onClick={() => onNav(`day-${d.n}`)}
        >
          <span className="pill-num">D{d.n}</span> {d.title.split(/[→]/)[0].trim().slice(0, 18)}
        </button>
      ))}
    </nav>
  );
}

// ===== Cover =====
function Cover() {
  const routeMilesText = useRouteMilesText();
  return (
    <div className="cover" id="cover">
      <span className="section-anchor" id="anchor-cover" aria-hidden="true" />
      <div className="cover-art">
        <Scene kind="cover" label="PACIFIC COAST HIGHWAY" temp="May ’26" />
      </div>
      <div className="cover-sub">{TRIP.dates}</div>
      <h1 className="cover-title">
        {TRIP.title}
      </h1>
      <p style={{color:'var(--ink-soft)', fontSize:'12px', lineHeight:1.4, marginTop:'8px', marginBottom:'2px', fontFamily:'var(--font-mono)', letterSpacing:'0.03em'}}>
        Made with <span className="heart">♥</span> for Ollie & Swilgs
      </p>
      <p style={{color:'var(--ink-soft)', fontSize:'14px', lineHeight:1.5, marginTop:'14px', maxWidth:'32ch'}}>
        Seven days down Highway 1 — from the sea lions at Pier 39 to a final lunch by the Manhattan Beach Pier.
      </p>
      <div className="cover-meta">
        <div className="cover-meta-item">
          <div className="val">7</div>
          <div className="lbl">Days</div>
        </div>
        <div className="cover-meta-item">
          <div className="val">{routeMilesText}</div>
          <div className="lbl">On the PCH</div>
        </div>
        <div className="cover-meta-item">
          <div className="val">6</div>
          <div className="lbl">Stops</div>
        </div>
      </div>
      {window.WeatherStrip && <window.WeatherStrip />}
    </div>
  );
}

// ===== Flag chip =====
function Flag({ kind }) {
  if (kind === 'reservation') return <span className="chip warn">⚑ Reservation</span>;
  if (kind === 'cash-only') return <span className="chip">$ Cash only</span>;
  if (kind === 'departure') return <span className="chip ocean">✈ Departure</span>;
  return null;
}

// ===== Contact links =====
function Links({ name }) {
  const p = window.placeFor ? window.placeFor(name) : null;
  const mapHref = window.mapsLinkFor(name, p && p.addr);
  return (
    <div className="links" onClick={(e) => e.stopPropagation()}>
      {p && p.phone && (
        <a className="link-btn phone" href={`tel:${p.phone.replace(/[^+0-9]/g,'')}`}>
          <span className="lb-ico">☏</span> Call
        </a>
      )}
      {p && p.web && (
        <a className="link-btn web" href={p.web} target="_blank" rel="noopener">
          <span className="lb-ico">↗</span> Website
        </a>
      )}
      <a className="link-btn map" href={mapHref} target="_blank" rel="noopener">
        <span className="lb-ico">◉</span> Maps
      </a>
    </div>
  );
}

// ===== Custom-meal storage (user-added lunch/dinner options) =====
const CUSTOM_MEALS_KEY = 'roadtrip-custom-meals-v1';

function readCustomMeals() {
  try { return JSON.parse(localStorage.getItem(CUSTOM_MEALS_KEY) || '{}'); }
  catch { return {}; }
}
function writeCustomMeals(data) {
  try { localStorage.setItem(CUSTOM_MEALS_KEY, JSON.stringify(data)); } catch {}
  window.dispatchEvent(new CustomEvent('custom-meals-updated', { detail: data }));
}
function useCustomMealsForKey(key) {
  const [data, setData] = useState(() => readCustomMeals());
  useEffect(() => {
    const h = (e) => setData(e.detail || readCustomMeals());
    window.addEventListener('custom-meals-updated', h);
    return () => window.removeEventListener('custom-meals-updated', h);
  }, []);
  return data[key] || [];
}
function addCustomMeal(key, item) {
  const all = readCustomMeals();
  all[key] = [...(all[key] || []), item];
  writeCustomMeals(all);
}
function removeCustomMeal(key, idx) {
  const all = readCustomMeals();
  if (!all[key]) return;
  all[key] = all[key].filter((_, i) => i !== idx);
  writeCustomMeals(all);
}

// ===== "+ Add another option" UI inside a meal card =====
function CustomMealAdder({ dayN, kind, canEdit }) {
  const key = `${dayN}-${kind}`;
  const customs = useCustomMealsForKey(key);
  const [open, setOpen] = useState(false);
  const [name, setName] = useState('');
  const [desc, setDesc] = useState('');
  const [reservation, setReservation] = useState(false);
  const [usePlaces, setUsePlaces] = useState(false);
  const inputRef = useRef();
  const placesRef = useRef();

  // If Google Maps + Places library is loaded, attach an Autocomplete to the name field
  useEffect(() => {
    if (!open) return;
    if (!window.google || !window.google.maps || !window.google.maps.places) {
      setUsePlaces(false);
      return;
    }
    if (placesRef.current) return; // already attached
    try {
      const ac = new window.google.maps.places.Autocomplete(inputRef.current, {
        types: ['restaurant', 'cafe', 'food', 'bakery'],
        fields: ['name', 'formatted_address', 'rating', 'website', 'formatted_phone_number'],
      });
      ac.addListener('place_changed', () => {
        const p = ac.getPlace();
        if (p && p.name) {
          setName(p.name);
          if (!desc && p.formatted_address) setDesc(p.formatted_address);
          // Update PLACES so the "Maps" / "Call" / "Web" links work for this entry
          if (window.PLACES && p.name) {
            window.PLACES[p.name] = {
              phone: p.formatted_phone_number || undefined,
              web: p.website || undefined,
              addr: p.formatted_address || undefined,
            };
          }
        }
      });
      placesRef.current = ac;
      setUsePlaces(true);
    } catch (e) {
      setUsePlaces(false);
    }
  }, [open]);

  const submit = (e) => {
    e.preventDefault();
    if (!name.trim()) return;
    addCustomMeal(key, {
      name: name.trim(),
      desc: desc.trim() || 'Added by you.',
      flag: reservation ? 'reservation' : null,
      custom: true,
    });
    setName('');
    setDesc('');
    setReservation(false);
    setOpen(false);
  };

  return (
    <div className="custom-meals">
      {customs.length > 0 && (
        <div className="options" style={{marginTop: '14px'}}>
          {customs.map((o, i) => (
            <div className="option" key={`custom-${i}`}>
              <div className="o-idx" style={{color: 'var(--ocean-600)'}}>+</div>
              <div className="o-body">
                <div className="o-name">
                  {o.name}
                  {canEdit && (
                    <button
                      className="custom-del"
                      onClick={(e) => { e.preventDefault(); removeCustomMeal(key, i); }}
                      aria-label="Remove"
                      title="Remove"
                    >×</button>
                  )}
                </div>
                <div className="o-desc">{o.desc}</div>
                <div className="o-meta">
                  <span className="chip ocean" style={{fontSize: '10px'}}>added</span>
                  {o.flag && <Flag kind={o.flag} />}
                </div>
                <Links name={o.name} />
              </div>
            </div>
          ))}
        </div>
      )}

      {canEdit && !open ? (
        <button
          className="add-option-btn"
          onClick={(e) => { e.preventDefault(); setOpen(true); }}
        >
          + Add another option
        </button>
      ) : canEdit ? (
        <form className="add-option-form" onSubmit={submit}>
          <div className="aof-row">
            <label>Restaurant name</label>
            <input
              ref={inputRef}
              type="text"
              value={name}
              onChange={e => setName(e.target.value)}
              placeholder={usePlaces ? "Start typing — search powered by Google" : "e.g. Cliffside Café"}
              autoFocus
            />
          </div>
          <div className="aof-row">
            <label>Description / area</label>
            <textarea
              value={desc}
              onChange={e => setDesc(e.target.value)}
              placeholder="Brief note about why or where (e.g. ‘Castro St, Mountain View — great patio’)"
              rows={2}
            />
          </div>
          <label className="aof-check">
            <input
              type="checkbox"
              checked={reservation}
              onChange={e => setReservation(e.target.checked)}
            />
            Needs a reservation
          </label>
          {!usePlaces && (
            <div className="aof-hint">
              💡 Add a Google Maps API key to <code>index.html</code> for live Places search.
            </div>
          )}
          <div className="aof-actions">
            <button type="button" className="aof-btn" onClick={() => setOpen(false)}>Cancel</button>
            <button type="submit" className="aof-btn primary">Add</button>
          </div>
        </form>
      ) : null}
      {!canEdit && customs.length === 0 && (
        <div style={{ marginTop: '10px', fontSize: '11px', color: 'var(--ink-muted)' }}>
          Editing is disabled in view-only mode.
        </div>
      )}
    </div>
  );
}

// ===== Meal card (expandable) =====
function MealCard({ kind, meta, options, defaultOpen = false, dayN, canEditMeals }) {
  const labels = { lunch: 'Lunch', dinner: 'Dinner' };
  const initials = { lunch: 'L', dinner: 'D' };
  const customs = useCustomMealsForKey(`${dayN}-${kind}`);
  const totalCount = options.length + customs.length;
  return (
    <details className="card" open={defaultOpen}>
      <summary>
        <div className={`meal-icon ${kind}`}>{initials[kind]}</div>
        <div className="meal-head">
          <div className="meal-title">{labels[kind]} · {totalCount} option{totalCount === 1 ? '' : 's'}</div>
          <div className="meal-meta"><span>{meta}</span></div>
        </div>
      </summary>
      <div className="meal-body">
        <div className="options">
          {options.map((o, i) => (
            <div className="option" key={i}>
              <div className="o-idx">{String.fromCharCode(97 + i)}.</div>
              <div className="o-body">
                <div className="o-name">{o.name}</div>
                <div className="o-desc">{o.desc}</div>
                <div className="o-meta">
                  {o.rating && <span className="star">{o.rating}</span>}
                  {o.flag && <Flag kind={o.flag} />}
                </div>
                <Links name={o.name} />
              </div>
            </div>
          ))}
        </div>
        <CustomMealAdder dayN={dayN} kind={kind} canEdit={!!canEditMeals} />
      </div>
    </details>
  );
}

// ===== Attractions card (expandable) =====
function AttractionsCard({ list, dayN }) {
  return (
    <details className="card">
      <summary>
        <div className="meal-icon attr">✦</div>
        <div className="meal-head">
          <div className="meal-title">Attractions · {list.length}</div>
          <div className="meal-meta"><span>Top-rated picks</span></div>
        </div>
      </summary>
      <div className="meal-body">
        <div className="options">
          {list.map((o, i) => (
            <div className="option" key={i}>
              <div className="o-idx">{i + 1}.</div>
              <div className="o-body">
                <div className="o-name">{o.name}</div>
                <div className="o-desc">{o.desc}</div>
                <div className="o-meta">
                  {o.rating && <span className="star">{o.rating}</span>}
                  {o.flag && <Flag kind={o.flag} />}
                </div>
                <Links name={o.name} />
              </div>
            </div>
          ))}
        </div>
      </div>
    </details>
  );
}

// ===== Hotel card =====
function HotelCard({ h, dayN }) {
  const hotels = INFO.hotels || [];
  const byDay = INFO.byDay || {};
  const mappedHotelId = byDay[dayN] && byDay[dayN].hotelId;
  const booked = (mappedHotelId && hotels.find((x) => x.id === mappedHotelId))
    || hotels.find((x) => (x.name || "").toLowerCase() === (h.name || "").toLowerCase())
    || null;

  const roomsText = booked ? `${booked.rooms || 1}` : "1";
  const roomsLabel = `${roomsText} room${roomsText === "1" ? "" : "s"}`;
  const roomTypeText = booked ? (booked.roomType || "") : "";
  const bedSummary = booked ? (booked.bedType || h.beds) : h.beds;
  const confirmationText = booked && booked.conf ? booked.conf : "";

  return (
    <div className="hotel-card">
      <div style={{display:'flex', gap:'14px', alignItems:'flex-start'}}>
        <div className="h-illust">
          <div className="roof" /><div className="body" /><div className="window" />
        </div>
        <div style={{flex:1, minWidth:0}}>
          <div className="h-eyebrow">Tonight's Stay</div>
          <div className="h-name">{h.name}</div>
          <div className="h-location">{h.loc}</div>
        </div>
      </div>
      <div style={{
        marginTop: '10px',
        paddingTop: '10px',
        borderTop: '1px dashed rgba(53, 49, 42, 0.24)',
        display: 'grid',
        gap: '7px',
      }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', gap: '12px', alignItems: 'baseline' }}>
          <span style={{ fontFamily: 'var(--font-mono)', fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--ink-muted)' }}>Rooms</span>
          <span style={{ fontSize: '13px', color: 'var(--ink)', textAlign: 'right', fontWeight: 600 }}>{roomsLabel}</span>
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', gap: '12px', alignItems: 'baseline' }}>
          <span style={{ fontFamily: 'var(--font-mono)', fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--ink-muted)' }}>Room type</span>
          <span style={{ fontSize: '13px', color: 'var(--ink)', textAlign: 'right', fontWeight: 500 }}>{roomTypeText || "See Info tab"}</span>
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', gap: '12px', alignItems: 'baseline' }}>
          <span style={{ fontFamily: 'var(--font-mono)', fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--ink-muted)' }}>Beds</span>
          <span style={{ fontSize: '13px', color: 'var(--ink)', textAlign: 'right', fontWeight: 500 }}>{bedSummary}</span>
        </div>
        {confirmationText && (
          <div style={{ display: 'flex', justifyContent: 'space-between', gap: '12px', alignItems: 'baseline' }}>
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--ink-muted)' }}>Confirmation</span>
            <span style={{ fontSize: '12px', color: 'var(--terra-700)', textAlign: 'right', fontFamily: 'var(--font-mono)' }}>{confirmationText}</span>
          </div>
        )}
      </div>
      <Links name={h.name} />
    </div>
  );
}

// ===== Drive callout =====
function DriveCallout({ d, liveMetric }) {
  if (!d) return null;
  const from = liveMetric && liveMetric.from ? liveMetric.from : d.from;
  const to = liveMetric && liveMetric.to ? liveMetric.to : d.to;
  const via = liveMetric && liveMetric.via ? liveMetric.via : d.via;
  const time = liveMetric && liveMetric.durationText ? liveMetric.durationText : d.time;
  const dist = liveMetric && liveMetric.distanceText ? liveMetric.distanceText : d.dist;
  return (
    <div className="drive">
      <div style={{width:'28px',height:'28px',borderRadius:'8px',background:'var(--terra-500)',color:'white',display:'grid',placeItems:'center',flexShrink:0,fontSize:'14px'}}>
        ➔
      </div>
      <div className="drive-route">
        <div className="from-to">{from} → {to}</div>
        <div className="via">
          {via}
          {liveMetric ? " · live from Map" : ""}
        </div>
      </div>
      <div className="drive-stat">
        <div className="time">{time}</div>
        <div className="dist">{dist}</div>
      </div>
    </div>
  );
}

// ===== Day section =====
function DaySection({ d, liveMetric, canEditMeals }) {
  return (
    <section className="day" id={`day-${d.n}`} data-screen-label={`Day ${d.n}`}>
      <span className="section-anchor" id={`anchor-day-${d.n}`} aria-hidden="true" />
      <Scene
        kind={d.scene}
        label={`${d.dayOfWeek.toUpperCase()} · ${d.date.toUpperCase()}`}
        temp={d.temp}
        dayN={d.n}
      />
      <div className="day-head">
        <div className="d-num">0{d.n}</div>
        <div className="d-date">{d.date}<br/>{d.dayOfWeek}</div>
      </div>
      <h2 className="day-title">{d.title}</h2>
      <p className="day-subtitle">{d.subtitle}</p>

      <DriveCallout d={d.drive} liveMetric={liveMetric} />

      <div className="sec-label"><span className="label">Eat</span><span className="bar" /></div>
      {d.lunch && <MealCard kind="lunch" meta={d.lunch.meta} options={d.lunch.options} dayN={d.n} canEditMeals={canEditMeals} />}
      {d.dinner && <MealCard kind="dinner" meta={d.dinner.meta} options={d.dinner.options} dayN={d.n} canEditMeals={canEditMeals} />}

      <div className="sec-label"><span className="label">Do</span><span className="bar" /></div>
      <AttractionsCard list={d.attractions} dayN={d.n} />

      {d.hotel && <>
        <div className="sec-label"><span className="label">Sleep</span><span className="bar" /></div>
        <HotelCard h={d.hotel} dayN={d.n} />
      </>}
    </section>
  );
}

// ===== Scroll spy hook — watches the scrollable ancestor (.app on desktop, window on mobile)
// Accepts a `suppressedRef` — when its current value is true, the spy stops updating
// `active`. We set it to true during click-driven nav so the smooth scroll runs without
// scrollSpy → re-render → DayNav useEffect → scrollIntoView side-effects interrupting it.
function useScrollSpy(ids, suppressedRef) {
  const [active, setActive] = useState(ids[0]);
  useEffect(() => {
    const getScroller = () => {
      const main = document.querySelector('.app-main');
      if (main && main.scrollHeight > main.clientHeight + 2) return main;
      const app = document.querySelector('.app');
      if (app && app.scrollHeight > app.clientHeight + 2) return app;
      return window;
    };
    const handler = () => {
      if (suppressedRef && suppressedRef.current) return;
      const scroller = getScroller();
      const isWin = scroller === window;
      const scrollTop = isWin ? window.scrollY : scroller.scrollTop;
      const viewportH = isWin ? window.innerHeight : scroller.clientHeight;
      const probe = scrollTop + viewportH * 0.35;
      const scrollerTop = isWin ? 0 : scroller.getBoundingClientRect().top;
      let current = ids[0];
      for (const id of ids) {
        const el = document.getElementById(id);
        if (!el) continue;
        const top = isWin
          ? (el.getBoundingClientRect().top + window.scrollY)
          : (el.getBoundingClientRect().top - scrollerTop + scrollTop);
        if (top <= probe) current = id;
      }
      setActive(current);
    };
    const scroller = getScroller();
    scroller.addEventListener('scroll', handler, { passive: true });
    window.addEventListener('resize', handler);
    handler();
    return () => {
      scroller.removeEventListener('scroll', handler);
      window.removeEventListener('resize', handler);
    };
  }, [ids.join('|')]);
  return [active, setActive];
}

// ===== Itinerary tab (the original main view, sans the map) =====
function Itinerary() {
  const dayIds = useMemo(() => ['cover', ...TRIP.days.map(d => `day-${d.n}`)], []);
  const routeMetricsByDay = useRouteDayMetrics();
  const auth = useRoadtripAuthState();
  // Ref-flag that suppresses scrollSpy updates while a programmatic scroll is in flight.
  const isNavigatingRef = useRef(false);
  const navTimeoutRef = useRef(null);
  const [active, setActive] = useScrollSpy(dayIds, isNavigatingRef);

  const nav = (id) => {
    // Use scrollTo with calculated offset (NOT scrollIntoView).
    // scrollIntoView walks up every scroll ancestor and that's been racing with React's
    // re-render of DayNav, requiring a double-click. scrollTo on a single explicit
    // scroller is unambiguous and lands on the first try.
    const el = document.getElementById(id);
    if (!el) return;
    const main = document.querySelector('.app-main');
    const app = document.querySelector('.app');
    const scroller = (main && main.scrollHeight > main.clientHeight + 2)
      ? main
      : ((app && app.scrollHeight > app.clientHeight + 2) ? app : window);
    const isWin = scroller === window;
    const headerEl = document.querySelector('.day-nav');
    const HEADER_HEIGHT = headerEl ? Math.round(headerEl.getBoundingClientRect().height) : 72;
    const GAP = 8;            // small breathing room below header

    let target;
    if (isWin) {
      target = el.getBoundingClientRect().top + window.scrollY - HEADER_HEIGHT - GAP;
    } else {
      const scrollerTop = scroller.getBoundingClientRect().top;
      target = (el.getBoundingClientRect().top - scrollerTop + scroller.scrollTop) - HEADER_HEIGHT - GAP;
    }

    setActive(id);
    isNavigatingRef.current = true;
    if (navTimeoutRef.current) clearTimeout(navTimeoutRef.current);

    scroller.scrollTo({ top: Math.max(0, target), behavior: 'smooth' });

    navTimeoutRef.current = setTimeout(() => {
      isNavigatingRef.current = false;
    }, 800);
  };

  return (
    <>
      <DayNav activeDay={active} onNav={nav} />
      <Cover />
      {TRIP.days.map(d => <DaySection key={d.n} d={d} liveMetric={routeMetricsByDay[d.n]} canEditMeals={!!auth.user} />)}
    </>
  );
}

// ===== Shared page header (used by Map / Budget / Tickets / Info tabs) =====
function PageHeader({ title, sub }) {
  return (
    <header className="page-header">
      <div className="ph-h-title">{title}</div>
      {sub && <div className="ph-h-sub">{sub}</div>}
    </header>
  );
}
window.PageHeader = PageHeader;

// ===== Map tab — just the existing MapSection wrapped in a page =====
function MapPage() {
  return (
    <>
      <PageHeader title="The Route" />
      <section id="map-page" data-screen-label="Map">
        {window.MapSection ? <window.MapSection /> : null}
      </section>
    </>
  );
}

function BudgetLockedPage() {
  return (
    <>
      <PageHeader title="Budget" sub="Private section" />
      <section className="day" style={{ paddingTop: "20px" }}>
        <div className="sec-label"><span className="label">Locked</span><span className="bar" /></div>
        <div className="card" style={{ padding: "16px" }}>
          <div style={{ fontFamily: "var(--font-display)", fontStyle: "italic", fontSize: "28px", marginBottom: "8px" }}>
            Budget is private
          </div>
          <p style={{ margin: 0, color: "var(--ink-soft)", lineHeight: 1.5 }}>
            Unlock access from the control at the top of the Info tab to view and edit trip budget numbers.
          </p>
        </div>
      </section>
    </>
  );
}

// ===== Bottom tab bar =====
function TabBar({ active, onChange }) {
  const tabs = [
    { id: 'itinerary', label: 'Trip',    ico: '◈' },
    { id: 'map',       label: 'Map',     ico: '◉' },
    { id: 'tickets',   label: 'Tickets', ico: '⚑' },
    { id: 'budget',    label: 'Budget',  ico: '$' },
    { id: 'info',      label: 'Info',    ico: 'ⓘ' },
  ];
  return (
    <nav className="tabbar">
      {tabs.map(t => (
        <button
          key={t.id}
          className={`tab-btn ${active === t.id ? 'active' : ''}`}
          onClick={() => onChange(t.id)}
        >
          <span className="tab-ico">{t.ico}</span>
          <span className="tab-lbl">{t.label}</span>
        </button>
      ))}
    </nav>
  );
}

// ===== App shell =====
function App() {
  const [tab, setTab] = useState(() => {
    try { return localStorage.getItem('roadtrip-tab') || 'itinerary'; } catch { return 'itinerary'; }
  });
  const auth = useRoadtripAuthState();

  // Persist + scroll to top whenever tab changes
  useEffect(() => {
    try { localStorage.setItem('roadtrip-tab', tab); } catch {}
    const main = document.querySelector('.app-main');
    if (main) main.scrollTo({ top: 0, behavior: 'auto' });
    const app = document.querySelector('.app');
    if (app) app.scrollTo({ top: 0, behavior: 'auto' });
    window.scrollTo({ top: 0, behavior: 'auto' });
  }, [tab]);

  return (
    <div className={`app tab-${tab}`}>
      <main className="app-main">
        {tab === 'itinerary' && <Itinerary />}
        {tab === 'map'       && <MapPage />}
        {tab === 'budget'    && (
          auth.user
            ? (window.BudgetPage ? <window.BudgetPage /> : null)
            : <BudgetLockedPage />
        )}
        {tab === 'tickets'   && window.TicketsPage && <window.TicketsPage />}
        {tab === 'info'      && window.InfoPage    && <window.InfoPage />}
      </main>
      <TabBar active={tab} onChange={setTab} />
    </div>
  );
}

// Expose
window.App = App;
