
// RMTCTRL Web App — Desktop screens

// ── API key placeholders ──────────────────────────────────────────────────────
// Replace '' with your token to enable the feature. Leave blank to use placeholder UI.
const MAPBOX_TOKEN      = ''; // mapbox.com → Tokens → Create token (public)
const AVIATIONSTACK_KEY = ''; // aviationstack.com → free tier (100 req/month)
const GOOGLE_PLACES_KEY = ''; // console.cloud.google.com → Places API
const CLAUDE_API_KEY    = ''; // console.anthropic.com → API Keys

// ── Mapbox workspace map component ───────────────────────────────────────────
// Renders a real Mapbox GL map when MAPBOX_TOKEN is set; shows a styled
// placeholder with setup instructions otherwise.
function MapboxWorkspaceMap({ ws, height = 360 }) {
  const mapContainerRef = React.useRef(null);
  const mapRef = React.useRef(null);

  // Approximate lat/lon per workspace name (Lisbon coordinates)
  const WS_COORDS = {
    'Hub Lisbon':      [-9.1421, 38.7083],
    'Dear Breakfast':  [-9.1450, 38.7105],
    'Selina CoWork':   [-9.1370, 38.7150],
    'Lisbon Library':  [-9.1400, 38.7120],
    'LX Factory':      [-9.1750, 38.7040],
    'Altis Grand Lobby': [-9.1490, 38.7200],
  };
  const coords = WS_COORDS[ws.name] || [-9.1421, 38.7083];

  React.useEffect(() => {
    if (!MAPBOX_TOKEN || !mapContainerRef.current) return;
    // Dynamically load Mapbox GL JS + CSS if not already loaded
    function initMap() {
      if (mapRef.current) return;
      const mapboxgl = window.mapboxgl;
      mapboxgl.accessToken = MAPBOX_TOKEN;
      const map = new mapboxgl.Map({
        container: mapContainerRef.current,
        style: 'mapbox://styles/mapbox/dark-v11',
        center: coords,
        zoom: 15,
      });
      new mapboxgl.Marker({ color: TOKENS.color.accent })
        .setLngLat(coords)
        .addTo(map);
      mapRef.current = map;
    }

    if (window.mapboxgl) {
      initMap();
    } else {
      // Load CSS
      if (!document.querySelector('#mapbox-css')) {
        const link = document.createElement('link');
        link.id = 'mapbox-css';
        link.rel = 'stylesheet';
        link.href = 'https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css';
        document.head.appendChild(link);
      }
      // Load JS
      if (!document.querySelector('#mapbox-js')) {
        const script = document.createElement('script');
        script.id = 'mapbox-js';
        script.src = 'https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js';
        script.onload = initMap;
        document.head.appendChild(script);
      }
    }

    return () => {
      if (mapRef.current) { mapRef.current.remove(); mapRef.current = null; }
    };
  }, [ws.name]);

  if (!MAPBOX_TOKEN) {
    // Styled placeholder — shows the grid + a setup prompt
    return (
      <div style={{ height, background: 'linear-gradient(160deg,#0D1117 0%,#111827 60%,#0A0F1A 100%)', borderRadius: 12, position: 'relative', overflow: 'hidden', border: '1px solid #26262E', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12 }}>
        <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: 0.06 }}>
          <defs><pattern id="ws-grid" width="40" height="40" patternUnits="userSpaceOnUse"><path d="M 40 0 L 0 0 0 40" fill="none" stroke="#F5F5F7" strokeWidth="0.5"/></pattern></defs>
          <rect width="100%" height="100%" fill="url(#ws-grid)"/>
        </svg>
        <div style={{ position: 'relative', zIndex: 1, textAlign: 'center' }}>
          <div style={{ fontSize: 28, marginBottom: 8 }}>🗺️</div>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 4 }}>MAP UNAVAILABLE</div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>Add MAPBOX_TOKEN in webapp-screens.jsx</div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textTertiary, marginTop: 2 }}>mapbox.com → Tokens → Create public token</div>
        </div>
        <div style={{ position: 'absolute', bottom: 12, right: 12, background: 'rgba(10,10,11,0.8)', borderRadius: 8, padding: '6px 12px', fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>
          {ws.address}
        </div>
      </div>
    );
  }

  return (
    <div ref={mapContainerRef} style={{ height, borderRadius: 12, overflow: 'hidden', border: '1px solid #26262E' }} />
  );
}

// ── Where Next? Modal ─────────────────────────────────────────────────────────
// AI-powered destination suggester. With CLAUDE_API_KEY set: calls Claude API
// with the user's trip history + preferences. Without key: shows curated static
// suggestions ranked by popularity, cost, and visa-friendliness.
function WhereNextModal({ onClose }) {
  const [loading, setLoading] = React.useState(false);
  const [aiSuggestions, setAiSuggestions] = React.useState(null);
  const [selectedDest, setSelectedDest] = React.useState(null);

  // Curated static suggestions — well-rounded nomad destinations
  const staticSuggestions = [
    {
      city: 'Chiang Mai',
      country: 'Thailand',
      flag: '🇹🇭',
      tagline: 'Southeast Asia\'s nomad capital',
      why: 'Ultra-low cost of living, fast fibre in every café, year-round warm weather. Digital nomad community of thousands.',
      cost: 'Very Low',
      costColor: TOKENS.color.accent,
      visa: '60 days on arrival',
      internet: '90+ Mbps avg',
      bestMonths: 'Nov – Feb',
      tags: ['Budget-friendly', 'Huge community', 'Great food'],
    },
    {
      city: 'Medellín',
      country: 'Colombia',
      flag: '🇨🇴',
      tagline: 'Latin America\'s tech hub',
      why: 'Spring weather all year (spring city at 1,500m), booming startup scene, excellent co-working infrastructure and great nightlife.',
      cost: 'Low',
      costColor: TOKENS.color.accent,
      visa: '90 days on arrival',
      internet: '60+ Mbps avg',
      bestMonths: 'Year-round',
      tags: ['Eternal spring', 'Tech scene', 'Monthly rate deals'],
    },
    {
      city: 'Tbilisi',
      country: 'Georgia',
      flag: '🇬🇪',
      tagline: 'Europe\'s most underrated gem',
      why: 'European culture, Asian prices. 365-day visa-free for most passports, stunning architecture, wine country on the doorstep.',
      cost: 'Low',
      costColor: TOKENS.color.accent,
      visa: '365 days visa-free',
      internet: '70+ Mbps avg',
      bestMonths: 'Apr – Jun, Sep – Nov',
      tags: ['Visa-friendly', 'Wine country', 'Low cost'],
    },
    {
      city: 'Mexico City',
      country: 'Mexico',
      flag: '🇲🇽',
      tagline: 'North America\'s digital nomad hotspot',
      why: 'Colima-style coffee culture, world-class food, US timezone overlap, huge coworking scene in Roma Norte / Condesa.',
      cost: 'Medium',
      costColor: TOKENS.color.info,
      visa: '180 days on arrival',
      internet: '80+ Mbps avg',
      bestMonths: 'Oct – Apr',
      tags: ['US timezone', 'Food scene', 'Creative hub'],
    },
  ];

  const suggestions = aiSuggestions || staticSuggestions;

  const handleAiRefine = async () => {
    if (!CLAUDE_API_KEY) return;
    setLoading(true);
    try {
      // Call Claude API with trip history context
      const resp = await fetch('https://api.anthropic.com/v1/messages', {
        method: 'POST',
        headers: {
          'x-api-key': CLAUDE_API_KEY,
          'anthropic-version': '2023-06-01',
          'content-type': 'application/json',
        },
        body: JSON.stringify({
          model: 'claude-haiku-4-5-20251001',
          max_tokens: 1024,
          messages: [{
            role: 'user',
            content: `You are a travel advisor for digital nomads. The user has recently visited: Lisbon (Portugal), Tokyo (Japan), Barcelona (Spain). Suggest 4 new destinations they\'d love, with brief reasons. Respond as JSON array: [{city, country, flag, tagline, why, cost, visa, internet, bestMonths, tags}]`,
          }],
        }),
      });
      const data = await resp.json();
      const text = data.content?.[0]?.text || '';
      const json = JSON.parse(text.match(/\[[\s\S]*\]/)?.[0] || '[]');
      if (json.length) setAiSuggestions(json.map(s => ({ ...s, costColor: TOKENS.color.accent })));
    } catch (e) {
      // silently fall back to static
    } finally {
      setLoading(false);
    }
  };

  const sel = selectedDest !== null ? suggestions[selectedDest] : null;

  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.7)', zIndex: 200, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}
      onClick={e => { if (e.target === e.currentTarget) onClose(); }}
    >
      <div style={{ background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 16, width: '100%', maxWidth: 620, maxHeight: '85vh', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
        {/* Header */}
        <div style={{ padding: '20px 24px', borderBottom: '1px solid #26262E', display: 'flex', alignItems: 'center', gap: 12 }}>
          <div style={{ width: 32, height: 32, borderRadius: 8, background: 'rgba(0,255,148,0.12)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16 }}>✦</div>
          <div style={{ flex: 1 }}>
            <h2 style={{ fontFamily: "'Inter',sans-serif", fontSize: 16, fontWeight: 700, color: TOKENS.color.textPrimary, margin: 0 }}>Where Next?</h2>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>Destinations matched to your travel style</div>
          </div>
          {CLAUDE_API_KEY && (
            <button onClick={handleAiRefine} disabled={loading} style={{ height: 32, padding: '0 12px', borderRadius: 8, border: `1px solid ${TOKENS.color.accent}`, background: 'rgba(0,255,148,0.08)', cursor: loading ? 'default' : 'pointer', fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.accent, letterSpacing: '0.06em' }}>
              {loading ? 'THINKING…' : 'AI REFRESH'}
            </button>
          )}
          <button onClick={onClose} style={{ width: 32, height: 32, borderRadius: 8, border: '1px solid #26262E', background: 'transparent', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', color: TOKENS.color.textSecondary, fontFamily: 'Inter', fontSize: 16 }}>✕</button>
        </div>

        <div style={{ flex: 1, overflowY: 'auto', padding: 24 }}>
          {!CLAUDE_API_KEY && (
            <div style={{ background: 'rgba(0,255,148,0.05)', border: '1px solid rgba(0,255,148,0.15)', borderRadius: 10, padding: '10px 14px', marginBottom: 20, fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>
              ✦ Add <code style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, color: TOKENS.color.accent }}>CLAUDE_API_KEY</code> in webapp-screens.jsx for AI-personalised suggestions based on your trip history.
            </div>
          )}

          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            {suggestions.map((dest, i) => (
              <button
                key={i}
                onClick={() => setSelectedDest(selectedDest === i ? null : i)}
                style={{ width: '100%', padding: '14px 16px', background: selectedDest === i ? 'rgba(0,255,148,0.06)' : TOKENS.color.surface2, border: `1px solid ${selectedDest === i ? 'rgba(0,255,148,0.25)' : '#26262E'}`, borderRadius: 12, cursor: 'pointer', textAlign: 'left', transition: `all ${TOKENS.anim.fast}` }}
              >
                <div style={{ display: 'flex', alignItems: 'flex-start', gap: 14 }}>
                  <span style={{ fontSize: 28, lineHeight: 1 }}>{dest.flag}</span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 2 }}>
                      <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 15, fontWeight: 700, color: TOKENS.color.textPrimary }}>{dest.city}</span>
                      <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>{dest.country}</span>
                    </div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textTertiary, marginBottom: 6 }}>{dest.tagline}</div>
                    <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                      {(dest.tags || []).map(tag => (
                        <span key={tag} style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 9, fontWeight: 600, color: TOKENS.color.textTertiary, background: TOKENS.color.surface, border: '1px solid #2A2A32', borderRadius: 999, padding: '2px 7px', letterSpacing: '0.04em' }}>{tag}</span>
                      ))}
                    </div>
                  </div>
                  <div style={{ textAlign: 'right', flexShrink: 0 }}>
                    <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: dest.costColor || TOKENS.color.accent }}>{dest.cost}</div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 10, color: TOKENS.color.textTertiary }}>{dest.visa}</div>
                  </div>
                </div>

                {selectedDest === i && (
                  <div style={{ marginTop: 14, paddingTop: 14, borderTop: '1px solid rgba(0,255,148,0.15)' }}>
                    <p style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary, lineHeight: 1.6, margin: '0 0 12px' }}>{dest.why}</p>
                    <div style={{ display: 'flex', gap: 8 }}>
                      {[
                        { label: 'BEST MONTHS', val: dest.bestMonths },
                        { label: 'INTERNET', val: dest.internet },
                        { label: 'VISA', val: dest.visa },
                      ].map(s => (
                        <div key={s.label} style={{ flex: 1, background: TOKENS.color.surface, borderRadius: 8, padding: '8px 10px', textAlign: 'center' }}>
                          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 9, color: TOKENS.color.textTertiary, letterSpacing: '0.06em', marginBottom: 3 }}>{s.label}</div>
                          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, fontWeight: 600, color: TOKENS.color.textSecondary }}>{s.val}</div>
                        </div>
                      ))}
                    </div>
                  </div>
                )}
              </button>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

// ── Web Dashboard ─────────────────────────────────────────────────────────────
function WebDashboard({ onNavigate, dashState, sidebarOpen = true }) {
  const today = [
    { pill: 'BOARDING', time: '14:05 → 21:30', icon: PlaneIcon, title: 'SYD → LIS · QF1', loc: 'Gate B12 · T1', color: TOKENS.color.accent },
    { pill: 'WORK BLOCK', time: '09:00 → 13:00', icon: LaptopIcon, title: 'Hub Lisbon · Chiado', loc: 'Desk #14 booked', color: TOKENS.color.info },
    { pill: 'ARRIVED', time: '21:30', icon: BedIcon, title: 'The Independente', loc: 'Lisbon, Portugal', color: TOKENS.color.accent },
  ];
  const workspaces = [
    { name: 'Hub Lisbon', type: 'Cowork', dist: '230m', wifi: '78 Mbps', noise: 'Quiet', price: '$$', saved: false },
    { name: 'Dear Breakfast', type: 'Cafe', dist: '480m', wifi: '34 Mbps', noise: 'Moderate', price: '$', saved: true },
    { name: 'Selina CoWork', type: 'Cowork', dist: '950m', wifi: '56 Mbps', noise: 'Lively', price: '$$', saved: false },
    { name: 'Lisbon Library', type: 'Library', dist: '720m', wifi: '22 Mbps', noise: 'Silent', price: 'Free', saved: false },
  ];
  const nextActionMap = {
    'pre-flight': { headline: 'Get to gate B12 in 1h 23m', sub: 'Terminal 1 · 4.2km from your hotel · QF1 to Lisbon', cta: 'Get directions' },
    'mid-trip': { headline: 'Landed in Lisbon · Day 3 of 14', sub: 'Find a workspace near your Airbnb in Chiado', cta: 'Find workspaces' },
    'no-trip': { headline: 'No upcoming trips', sub: 'Add a trip or explore workspaces nearby', cta: 'Add a trip' },
  };
  const na = nextActionMap[dashState] || nextActionMap['pre-flight'];
  const months = ['Apr 2025', 'May 2025', 'Jun 2025', 'Jul 2025', 'Aug 2025', 'Sep 2025'];
  const [selectedMonth, setSelectedMonth] = React.useState('May 2025');
  const [monthOpen, setMonthOpen] = React.useState(false);
  const [hoveredWsIdx, setHoveredWsIdx] = React.useState(null);
  const [whereNextOpen, setWhereNextOpen] = React.useState(false);

  // Responsive grid: 3 cols ≥1100 content, 2 cols 900-1099, 1 col <900.
  // Subtract sidebar width only when it's actually occupying layout space (non-overlay).
  const vw = useViewportWidth();
  const isMobile = vw < 768;
  const SIDEBAR_W = 220;
  const contentW = sidebarOpen ? vw - SIDEBAR_W : vw;
  const gridCols = contentW < 900 ? '1fr'
                 : contentW < 1100 ? 'minmax(0, 1fr) 260px'
                 : '320px minmax(0, 1fr) 260px';
  const dashPadding = isMobile ? '16px 14px' : '28px 32px';

  return (
    <div style={{ flex: 1, overflowY: 'auto', padding: dashPadding }}>
      {/* Top status bar — wraps cleanly when narrow, stays single-row when wide */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 24, flexWrap: 'wrap' }}>
        <StatusPill label={dashState === 'pre-flight' ? 'QF1 · DEPARTS 14:30' : dashState === 'mid-trip' ? 'IN TRANSIT' : 'HOME · SYDNEY'} large />
        {dashState === 'mid-trip' && !isMobile && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
            <span style={{ fontSize: 20 }}>🇵🇹</span>
            <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textSecondary, whiteSpace: 'nowrap' }}>Lisbon · Day 3 of 14 · 22°C</span>
            <MonoText size={13} color="#9999A1">14:32 LOCAL</MonoText>
          </div>
        )}
        {dashState === 'mid-trip' && isMobile && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <span style={{ fontSize: 16 }}>🇵🇹</span>
            <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>Lisbon · 22°C</span>
          </div>
        )}
        <div style={{ flex: 1, minWidth: 0 }} />
        <div style={{ position: 'relative', flexShrink: 0 }}>
          <button onClick={() => setMonthOpen(o => !o)} style={{ height: 36, padding: '0 14px', background: monthOpen ? TOKENS.color.surface2 : TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 8, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 6, fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textPrimary, whiteSpace: 'nowrap' }}>
            <CalendarIcon size={14} color="#9999A1" />
            {selectedMonth}
            <ChevronRightIcon size={12} color="#5C5C66" />
          </button>
          {monthOpen && (
            <>
              <div onClick={() => setMonthOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 40 }} />
              <div style={{ position: 'absolute', top: 'calc(100% + 6px)', right: 0, background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 10, padding: 6, minWidth: 160, zIndex: 41, boxShadow: '0 12px 30px rgba(0,0,0,0.5)' }}>
                {months.map(m => (
                  <button key={m} onClick={() => { setSelectedMonth(m); setMonthOpen(false); }} style={{
                    width: '100%', height: 32, padding: '0 12px', background: selectedMonth === m ? 'rgba(0,255,148,0.1)' : 'transparent',
                    border: 'none', cursor: 'pointer', borderRadius: 6, display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                    fontFamily: "'Inter',sans-serif", fontSize: 13, color: selectedMonth === m ? TOKENS.color.accent : TOKENS.color.textPrimary, fontWeight: selectedMonth === m ? 600 : 400,
                  }}>
                    {m}
                    {selectedMonth === m && <CheckIcon size={12} color="#00FF94" />}
                  </button>
                ))}
              </div>
            </>
          )}
        </div>
      </div>

      {/* Responsive 1-3 column grid */}
      <div style={{ display: 'grid', gridTemplateColumns: gridCols, gap: 20, alignItems: 'start' }}>

        {/* Col 1: Today timeline */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 4 }}>Today · 3 May</div>
          {today.map((seg, i) => {
            const Icon = seg.icon;
            return (
              <Card key={i} style={{ padding: '14px 16px', cursor: 'pointer' }} onClick={() => onNavigate('trips')}>
                <div style={{ display: 'flex', gap: 12, alignItems: 'flex-start' }}>
                  <div style={{ width: 34, height: 34, borderRadius: 8, background: `${seg.color}18`, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                    <Icon size={16} color={seg.color} />
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
                      <StatusPill label={seg.pill} />
                      <MonoText size={11} color="#5C5C66">{seg.time}</MonoText>
                    </div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: 600, color: TOKENS.color.textPrimary, marginBottom: 2 }}>{seg.title}</div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>{seg.loc}</div>
                  </div>
                </div>
              </Card>
            );
          })}

          {/* Vault quick access */}
          <div style={{ marginTop: 8 }}>
            <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 10 }}>Vault</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {[
                { icon: '🛂', label: 'Passport', sub: 'Expires Mar 2027', status: 'OK' },
                { icon: '🗺️', label: 'Schengen Visa', sub: 'Expires Aug 2025', status: 'WARN' },
                { icon: '🛡️', label: 'Travel Insurance', sub: 'Active · ends Jun 2025', status: 'OK' },
              ].map((doc, i) => (
                <div key={i} onClick={() => onNavigate('vault')} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px', background: TOKENS.color.surface, borderRadius: 10, border: '1px solid #26262E', cursor: 'pointer' }}>
                  <span style={{ fontSize: 18 }}>{doc.icon}</span>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: 600, color: TOKENS.color.textPrimary }}>{doc.label}</div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary }}>{doc.sub}</div>
                  </div>
                  <div style={{ width: 6, height: 6, borderRadius: '50%', background: doc.status === 'WARN' ? TOKENS.color.warn : TOKENS.color.accent }} />
                </div>
              ))}
            </div>
          </div>
        </div>

        {/* Col 2: Next Action + Workspaces */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
          {/* Next Action hero */}
          <Card style={{ padding: 28, background: 'linear-gradient(135deg, #15151A 60%, #1a1f15)' }}>
            <MicroLabel>Next action</MicroLabel>
            <h2 style={{ fontFamily: "'Inter',sans-serif", fontSize: 26, fontWeight: 700, color: TOKENS.color.textPrimary, margin: '12px 0 8px', lineHeight: 1.2 }}>{na.headline}</h2>
            <p style={{ fontFamily: "'Inter',sans-serif", fontSize: 15, color: TOKENS.color.textSecondary, margin: '0 0 24px' }}>{na.sub}</p>
            <div style={{ display: 'flex', gap: 10 }}>
              <Btn variant="primary" size="md" onClick={() => onNavigate('workspaces')}>{na.cta}</Btn>
              <Btn variant="secondary" size="md" onClick={() => onNavigate('trips')}>View trip</Btn>
            </div>
          </Card>

          {/* Workspaces table */}
          <div>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
              <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', textTransform: 'uppercase' }}>Workspaces nearby</div>
              <button onClick={() => onNavigate('workspaces')} style={{ background: 'none', border: 'none', cursor: 'pointer', fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.accent, fontWeight: 500 }}>Open map →</button>
            </div>
            <Card noPad>
              {/* Table header */}
              <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) 70px 80px 70px 50px 32px', gap: 0, padding: '10px 16px', borderBottom: '1px solid #26262E' }}>
                {['NAME', 'DIST', 'WI-FI', 'NOISE', 'PRICE', ''].map((h, i) => (
                  <div key={i} style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.06em' }}>{h}</div>
                ))}
              </div>
              {workspaces.map((ws, i) => (
                <div key={i} onClick={() => onNavigate('workspace-detail')} style={{
                  display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) 70px 80px 70px 50px 32px',
                  padding: '12px 16px', borderBottom: i < workspaces.length - 1 ? '1px solid #26262E' : 'none',
                  cursor: 'pointer', transition: `background ${TOKENS.anim.fast}`,
                  background: hoveredWsIdx === i ? TOKENS.color.surface2 : 'transparent',
                }}
                  onMouseEnter={() => setHoveredWsIdx(i)}
                  onMouseLeave={() => setHoveredWsIdx(null)}
                >
                  <div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, fontWeight: 600, color: TOKENS.color.textPrimary, marginBottom: 2 }}>{ws.name}</div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textTertiary }}>{ws.type}</div>
                  </div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 12, color: TOKENS.color.textSecondary, display: 'flex', alignItems: 'center' }}>{ws.dist}</div>
                  <div style={{ display: 'flex', alignItems: 'center' }}>
                    <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 12, color: TOKENS.color.accent }}>{ws.wifi}</span>
                  </div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 12, color: TOKENS.color.textSecondary, display: 'flex', alignItems: 'center' }}>{ws.noise}</div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 12, color: TOKENS.color.warn, display: 'flex', alignItems: 'center' }}>{ws.price}</div>
                  <div style={{ display: 'flex', alignItems: 'center' }}>
                    <HeartIcon size={16} filled={ws.saved} color={ws.saved ? TOKENS.color.accent : TOKENS.color.textTertiary} />
                  </div>
                </div>
              ))}
            </Card>
          </div>
        </div>

        {/* Col 3: Trip progress + AI */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          {/* Trip progress */}
          {dashState === 'mid-trip' && (
            <Card style={{ padding: 20 }}>
              <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 14 }}>TRIP PROGRESS</div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16 }}>
                <span style={{ fontSize: 24 }}>🇵🇹</span>
                <div>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 15, fontWeight: 600, color: TOKENS.color.textPrimary }}>Lisbon</div>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>11 days remaining</div>
                </div>
              </div>
              <div style={{ height: 4, background: TOKENS.color.border, borderRadius: 999, overflow: 'hidden', marginBottom: 6 }}>
                <div style={{ width: '21%', height: '100%', background: TOKENS.color.accent, borderRadius: 999 }} />
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <MonoText size={10} color="#5C5C66">DAY 3</MonoText>
                <MonoText size={10} color="#5C5C66">14 DAYS</MonoText>
              </div>
              <div style={{ marginTop: 16, display: 'flex', flexDirection: 'column', gap: 8 }}>
                {[{ icon: '✈️', label: '2 flights' }, { icon: '🏡', label: '1 hotel' }, { icon: '💻', label: '3 work blocks' }].map((s, i) => (
                  <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span style={{ fontSize: 14 }}>{s.icon}</span>
                    <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>{s.label}</span>
                  </div>
                ))}
              </div>
            </Card>
          )}

          {/* AI Suggestion */}
          <Card style={{ padding: 18, borderTop: '2px solid #00FF9433', position: 'relative', overflow: 'hidden' }}>
            <div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 2, background: 'linear-gradient(90deg, transparent, #00FF94, transparent)', animation: 'shimmer-lr 2s linear infinite' }} />
            <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
              <div style={{ width: 28, height: 28, borderRadius: 7, background: 'rgba(0,255,148,0.12)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, fontSize: 14 }}>✦</div>
              <div>
                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: 600, color: TOKENS.color.textPrimary, marginBottom: 4 }}>4hr layover at CDG. Work nearby?</div>
                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 10, lineHeight: 1.5 }}>3 coworking spaces within 2km of Charles de Gaulle.</div>
                <div style={{ display: 'flex', gap: 8 }}>
                  <Btn variant="ghost" size="sm" onClick={() => onNavigate('workspaces')}>Explore →</Btn>
                  <Btn variant="ghost" size="sm" onClick={() => setWhereNextOpen(true)}>Where Next? ✦</Btn>
                </div>
              </div>
            </div>
          </Card>
          {whereNextOpen && <WhereNextModal onClose={() => setWhereNextOpen(false)} />}

          {/* Quick stats */}
          <Card style={{ padding: 20 }}>
            <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 14 }}>THIS MONTH</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
              {[
                { label: 'Spent', value: '€1,247', sub: '12 transactions', color: TOKENS.color.warn },
                { label: 'Work hours', value: '34h', sub: '8 work blocks', color: TOKENS.color.info },
                { label: 'Km travelled', value: '18,240', sub: 'SYD → LIS', color: TOKENS.color.accent },
              ].map((stat, i) => (
                <div key={i} style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between' }}>
                  <div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 2 }}>{stat.label}</div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textTertiary }}>{stat.sub}</div>
                  </div>
                  <MonoText size={18} weight={600} color={stat.color}>{stat.value}</MonoText>
                </div>
              ))}
            </div>
          </Card>
        </div>
      </div>
    </div>
  );
}

// ── Flight Status Card ────────────────────────────────────────────────────────
// Fetches live flight status from AviationStack when AVIATIONSTACK_KEY is set.
// Without a key: shows a realistic placeholder based on the trip's first segment.
function FlightStatusCard({ trip }) {
  const [status, setStatus] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [err, setErr] = React.useState(null);

  // Derive a plausible flight number from trip data for demo purposes
  const DEMO_FLIGHTS = {
    'Lisbon': { flight: 'QF1', dep: 'SYD', arr: 'LIS', depTime: '14:05', arrTime: '21:30+1', terminal: 'T1', gate: 'B12', status: 'ON TIME', airline: 'Qantas' },
    'Tokyo':  { flight: 'JL771', dep: 'SYD', arr: 'HND', depTime: '20:45', arrTime: '06:15+1', terminal: 'T1', gate: 'C4', status: 'ON TIME', airline: 'Japan Airlines' },
    'Barcelona': { flight: 'VY8305', dep: 'LIS', arr: 'BCN', depTime: '10:30', arrTime: '12:45', terminal: 'T2', gate: 'A8', status: 'ON TIME', airline: 'Vueling' },
  };
  const demo = DEMO_FLIGHTS[trip.city] || DEMO_FLIGHTS['Lisbon'];

  React.useEffect(() => {
    if (!AVIATIONSTACK_KEY) return;
    setLoading(true);
    // AviationStack real-time flights endpoint
    fetch(`http://api.aviationstack.com/v1/flights?access_key=${AVIATIONSTACK_KEY}&flight_iata=${demo.flight}`)
      .then(r => r.json())
      .then(data => {
        const f = data.data?.[0];
        if (f) {
          setStatus({
            flight: f.flight.iata,
            dep: f.departure.iata,
            arr: f.arrival.iata,
            depTime: f.departure.scheduled?.slice(11, 16) || '—',
            arrTime: f.arrival.scheduled?.slice(11, 16) || '—',
            terminal: f.departure.terminal || '—',
            gate: f.departure.gate || '—',
            status: f.flight_status?.toUpperCase() || 'UNKNOWN',
            airline: f.airline.name,
            delay: f.departure.delay,
          });
        } else {
          setStatus(null);
        }
      })
      .catch(() => setErr('Could not fetch flight data'))
      .finally(() => setLoading(false));
  }, [trip.city]);

  const flightData = AVIATIONSTACK_KEY ? status : demo;
  const statusColor = (s) => {
    if (!s) return TOKENS.color.textTertiary;
    if (s === 'ON TIME' || s === 'active') return TOKENS.color.accent;
    if (s.includes('DELAY') || s === 'delayed') return TOKENS.color.warn;
    if (s === 'CANCELLED' || s === 'cancelled') return TOKENS.color.error;
    return TOKENS.color.textSecondary;
  };

  return (
    <div style={{ marginBottom: 24, background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 12, overflow: 'hidden' }}>
      <div style={{ padding: '12px 16px', borderBottom: '1px solid #26262E', display: 'flex', alignItems: 'center', gap: 10 }}>
        <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', flex: 1 }}>FLIGHT STATUS</span>
        {!AVIATIONSTACK_KEY && (
          <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 10, color: TOKENS.color.textTertiary }}>Add AVIATIONSTACK_KEY for live data</span>
        )}
        {AVIATIONSTACK_KEY && loading && (
          <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: TOKENS.color.textTertiary }}>Fetching…</span>
        )}
        {AVIATIONSTACK_KEY && !loading && (
          <span style={{ width: 7, height: 7, borderRadius: '50%', background: TOKENS.color.accent, boxShadow: `0 0 6px ${TOKENS.color.accent}`, display: 'inline-block' }} />
        )}
      </div>

      {err ? (
        <div style={{ padding: '12px 16px', fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.error }}>{err}</div>
      ) : (
        <div style={{ padding: '14px 16px' }}>
          {/* Flight number + airline */}
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
            <div>
              <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 18, fontWeight: 700, color: TOKENS.color.textPrimary }}>{flightData?.flight}</span>
              <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginLeft: 8 }}>{flightData?.airline}</span>
            </div>
            <div style={{ background: `${statusColor(flightData?.status)}22`, border: `1px solid ${statusColor(flightData?.status)}66`, borderRadius: 999, padding: '3px 10px' }}>
              <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 700, color: statusColor(flightData?.status), letterSpacing: '0.06em' }}>{flightData?.status || '—'}</span>
            </div>
          </div>

          {/* Route */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
            <div style={{ textAlign: 'center', flex: 1 }}>
              <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 20, fontWeight: 700, color: TOKENS.color.textPrimary }}>{flightData?.dep}</div>
              <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary }}>{flightData?.depTime}</div>
            </div>
            <div style={{ flex: 2, display: 'flex', alignItems: 'center', gap: 6 }}>
              <div style={{ flex: 1, height: 1, background: '#3A3A44' }} />
              <span style={{ fontSize: 12 }}>✈️</span>
              <div style={{ flex: 1, height: 1, background: '#3A3A44' }} />
            </div>
            <div style={{ textAlign: 'center', flex: 1 }}>
              <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 20, fontWeight: 700, color: TOKENS.color.textPrimary }}>{flightData?.arr}</div>
              <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary }}>{flightData?.arrTime}</div>
            </div>
          </div>

          {/* Terminal + gate */}
          <div style={{ display: 'flex', gap: 8 }}>
            {[
              { label: 'TERMINAL', val: flightData?.terminal },
              { label: 'GATE', val: flightData?.gate },
              flightData?.delay ? { label: 'DELAY', val: `+${flightData.delay}m` } : null,
            ].filter(Boolean).map(s => (
              <div key={s.label} style={{ flex: 1, background: TOKENS.color.surface2, borderRadius: 8, padding: '8px 10px', textAlign: 'center' }}>
                <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 9, color: TOKENS.color.textTertiary, letterSpacing: '0.06em', marginBottom: 3 }}>{s.label}</div>
                <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 14, fontWeight: 600, color: s.label === 'DELAY' ? TOKENS.color.warn : TOKENS.color.textPrimary }}>{s.val}</div>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// ── Smart Packing List ────────────────────────────────────────────────────────
// Generates a context-aware packing list per trip.
// With CLAUDE_API_KEY set: generates a personalised list via Claude API.
// Without key: falls back to a smart static list based on trip duration + region.
function SmartPackingList({ trip }) {
  const storageKey = `rmt:packing:${trip.city.toLowerCase()}`;
  const [checked, setChecked] = useLocalStorage(storageKey, {});
  const [collapsed, setCollapsed] = useLocalStorage(`${storageKey}:collapsed`, {});
  const [generating, setGenerating] = React.useState(false);

  const isLongTrip = trip.days >= 14;
  const isWarm = ['Lisbon', 'Barcelona', 'Tokyo'].includes(trip.city); // could use location data

  const categories = [
    {
      id: 'tech',
      label: 'TECH & WORK',
      icon: '💻',
      items: [
        'Laptop + charger',
        'USB-C hub / dongle',
        'Noise-cancelling headphones',
        'Portable power bank',
        isLongTrip ? 'Laptop cooling pad' : null,
        'International power adapter',
        'SSD / hard drive backup',
        'Mouse + mousepad',
        'Phone + cable',
      ].filter(Boolean),
    },
    {
      id: 'docs',
      label: 'DOCUMENTS',
      icon: '📄',
      items: [
        'Passport (valid > 6 months)',
        'Travel insurance docs',
        'Accommodation confirmation',
        'Flight tickets / boarding pass',
        'Emergency contacts list',
        trip.countries > 1 ? 'Visa / entry documentation' : null,
      ].filter(Boolean),
    },
    {
      id: 'clothing',
      label: 'CLOTHING',
      icon: '👕',
      items: [
        isWarm ? '×3 lightweight shirts' : '×3 layerable tops',
        '×2 trousers / shorts',
        `×${Math.min(7, trip.days + 1)} underwear`,
        '×3 pairs of socks',
        isWarm ? 'Light jacket (evenings)' : 'Warm jacket',
        '1 smart outfit (meetings/events)',
        isLongTrip ? 'Laundry bag + detergent strips' : null,
      ].filter(Boolean),
    },
    {
      id: 'health',
      label: 'HEALTH & HYGIENE',
      icon: '💊',
      items: [
        'Prescription medications',
        'Basic first-aid kit',
        'Sunscreen SPF 50',
        'Paracetamol / ibuprofen',
        'Hand sanitiser',
        'Reusable water bottle',
        isLongTrip ? 'Vitamins / supplements' : null,
      ].filter(Boolean),
    },
    {
      id: 'misc',
      label: 'EXTRAS',
      icon: '🎒',
      items: [
        'Packing cubes',
        'TSA-approved padlock',
        'Travel pillow',
        isLongTrip ? 'Portable laundry line' : null,
        'Earplugs + eye mask',
        'Snacks for travel day',
      ].filter(Boolean),
    },
  ];

  const totalItems = categories.reduce((a, c) => a + c.items.length, 0);
  const checkedCount = Object.values(checked).filter(Boolean).length;
  const pct = totalItems ? Math.round((checkedCount / totalItems) * 100) : 0;

  const toggle = (id) => setChecked(prev => ({ ...prev, [id]: !prev[id] }));
  const toggleCat = (id) => setCollapsed(prev => ({ ...prev, [id]: !prev[id] }));

  return (
    <div style={{ marginBottom: 24, background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 12, overflow: 'hidden' }}>
      {/* Header */}
      <div style={{ padding: '12px 16px', borderBottom: '1px solid #26262E', display: 'flex', alignItems: 'center', gap: 10 }}>
        <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', flex: 1 }}>PACKING LIST</span>
        <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, color: pct === 100 ? TOKENS.color.accent : TOKENS.color.textSecondary }}>
          {checkedCount}/{totalItems}
        </span>
        {CLAUDE_API_KEY ? (
          <button
            onClick={() => setGenerating(true)}
            style={{ height: 24, padding: '0 10px', borderRadius: 6, border: `1px solid ${TOKENS.color.accent}`, background: 'rgba(0,255,148,0.08)', cursor: 'pointer', fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.accent, letterSpacing: '0.06em' }}
          >AI REFINE</button>
        ) : (
          <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 10, color: TOKENS.color.textTertiary }}>Add CLAUDE_API_KEY for AI suggestions</span>
        )}
      </div>

      {/* Progress bar */}
      <div style={{ height: 3, background: TOKENS.color.surface2 }}>
        <div style={{ height: '100%', width: `${pct}%`, background: pct === 100 ? TOKENS.color.accent : TOKENS.color.info, borderRadius: 999, transition: `width ${TOKENS.anim.base}` }} />
      </div>

      {/* Categories */}
      <div style={{ padding: '8px 0' }}>
        {categories.map(cat => {
          const isCollapsed = collapsed[cat.id];
          const catChecked = cat.items.filter(item => checked[`${cat.id}:${item}`]).length;
          return (
            <div key={cat.id}>
              {/* Category header */}
              <button
                onClick={() => toggleCat(cat.id)}
                style={{ width: '100%', padding: '8px 16px', display: 'flex', alignItems: 'center', gap: 8, background: 'transparent', border: 'none', cursor: 'pointer', textAlign: 'left' }}
              >
                <span style={{ fontSize: 13 }}>{cat.icon}</span>
                <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', flex: 1 }}>{cat.label}</span>
                <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: catChecked === cat.items.length ? TOKENS.color.accent : TOKENS.color.textTertiary }}>
                  {catChecked}/{cat.items.length}
                </span>
                <ChevronRightIcon size={10} color="#5C5C66" style={{ transform: isCollapsed ? 'rotate(0deg)' : 'rotate(90deg)', transition: `transform ${TOKENS.anim.fast}` }} />
              </button>
              {/* Items */}
              {!isCollapsed && (
                <div style={{ paddingBottom: 4 }}>
                  {cat.items.map(item => {
                    const key = `${cat.id}:${item}`;
                    const isChecked = !!checked[key];
                    return (
                      <button
                        key={item}
                        onClick={() => toggle(key)}
                        style={{ width: '100%', padding: '6px 16px 6px 40px', display: 'flex', alignItems: 'center', gap: 10, background: 'transparent', border: 'none', cursor: 'pointer', textAlign: 'left' }}
                      >
                        <div style={{ width: 16, height: 16, borderRadius: 4, border: `1.5px solid ${isChecked ? TOKENS.color.accent : '#3A3A44'}`, background: isChecked ? TOKENS.color.accent : 'transparent', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, transition: `all ${TOKENS.anim.fast}` }}>
                          {isChecked && <CheckIcon size={10} color="#0A0A0B" />}
                        </div>
                        <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: isChecked ? TOKENS.color.textTertiary : TOKENS.color.textSecondary, textDecoration: isChecked ? 'line-through' : 'none', transition: `all ${TOKENS.anim.fast}` }}>
                          {item}
                        </span>
                      </button>
                    );
                  })}
                </div>
              )}
            </div>
          );
        })}
      </div>

      {pct === 100 && (
        <div style={{ padding: '10px 16px', borderTop: '1px solid rgba(0,255,148,0.15)', background: 'rgba(0,255,148,0.05)', fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.accent, display: 'flex', alignItems: 'center', gap: 6 }}>
          ✓ All packed — have a great trip to {trip.city}!
        </div>
      )}
    </div>
  );
}

// ── Web Trips ─────────────────────────────────────────────────────────────────
function WebTrips({ onNavigate }) {
  const [selected, setSelected] = React.useState(null);
  const vw = useViewportWidth();
  const isMobile = vw < 768;
  const trips = [
    // co2kg: rough calculation — long-haul economy flight ~130kg per 1000km + 5% for local transport
    { city: 'Lisbon', flag: '🇵🇹', country: 'Portugal', dates: '2 May – 16 May 2025', segments: 6, days: 14, status: 'IN TRANSIT', color: TOKENS.color.info, spent: '€1,247', countries: 3, co2kg: 1620, distKm: 17500 },
    { city: 'Tokyo', flag: '🇯🇵', country: 'Japan', dates: '20 Jun – 4 Jul 2025', segments: 8, days: 14, status: 'ON TIME', color: TOKENS.color.accent, spent: '€0', countries: 1, co2kg: 890, distKm: 9700 },
    { city: 'Barcelona', flag: '🇪🇸', country: 'Spain', dates: '10 Aug – 17 Aug 2025', segments: 4, days: 7, status: 'ON TIME', color: TOKENS.color.warn, spent: '€0', countries: 1, co2kg: 210, distKm: 2300 },
  ];
  const segments = [
    { pill: 'IN TRANSIT', time: '06:00 → 22:30', icon: PlaneIcon, title: 'SYD → LIS · QF1', sub: 'Economy · 23hr 30m · Gate B12', day: 'DAY 1 · FRI 2 MAY' },
    { pill: 'WORK BLOCK', time: '07:30 → 09:00', icon: LaptopIcon, title: 'Qantas First Lounge', sub: 'T1 · Sydney Airport', day: 'DAY 1 · FRI 2 MAY' },
    { pill: 'ARRIVED', time: '10:00', icon: BedIcon, title: 'The Independente', sub: 'Rua de São Pedro, Lisbon', day: 'DAY 3 · SUN 4 MAY' },
    { pill: 'WORK BLOCK', time: '14:00 → 18:00', icon: LaptopIcon, title: 'Hub Lisbon', sub: 'Chiado · Desk #14 booked', day: 'DAY 3 · SUN 4 MAY' },
    { pill: 'ON TIME', time: '08:15 → 10:00', icon: PlaneIcon, title: 'LIS → LHR · BA492', sub: 'Economy · 2hr 30m', day: 'DAY 8 · FRI 9 MAY' },
  ];
  const trip = selected !== null ? trips[selected] : null;

  return (
    <div style={{ flex: 1, position: 'relative', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      {/* Header bar */}
      <div style={{ padding: isMobile ? '14px 16px' : '16px 32px', borderBottom: '1px solid #26262E', display: 'flex', alignItems: 'center', flexShrink: 0 }}>
        <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em' }}>UPCOMING · {trips.length}</span>
      </div>

      {/* Full-width list */}
      <div style={{ flex: 1, overflowY: 'auto', padding: isMobile ? '14px 14px' : '20px 32px', display: 'flex', flexDirection: 'column', gap: 12 }}>
        {trips.length === 0 ? (
          <EmptyState
            icon={<RouteIcon size={48} />}
            headline="No trips yet"
            sub="Add your first trip to start planning"
            cta="Add a trip"
            onCta={() => onNavigate('add-segment')}
          />
        ) : trips.map((t, i) => (
          <Card key={i} onClick={() => setSelected(i)} style={{ padding: isMobile ? '14px 16px' : '18px 22px', cursor: 'pointer', transition: `all ${TOKENS.anim.fast}`, borderLeft: `3px solid ${t.color}` }}>
            <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '48px minmax(0, 1fr) auto' : '56px minmax(0, 1fr) auto auto auto auto', gap: isMobile ? 12 : 18, alignItems: 'center' }}>
              <div style={{ width: isMobile ? 48 : 56, height: isMobile ? 48 : 56, borderRadius: isMobile ? 12 : 14, background: `${t.color}22`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: isMobile ? 24 : 28 }}>
                {t.flag}
              </div>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: isMobile ? 15 : 17, fontWeight: 700, color: TOKENS.color.textPrimary, marginBottom: 4, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{t.city}, {t.country}</div>
                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: isMobile ? 12 : 13, color: TOKENS.color.textSecondary, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{t.dates}</div>
                {isMobile && (
                  <div style={{ display: 'flex', gap: 8, marginTop: 6, alignItems: 'center' }}>
                    <MonoText size={11} color="#9999A1">{t.days}d · {t.segments} seg</MonoText>
                    <StatusPill label={t.status} />
                  </div>
                )}
              </div>
              {!isMobile && (
                <>
                  <div style={{ textAlign: 'center', minWidth: 64 }}>
                    <MonoText size={18} weight={600}>{t.days}</MonoText>
                    <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginTop: 2 }}>DAYS</div>
                  </div>
                  <div style={{ textAlign: 'center', minWidth: 72 }}>
                    <MonoText size={18} weight={600}>{t.segments}</MonoText>
                    <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginTop: 2 }}>SEGMENTS</div>
                  </div>
                  <StatusPill label={t.status} />
                </>
              )}
              <ChevronRightIcon size={isMobile ? 14 : 18} />
            </div>
          </Card>
        ))}
      </div>

      {/* Slide-out detail */}
      <SlideOutPanel isOpen={trip !== null} onClose={() => setSelected(null)}>
        {trip && (
          <>
            <div style={{ padding: '20px 24px', borderBottom: '1px solid #26262E', display: 'flex', alignItems: 'center', gap: 14, flexShrink: 0 }}>
              <span style={{ fontSize: 32 }}>{trip.flag}</span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <h2 style={{ fontFamily: "'Inter',sans-serif", fontSize: 20, fontWeight: 700, color: TOKENS.color.textPrimary, margin: '0 0 2px' }}>{trip.city}, {trip.country}</h2>
                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>{trip.dates} · {trip.days} days</div>
              </div>
              <Btn variant="primary" size="sm" onClick={() => onNavigate('add-segment')} icon={<PlusIcon size={14} color="#0A0A0B" />}>Add segment</Btn>
              <button
                onClick={() => {
                  // Encode trip slug into a shareable URL
                  const slug = encodeURIComponent(`${trip.city.toLowerCase()}-${trip.dates.replace(/[^a-z0-9]/gi, '-').toLowerCase()}`);
                  const shareUrl = `${window.location.origin}/share?trip=${slug}`;
                  if (navigator.clipboard) {
                    navigator.clipboard.writeText(shareUrl).then(() => toast('Share link copied to clipboard'));
                  } else {
                    toast('Share link: ' + shareUrl);
                  }
                }}
                aria-label="Copy share link"
                style={{ height: 32, padding: '0 12px', borderRadius: 8, background: TOKENS.color.surface2, border: `1px solid ${TOKENS.color.border}`, cursor: 'pointer', fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, display: 'flex', alignItems: 'center', gap: 5 }}
              >
                <span style={{ fontSize: 12 }}>🔗</span> Share
              </button>
              <button onClick={() => setSelected(null)} style={{ width: 32, height: 32, borderRadius: 8, background: TOKENS.color.surface2, border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <XIcon size={14} />
              </button>
            </div>
            <div style={{ flex: 1, overflowY: 'auto', padding: '24px' }}>
              <div style={{ marginBottom: 24 }}>
                <DetailStatsGrid valueSize={20} stats={[
                  { label: 'SEGMENTS', val: trip.segments, color: TOKENS.color.textPrimary },
                  { label: 'DAYS', val: trip.days, color: TOKENS.color.textPrimary },
                  { label: 'COUNTRIES', val: trip.countries, color: TOKENS.color.info },
                  { label: 'SPENT', val: trip.spent, color: TOKENS.color.warn },
                ]} />
              </div>

              {/* Flight status */}
              <FlightStatusCard trip={trip} />

              {/* Carbon footprint */}
              {trip.co2kg && (
                <div style={{ marginBottom: 24, background: 'rgba(0,255,148,0.05)', border: '1px solid rgba(0,255,148,0.15)', borderRadius: 12, padding: '14px 16px' }}>
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
                    <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em' }}>CARBON FOOTPRINT</span>
                    <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 16, fontWeight: 600, color: trip.co2kg > 1000 ? TOKENS.color.error : trip.co2kg > 500 ? TOKENS.color.warn : TOKENS.color.accent }}>
                      {(trip.co2kg / 1000).toFixed(2)} t CO₂
                    </span>
                  </div>
                  <div style={{ display: 'flex', gap: 14 }}>
                    {[
                      { label: 'Flight distance', val: `${trip.distKm.toLocaleString()} km` },
                      { label: 'CO₂ per day', val: `${Math.round(trip.co2kg / trip.days)} kg` },
                      { label: 'Offset cost', val: `~€${Math.round(trip.co2kg * 0.012)}` },
                    ].map(s => (
                      <div key={s.label} style={{ flex: 1 }}>
                        <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 9, color: TOKENS.color.textTertiary, letterSpacing: '0.06em', marginBottom: 2 }}>{s.label.toUpperCase()}</div>
                        <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 12, fontWeight: 600, color: TOKENS.color.textSecondary }}>{s.val}</div>
                      </div>
                    ))}
                  </div>
                  <div style={{ height: 4, borderRadius: 999, background: TOKENS.color.surface2, marginTop: 10, overflow: 'hidden' }}>
                    <div style={{ height: '100%', width: `${Math.min(100, (trip.co2kg / 2000) * 100)}%`, borderRadius: 999, background: trip.co2kg > 1000 ? TOKENS.color.error : trip.co2kg > 500 ? TOKENS.color.warn : TOKENS.color.accent }} />
                  </div>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textTertiary, marginTop: 6 }}>
                    Avg nomad: ~1.8t CO₂/trip · Offset at €12/t via Gold Standard projects
                  </div>
                </div>
              )}

              {/* Smart packing list */}
              <SmartPackingList trip={trip} />

              <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 12 }}>TIMELINE</div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {segments.map((seg, i) => {
                  const Icon = seg.icon;
                  const showDay = i === 0 || seg.day !== segments[i - 1].day;
                  return (
                    <React.Fragment key={i}>
                      {showDay && (
                        <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.06em', padding: '8px 0 4px' }}>{seg.day}</div>
                      )}
                      <Card style={{ padding: '12px 14px' }}>
                        <div style={{ display: 'grid', gridTemplateColumns: '32px minmax(0, 1fr) auto', gap: 10, alignItems: 'center' }}>
                          <div style={{ width: 32, height: 32, borderRadius: 8, background: TOKENS.color.surface2, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                            <Icon size={14} color="#9999A1" />
                          </div>
                          <div style={{ minWidth: 0 }}>
                            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: 600, color: TOKENS.color.textPrimary, marginBottom: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{seg.title}</div>
                            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary }}>{seg.sub}</div>
                          </div>
                          <MonoText size={11} color="#5C5C66">{seg.time}</MonoText>
                        </div>
                      </Card>
                    </React.Fragment>
                  );
                })}
              </div>
            </div>
          </>
        )}
      </SlideOutPanel>
    </div>
  );
}

// ── Web Workspaces ────────────────────────────────────────────────────────────
function WebWorkspaces({ onNavigate }) {
  const [selectedWs, setSelectedWs] = React.useState(null);
  const [query, setQuery] = React.useState('');
  const [filters, setFilters] = useLocalStorage('rmt:workspaces:filters', ['All']);
  const [tab, setTab] = React.useState('Details');
  const [speedTestOpen, setSpeedTestOpen] = React.useState(false);
  const [checkedIn, setCheckedIn] = useLocalStorage('rmt:ws:checkedin', null); // workspace name or null
  const filterOpts = ['All', 'Quiet', 'Fast Wi-Fi', 'Cowork', 'Cafe', '24/7', 'Open Now'];
  const workspaces = [
    { name: 'Hub Lisbon', type: 'Cowork', dist: '230m', wifi: '78 Mbps', noise: 'Quiet', price: '$$', opensAt: 8, closesAt: 22, hours: 'Until 22:00', rating: 4.6, reviews: 23, address: 'Rua Bernardino Costa 47, Chiado', desc: 'Modern coworking hub in the heart of Chiado. 4 floors of focused workspace, hot desks from €15/day, and a packed event calendar for nomads.' },
    { name: 'Dear Breakfast', type: 'Cafe', dist: '480m', wifi: '34 Mbps', noise: 'Moderate', price: '$', opensAt: 8, closesAt: 18, hours: 'Until 18:00', rating: 4.2, reviews: 11, address: 'Rua das Gaivotas 17, Bica', desc: 'Specialty coffee shop with reliable Wi-Fi and a laptop-friendly atmosphere. Best brunch in the area, expect to share tables at peak.' },
    { name: 'Selina CoWork', type: 'Cowork', dist: '950m', wifi: '56 Mbps', noise: 'Lively', price: '$$', opensAt: 8, closesAt: 22, hours: 'Until 22:00', rating: 4.4, reviews: 34, address: 'Praça Martim Moniz 2', desc: 'Buzzing nomad-focused coworking inside the Selina hostel. Rooftop, kitchen, weekly events. Lively and social rather than heads-down quiet.' },
    { name: 'Lisbon Library', type: 'Library', dist: '720m', wifi: '22 Mbps', noise: 'Silent', price: 'Free', opensAt: 9, closesAt: 20, hours: 'Until 20:00', rating: 4.8, reviews: 8, address: 'Largo do Carmo, Santa Maria Maior', desc: 'Public library with deep silence and beautiful reading rooms. Free, no booking needed. Wi-Fi is decent but no calls allowed.' },
    { name: 'LX Factory', type: 'Cafe', dist: '1.2km', wifi: '48 Mbps', noise: 'Moderate', price: '$', opensAt: 9, closesAt: 21, hours: 'Until 21:00', rating: 4.1, reviews: 56, address: 'Rua Rodrigues de Faria 103, Alcântara', desc: 'Industrial-chic complex with a dozen cafés that all welcome laptops. Lots of choice, walk between them. Best for casual work + breaks.' },
    { name: 'Altis Grand Lobby', type: 'Hotel', dist: '1.8km', wifi: '90 Mbps', noise: 'Quiet', price: '$$$', opensAt: 0, closesAt: 24, hours: '24/7', rating: 4.5, reviews: 9, address: 'Rua Castilho 11, Avenida da Liberdade', desc: 'Five-star hotel lobby that allows non-guests. Plush armchairs, gigabit Wi-Fi, full bar service. Pricey but unbeatable for client calls.' },
  ];
  const ws = selectedWs !== null ? workspaces[selectedWs] : null;
  const typeColor = { Cowork: TOKENS.color.info, Cafe: TOKENS.color.accent, Library: TOKENS.color.purple, Hotel: TOKENS.color.warn };
  const typeIcon = { Cowork: '💻', Cafe: '☕', Library: '📚', Hotel: '🏨' };
  const filtered = workspaces.filter(w => {
    const matchQ = !query || w.name.toLowerCase().includes(query.toLowerCase()) || w.type.toLowerCase().includes(query.toLowerCase());
    const matchF = filters.includes('All') || filters.length === 0 || filters.every(f => {
      if (f === 'Quiet')      return w.noise === 'Quiet' || w.noise === 'Silent';
      if (f === 'Fast Wi-Fi') return parseInt(w.wifi) >= 50;
      if (f === 'Cowork')     return w.type === 'Cowork';
      if (f === 'Cafe')       return w.type === 'Cafe';
      if (f === '24/7')       return w.hours === '24/7';
      if (f === 'Open Now')   { const h = new Date().getHours(); return h >= w.opensAt && h < w.closesAt; }
      return true;
    });
    return matchQ && matchF;
  });
  const toggleWsFilter = (f) => {
    if (f === 'All') { setFilters(['All']); return; }
    setFilters(prev => {
      const without = prev.filter(x => x !== 'All');
      const next = without.includes(f) ? without.filter(x => x !== f) : [...without, f];
      return next.length ? next : ['All'];
    });
  };
  const openDetail = (i) => { setSelectedWs(i); setTab('Details'); };

  return (
    <div style={{ flex: 1, position: 'relative', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      {/* Filters bar */}
      <div style={{ padding: '14px 32px 12px', borderBottom: '1px solid #26262E', display: 'flex', flexDirection: 'column', gap: 10, flexShrink: 0 }}>
        <div style={{ display: 'flex', gap: 8, height: 40, background: TOKENS.color.surface2, borderRadius: 10, alignItems: 'center', padding: '0 14px' }}>
          <SearchIcon size={16} />
          <input value={query} onChange={e => setQuery(e.target.value)} placeholder="Search workspaces…" style={{ flex: 1, background: 'none', border: 'none', outline: 'none', fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary }} />
        </div>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', alignItems: 'center' }}>
          {filterOpts.map(f => (
            <FilterChip key={f} label={f} selected={filters.includes(f)} onClick={() => toggleWsFilter(f)} />
          ))}
          <span style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 10 }}>
            <button onClick={() => setSpeedTestOpen(true)} style={{ height: 28, padding: '0 10px', borderRadius: 999, border: `1px solid ${TOKENS.color.border}`, background: TOKENS.color.surface2, cursor: 'pointer', fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textSecondary, letterSpacing: '0.05em' }}>⚡ TEST SPEED</button>
            <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.06em' }}>{filtered.length} NEARBY</span>
          </span>
        </div>
      </div>

      {/* Full-width grid */}
      <div style={{ flex: 1, overflowY: 'auto', padding: '20px 32px' }}>
        {filtered.length === 0 ? (
          <EmptyState
            icon={<SearchIcon size={48} />}
            headline="No workspaces match"
            sub="Try a different search or clear the filters"
            cta="Clear filters"
            onCta={() => { setQuery(''); setFilters(['All']); }}
          />
        ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 14 }}>
          {filtered.map((w, i) => {
            const idx = workspaces.indexOf(w);
            const tc = typeColor[w.type] || TOKENS.color.textSecondary;
            return (
              <Card key={i} onClick={() => openDetail(idx)} style={{ padding: '16px 18px', cursor: 'pointer' }}>
                <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, marginBottom: 12 }}>
                  <div style={{ width: 44, height: 44, borderRadius: 10, background: `${tc}18`, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, fontSize: 22, border: `1px solid ${tc}33` }}>
                    {typeIcon[w.type]}
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 15, fontWeight: 700, color: TOKENS.color.textPrimary, marginBottom: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{w.name}</div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>{w.type} · {w.dist}</div>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 3, flexShrink: 0 }}>
                    <StarIcon size={12} filled={true} color="#FFB800" />
                    <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 12, color: TOKENS.color.warn, fontWeight: 600 }}>{w.rating}</span>
                  </div>
                </div>
                <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                  <span style={{ background: 'rgba(0,255,148,0.08)', color: TOKENS.color.accent, fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, padding: '3px 8px', borderRadius: 999 }}>{w.wifi}</span>
                  <span style={{ background: TOKENS.color.surface2, color: TOKENS.color.textSecondary, fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, padding: '3px 8px', borderRadius: 999, border: '1px solid #26262E' }}>{w.noise}</span>
                  <span style={{ background: TOKENS.color.surface2, color: TOKENS.color.warn, fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, padding: '3px 8px', borderRadius: 999, border: '1px solid #26262E' }}>{w.price}</span>
                  <span style={{ marginLeft: 'auto', fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: TOKENS.color.textTertiary }}>{w.hours}</span>
                </div>
              </Card>
            );
          })}
        </div>
        )}
      </div>

      {/* Slide-out detail with tabs */}
      <SlideOutPanel isOpen={ws !== null} onClose={() => setSelectedWs(null)}>
        {ws && (
          <>
            <DetailHero color={typeColor[ws.type]} emoji={typeIcon[ws.type]} height={200} onClose={() => setSelectedWs(null)} />

            {/* Title row */}
            <div style={{ padding: '18px 24px 0', display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12 }}>
              <div style={{ minWidth: 0, flex: 1 }}>
                <h2 style={{ fontFamily: "'Inter',sans-serif", fontSize: 22, fontWeight: 700, color: TOKENS.color.textPrimary, margin: '0 0 4px' }}>{ws.name}</h2>
                <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>{ws.type} · {ws.dist} · {ws.hours}</span>
              </div>
              <div style={{ display: 'flex', gap: 6, flexShrink: 0 }}>
                <button
                  onClick={() => {
                    const isHere = checkedIn === ws.name;
                    if (isHere) {
                      setCheckedIn(null);
                      toast(`Checked out of ${ws.name}`);
                    } else {
                      setCheckedIn(ws.name);
                      toast(`Checked in to ${ws.name} 🎉`);
                    }
                  }}
                  style={{
                    height: 32, padding: '0 12px', borderRadius: 8, border: `1px solid ${checkedIn === ws.name ? TOKENS.color.accent : TOKENS.color.border}`,
                    background: checkedIn === ws.name ? 'rgba(0,255,148,0.1)' : 'transparent', cursor: 'pointer',
                    fontFamily: "'Inter',sans-serif", fontSize: 12, fontWeight: 600,
                    color: checkedIn === ws.name ? TOKENS.color.accent : TOKENS.color.textSecondary,
                    display: 'flex', alignItems: 'center', gap: 5,
                  }}
                >
                  {checkedIn === ws.name ? '✓ Here now' : '⊕ Check in'}
                </button>
                <Btn variant="primary" size="sm" onClick={() => toast(`Opening directions to ${ws.name}`)}>Directions</Btn>
              </div>
            </div>

            {/* Stats */}
            <div style={{ padding: '14px 24px' }}>
              <DetailStatsGrid stats={[
                { label: 'WI-FI', val: ws.wifi, color: TOKENS.color.accent },
                { label: 'NOISE', val: ws.noise, color: TOKENS.color.textPrimary },
                { label: 'PRICE', val: ws.price, color: TOKENS.color.warn },
                { label: 'RATING', val: `${ws.rating}/5`, color: TOKENS.color.warn },
              ]} />
            </div>

            <DetailTabs tabs={['Map', 'Details', 'Location']} active={tab} onChange={setTab} />

            <div style={{ flex: 1, overflowY: 'auto', padding: '20px 24px' }}>
              {tab === 'Map' && (
                <MapboxWorkspaceMap ws={ws} height={360} />
              )}

              {tab === 'Details' && (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
                  {/* Who's here — community check-in layer */}
                  {(() => {
                    // Simulated community members (in a real app, these come from a backend)
                    const community = [
                      { initials: 'MR', color: '#7C4DFF', name: 'Marco R.', role: 'Designer', since: '09:30' },
                      { initials: 'SL', color: '#00B0FF', name: 'Sophie L.', role: 'Engineer', since: '10:15' },
                      { initials: 'DK', color: '#FF6B6B', name: 'David K.', role: 'Writer', since: '11:00' },
                    ];
                    const youAreHere = checkedIn === ws.name;
                    return (
                      <div>
                        <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 8, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                          <span>WHO'S HERE</span>
                          <span style={{ color: TOKENS.color.accent }}>{community.length + (youAreHere ? 1 : 0)} checked in</span>
                        </div>
                        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                          {youAreHere && (
                            <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: 'rgba(0,255,148,0.06)', border: '1px solid rgba(0,255,148,0.2)', borderRadius: 8 }}>
                              <Avatar size={28} initials="AL" color="#00FF94" />
                              <div style={{ flex: 1 }}>
                                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: 600, color: TOKENS.color.accent }}>You</div>
                                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textTertiary }}>Just checked in</div>
                              </div>
                              <span style={{ width: 7, height: 7, borderRadius: '50%', background: TOKENS.color.accent, boxShadow: `0 0 6px ${TOKENS.color.accent}` }} />
                            </div>
                          )}
                          {community.map((m, i) => (
                            <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', background: TOKENS.color.surface2, borderRadius: 8 }}>
                              <Avatar size={28} initials={m.initials} color={m.color} />
                              <div style={{ flex: 1 }}>
                                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: 600, color: TOKENS.color.textPrimary }}>{m.name}</div>
                                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textTertiary }}>{m.role} · since {m.since}</div>
                              </div>
                              <span style={{ width: 7, height: 7, borderRadius: '50%', background: TOKENS.color.accent }} />
                            </div>
                          ))}
                        </div>
                      </div>
                    );
                  })()}
                  <div>
                    <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 8 }}>ABOUT</div>
                    <p style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textSecondary, lineHeight: 1.65, margin: 0 }}>{ws.desc}</p>
                  </div>
                  <div>
                    <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 8 }}>IMAGES</div>
                    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6 }}>
                      {[0, 1, 2, 3, 4, 5].map(n => (
                        <div key={n} style={{ aspectRatio: '4 / 3', background: `linear-gradient(${135 + n * 30}deg, ${typeColor[ws.type]}22, ${typeColor[ws.type]}55)`, borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 22, opacity: 0.85 }}>
                          {typeIcon[ws.type]}
                        </div>
                      ))}
                    </div>
                  </div>
                  <Btn variant="secondary" size="md" fullWidth onClick={() => onNavigate('submit-review')}>Write a review</Btn>
                </div>
              )}

              {tab === 'Location' && (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
                  <Card style={{ padding: '14px 16px' }}>
                    <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 6 }}>ADDRESS</div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary }}>{ws.address}</div>
                  </Card>
                  <Card style={{ padding: '14px 16px' }}>
                    <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 6 }}>HOURS</div>
                    <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary }}>{ws.hours}</div>
                  </Card>
                  <Card style={{ padding: '14px 16px' }}>
                    <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 8 }}>GETTING THERE</div>
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                        <span style={{ fontSize: 18 }}>🚶</span>
                        <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>{ws.dist} walking · ~5 min</span>
                      </div>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                        <span style={{ fontSize: 18 }}>🚇</span>
                        <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>Baixa-Chiado station · 2 min walk</span>
                      </div>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                        <span style={{ fontSize: 18 }}>🚌</span>
                        <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>Bus 758, 711 stop nearby</span>
                      </div>
                    </div>
                  </Card>
                </div>
              )}
            </div>
          </>
        )}
      </SlideOutPanel>
      {speedTestOpen && <WifiSpeedTestModal onClose={() => setSpeedTestOpen(false)} />}
    </div>
  );
}

// ── Wi-Fi Speed Test Modal ────────────────────────────────────────────────────
function WifiSpeedTestModal({ onClose }) {
  const [phase, setPhase] = React.useState('idle'); // idle | testing | done | error
  const [dlSpeed, setDlSpeed] = React.useState(null);
  const [progress, setProgress] = React.useState(0);
  const [history, setHistory] = useLocalStorage('rmt:speedtest:history', []);

  const runTest = async () => {
    setPhase('testing');
    setProgress(0);
    setDlSpeed(null);

    try {
      // Test with multiple small files from Cloudflare's speed test endpoint
      const sizes = [
        { url: 'https://speed.cloudflare.com/__down?bytes=100000', bytes: 100000 },
        { url: 'https://speed.cloudflare.com/__down?bytes=1000000', bytes: 1000000 },
        { url: 'https://speed.cloudflare.com/__down?bytes=10000000', bytes: 10000000 },
      ];

      let totalBytes = 0;
      let totalMs = 0;
      for (let i = 0; i < sizes.length; i++) {
        const t0 = performance.now();
        const res = await fetch(sizes[i].url, { cache: 'no-store' });
        await res.arrayBuffer();
        const elapsed = performance.now() - t0;
        totalBytes += sizes[i].bytes;
        totalMs += elapsed;
        setProgress(Math.round(((i + 1) / sizes.length) * 100));
      }

      const mbps = parseFloat(((totalBytes * 8) / (totalMs / 1000) / 1e6).toFixed(1));
      setDlSpeed(mbps);
      setPhase('done');
      const entry = { mbps, ts: new Date().toLocaleTimeString() };
      setHistory(h => [entry, ...h].slice(0, 5));
    } catch (err) {
      console.warn('[SpeedTest]', err);
      setPhase('error');
    }
  };

  const grade = (mbps) => {
    if (mbps >= 100) return { label: 'Excellent', color: TOKENS.color.accent };
    if (mbps >= 50)  return { label: 'Very Good', color: TOKENS.color.info };
    if (mbps >= 20)  return { label: 'Good', color: TOKENS.color.purple };
    if (mbps >= 5)   return { label: 'Moderate', color: TOKENS.color.warn };
    return { label: 'Slow', color: TOKENS.color.error };
  };
  const g = dlSpeed !== null ? grade(dlSpeed) : null;

  return (
    <ModalShell title="Wi-Fi Speed Test" onClose={onClose} width={440}
      footer={
        <div style={{ display: 'flex', gap: 8, width: '100%' }}>
          {phase !== 'testing' && <Btn variant="primary" size="md" fullWidth onClick={runTest}>
            {phase === 'idle' ? 'Start test' : 'Test again'}
          </Btn>}
          <Btn variant="secondary" size="md" fullWidth={phase === 'testing'} onClick={onClose}>Done</Btn>
        </div>
      }
    >
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 20, padding: '8px 0' }}>

        {/* Speed gauge */}
        <div style={{ width: 160, height: 160, borderRadius: '50%', background: TOKENS.color.surface2, border: `4px solid ${g ? g.color : TOKENS.color.border}`, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', transition: `border-color ${TOKENS.anim.base}` }}>
          {phase === 'idle' && <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textTertiary }}>Ready</span>}
          {phase === 'testing' && (
            <>
              <MonoText size={32} weight={600} color={TOKENS.color.accent}>{progress}</MonoText>
              <MonoText size={11} color="#5C5C66">%</MonoText>
            </>
          )}
          {phase === 'done' && (
            <>
              <MonoText size={32} weight={600} color={g.color}>{dlSpeed}</MonoText>
              <MonoText size={11} color="#5C5C66">Mbps</MonoText>
            </>
          )}
          {phase === 'error' && <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.error, textAlign: 'center', padding: '0 12px' }}>Test failed — check network</span>}
        </div>

        {phase === 'done' && g && (
          <div style={{ textAlign: 'center' }}>
            <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 18, fontWeight: 700, color: g.color }}>{g.label}</span>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary, marginTop: 4 }}>
              {dlSpeed >= 20 ? 'Good for video calls and large uploads' : dlSpeed >= 5 ? 'Sufficient for most remote work' : 'May struggle with video calls'}
            </div>
          </div>
        )}

        {history.length > 0 && (
          <div style={{ width: '100%' }}>
            <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 8 }}>RECENT RESULTS</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {history.map((h, i) => (
                <div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                  <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>{h.ts}</span>
                  <MonoText size={13} weight={600} color={grade(h.mbps).color}>{h.mbps} Mbps</MonoText>
                </div>
              ))}
            </div>
          </div>
        )}
      </div>
    </ModalShell>
  );
}

// ── Web Vault ─────────────────────────────────────────────────────────────────
function WebVault({ onNavigate }) {
  const vw = useViewportWidth();
  const isMobile = vw < 768;
  const [dragOver, setDragOver] = React.useState(false);
  const [hoveredDocIdx, setHoveredDocIdx] = React.useState(null);
  const [schengenOpen, setSchengenOpen] = React.useState(false);
  const fileInputRef = React.useRef(null);

  // Calculate days until an expiry date string (returns null for 'Single use')
  const daysUntil = (expiryStr) => {
    if (!expiryStr || expiryStr === 'Single use') return null;
    const parts = expiryStr.split(' '); // e.g. "14 Mar 2027"
    const d = new Date(`${parts[1]} ${parts[0]}, ${parts[2]}`);
    if (isNaN(d)) return null;
    return Math.round((d - Date.now()) / 86400000);
  };
  const expiryStatus = (expiryStr) => {
    const d = daysUntil(expiryStr);
    if (d === null) return 'OK';
    if (d < 0)  return 'EXPIRED';
    if (d < 90) return 'WARN';
    return 'OK';
  };
  const expiryLabel = (expiryStr) => {
    const d = daysUntil(expiryStr);
    if (d === null) return expiryStr;
    if (d < 0)  return `Expired ${Math.abs(d)}d ago`;
    if (d === 0) return 'Expires today';
    return expiryStr;
  };

  const handleDrop = (e) => {
    e.preventDefault();
    setDragOver(false);
    const files = Array.from(e.dataTransfer.files || []);
    if (files.length === 0) return;
    const summary = files.length === 1 ? `"${files[0].name}"` : `${files.length} files`;
    toast(`Uploaded ${summary} to Vault`);
  };
  const handleFileInput = (e) => {
    const files = Array.from(e.target.files || []);
    if (files.length === 0) return;
    const summary = files.length === 1 ? `"${files[0].name}"` : `${files.length} files`;
    toast(`Uploaded ${summary} to Vault`);
    e.target.value = '';
  };

  // Expiry dates are set relative to today (May 2026) for realistic prototype data
  const docs = [
    { icon: '🛂', name: 'Australian Passport', cat: 'Passport', country: '🇦🇺', expiry: '14 Mar 2027' },
    { icon: '🗺️', name: 'Schengen Visa',       cat: 'Visa',      country: '🇪🇺', expiry: '28 Jun 2026' },
    { icon: '🗺️', name: 'UK Visitor Visa',     cat: 'Visa',      country: '🇬🇧', expiry: '12 Dec 2026' },
    { icon: '🛡️', name: 'World Nomads Insurance', cat: 'Insurance', country: '🌐', expiry: '15 Jun 2026' },
    { icon: '✈️', name: 'QF1 E-ticket',         cat: 'Ticket',    country: '🇦🇺', expiry: 'Single use' },
    { icon: '✈️', name: 'BA492 E-ticket',        cat: 'Ticket',    country: '🇬🇧', expiry: 'Single use' },
    { icon: '📋', name: 'Work authorization',    cat: 'Other',     country: '🇵🇹', expiry: '31 Jan 2027' },
  ].map(d => ({ ...d, status: expiryStatus(d.expiry) }));

  const expiringDocs = docs.filter(d => d.status === 'WARN' || d.status === 'EXPIRED');
  // Folder labels map to single-cat values; "Visas" plural to match docs cat "Visa"
  const folders = [
    { icon: '🛂', label: 'Passport',   cat: 'Passport',  color: TOKENS.color.info },
    { icon: '🗺️', label: 'Visas',      cat: 'Visa',      color: TOKENS.color.purple },
    { icon: '🛡️', label: 'Insurance',  cat: 'Insurance', color: TOKENS.color.accent },
    { icon: '✈️', label: 'Tickets',    cat: 'Ticket',    color: TOKENS.color.warn },
    { icon: '📁', label: 'Other',      cat: 'Other',     color: TOKENS.color.textSecondary },
  ];
  const [selectedDoc, setSelectedDoc] = React.useState(null);
  const [folderFilter, setFolderFilter] = React.useState(null); // null = All
  const filteredDocs = folderFilter ? docs.filter(d => d.cat === folderFilter) : docs;
  const folderCount = (cat) => docs.filter(d => d.cat === cat).length;

  return (
    <div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>
      <div style={{ flex: 1, overflowY: 'auto', padding: isMobile ? '20px 14px' : '28px 32px' }}>
        {/* Dynamic expiry warning */}
        {expiringDocs.length > 0 && (
          <div style={{ background: 'rgba(255,184,0,0.07)', border: '1px solid rgba(255,184,0,0.2)', borderRadius: 12, padding: '14px 20px', marginBottom: 24, display: 'flex', alignItems: 'center', gap: 16 }}>
            <span style={{ fontSize: 20 }}>⚠️</span>
            <div style={{ flex: 1 }}>
              <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.warn, fontWeight: 600 }}>
                {expiringDocs.length} {expiringDocs.length === 1 ? 'document' : 'documents'} expiring soon
              </span>
              <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary, marginLeft: 12 }}>
                {expiringDocs.map(d => {
                  const days = daysUntil(d.expiry);
                  return `${d.name} (${days != null && days >= 0 ? `${days}d` : 'expired'})`;
                }).join(' · ')}
              </span>
            </div>
            <Btn variant="secondary" size="sm" onClick={() => toast(`Reviewing ${expiringDocs.length} expiring documents`)}>Review</Btn>
          </div>
        )}

        {/* Folder grid */}
        <div style={{ display: 'grid', gridTemplateColumns: isMobile ? 'repeat(2, 1fr)' : 'repeat(5, 1fr)', gap: isMobile ? 8 : 12, marginBottom: 24 }}>
          {folders.map(f => {
            const active = folderFilter === f.cat;
            return (
              <Card key={f.label} style={{
                padding: 20, textAlign: 'center', cursor: 'pointer',
                borderColor: active ? f.color : undefined,
                background: active ? `${f.color}11` : undefined,
                transition: `all ${TOKENS.anim.fast}`,
              }} onClick={() => setFolderFilter(active ? null : f.cat)}>
                <div style={{ fontSize: 32, marginBottom: 8 }}>{f.icon}</div>
                <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, fontWeight: 600, color: TOKENS.color.textPrimary, marginBottom: 6 }}>{f.label}</div>
                <span style={{ background: `${f.color}22`, color: f.color, fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, padding: '3px 8px', borderRadius: 999 }}>{folderCount(f.cat)} files</span>
              </Card>
            );
          })}
        </div>

        {/* Upload drop zone + Schengen row */}
        <div style={{ display: 'flex', gap: 12, marginBottom: 24, flexWrap: 'wrap' }}>
          {/* Hidden file input */}
          <input
            ref={fileInputRef}
            type="file"
            multiple
            accept=".pdf,.jpg,.jpeg,.png,.webp"
            style={{ display: 'none' }}
            onChange={handleFileInput}
          />
          <div
            onDragOver={e => { e.preventDefault(); setDragOver(true); }}
            onDragLeave={() => setDragOver(false)}
            onDrop={handleDrop}
            onClick={() => fileInputRef.current && fileInputRef.current.click()}
            style={{
              flex: 1, minWidth: 200, padding: '20px 18px', borderRadius: 14,
              border: `2px dashed ${dragOver ? TOKENS.color.accent : TOKENS.color.border}`,
              background: dragOver ? 'rgba(0,255,148,0.05)' : 'transparent',
              cursor: 'pointer', textAlign: 'center',
              transition: `all ${TOKENS.anim.fast}`,
            }}
          >
            <div style={{ fontSize: 28, marginBottom: 6 }}>📂</div>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, fontWeight: 600, color: dragOver ? TOKENS.color.accent : TOKENS.color.textPrimary, marginBottom: 2 }}>
              {dragOver ? 'Drop to upload' : 'Upload documents'}
            </div>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>
              Click or drag PDFs, images, e-tickets
            </div>
          </div>

          {/* Schengen tracker CTA */}
          <div
            onClick={() => setSchengenOpen(true)}
            style={{
              width: isMobile ? '100%' : 220, flexShrink: 0, padding: '20px 18px', borderRadius: 14,
              border: '1px solid rgba(124,77,255,0.3)', background: 'rgba(124,77,255,0.05)',
              cursor: 'pointer', textAlign: 'center',
              transition: `all ${TOKENS.anim.fast}`,
            }}
          >
            <div style={{ fontSize: 28, marginBottom: 6 }}>🇪🇺</div>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, fontWeight: 600, color: TOKENS.color.textPrimary, marginBottom: 2 }}>Schengen Tracker</div>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>90/180-day calculator</div>
          </div>
        </div>

        {/* Docs table */}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em' }}>
            {folderFilter ? folderFilter.toUpperCase() + ' DOCUMENTS' : 'ALL DOCUMENTS'} · {filteredDocs.length}
          </div>
          {folderFilter && (
            <button onClick={() => setFolderFilter(null)} style={{
              background: 'none', border: 'none', cursor: 'pointer',
              fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.accent, fontWeight: 500,
            }}>Clear filter ×</button>
          )}
        </div>
        <Card noPad>
          <div style={{ display: 'grid', gridTemplateColumns: '2fr 120px 80px 140px 80px 32px', padding: '10px 16px', borderBottom: '1px solid #26262E' }}>
            {['NAME', 'CATEGORY', 'COUNTRY', 'EXPIRY', 'STATUS', ''].map(h => (
              <div key={h} style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.06em' }}>{h}</div>
            ))}
          </div>
          {filteredDocs.length === 0 && (
            <EmptyState
              icon={<FolderIcon size={48} />}
              headline={`No ${folderFilter?.toLowerCase() || ''} documents`}
              sub="Upload one or clear the folder filter"
              cta="Clear filter"
              onCta={() => setFolderFilter(null)}
            />
          )}
          {filteredDocs.map((doc, i) => (
            <div key={i} onClick={() => { setSelectedDoc(i); onNavigate('doc-detail'); }} style={{
              display: 'grid', gridTemplateColumns: '2fr 120px 80px 140px 80px 32px',
              padding: '12px 16px', borderBottom: i < filteredDocs.length - 1 ? '1px solid #26262E' : 'none',
              cursor: 'pointer', alignItems: 'center', transition: `background ${TOKENS.anim.fast}`,
              background: hoveredDocIdx === i ? TOKENS.color.surface2 : 'transparent',
            }}
              onMouseEnter={() => setHoveredDocIdx(i)}
              onMouseLeave={() => setHoveredDocIdx(null)}
            >
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{ fontSize: 18 }}>{doc.icon}</span>
                <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, fontWeight: 600, color: TOKENS.color.textPrimary }}>{doc.name}</span>
              </div>
              <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>{doc.cat}</div>
              <div style={{ fontSize: 18 }}>{doc.country}</div>
              <MonoText size={13} color={doc.status === 'EXPIRED' ? TOKENS.color.error : doc.status === 'WARN' ? TOKENS.color.warn : TOKENS.color.textSecondary}>{expiryLabel(doc.expiry)}</MonoText>
              <div style={{ width: 8, height: 8, borderRadius: '50%', background: doc.status === 'EXPIRED' ? TOKENS.color.error : doc.status === 'WARN' ? TOKENS.color.warn : TOKENS.color.accent }} />
              <ChevronRightIcon size={14} />
            </div>
          ))}
        </Card>
      </div>
    </div>
    {schengenOpen && <SchengenModal onClose={() => setSchengenOpen(false)} />}
  );
}

// ── Schengen 90/180-day Calculator ────────────────────────────────────────────
function SchengenModal({ onClose }) {
  const WARN_DAYS = 90; // Schengen rule: max 90 days in any rolling 180-day window

  // Pre-populated example entries; user can add/remove
  const [entries, setEntries] = useLocalStorage('rmt:schengen:entries', [
    { id: 1, country: '🇵🇹 Portugal', entry: '2026-04-24', exit: '' },
    { id: 2, country: '🇪🇸 Spain',    entry: '2026-01-10', exit: '2026-01-28' },
    { id: 3, country: '🇩🇪 Germany',  entry: '2025-11-01', exit: '2025-11-14' },
  ]);
  const [newCountry, setNewCountry] = React.useState('');
  const [newEntry, setNewEntry] = React.useState('');
  const [newExit, setNewExit] = React.useState('');

  // Calculate days used in the rolling 180-day window ending today
  const calcDaysUsed = () => {
    const today = new Date();
    const windowStart = new Date(today);
    windowStart.setDate(windowStart.getDate() - 179);
    let total = 0;
    for (const e of entries) {
      const entry = new Date(e.entry);
      const exit = e.exit ? new Date(e.exit) : today;
      if (isNaN(entry) || isNaN(exit)) continue;
      const overlapStart = entry > windowStart ? entry : windowStart;
      const overlapEnd = exit < today ? exit : today;
      if (overlapEnd >= overlapStart) {
        total += Math.round((overlapEnd - overlapStart) / 86400000) + 1;
      }
    }
    return Math.min(total, 180);
  };
  const daysUsed = calcDaysUsed();
  const daysLeft = Math.max(0, WARN_DAYS - daysUsed);
  const pct = Math.min(100, (daysUsed / WARN_DAYS) * 100);
  const barColor = pct >= 100 ? TOKENS.color.error : pct >= 80 ? TOKENS.color.warn : TOKENS.color.accent;

  const addEntry = () => {
    if (!newCountry || !newEntry) return;
    setEntries(prev => [...prev, { id: Date.now(), country: newCountry, entry: newEntry, exit: newExit }]);
    setNewCountry(''); setNewEntry(''); setNewExit('');
  };
  const removeEntry = (id) => setEntries(prev => prev.filter(e => e.id !== id));

  return (
    <ModalShell title="Schengen Tracker" onClose={onClose} width={560}
      footer={<Btn variant="secondary" size="md" fullWidth onClick={onClose}>Done</Btn>}
    >
      {/* Days gauge */}
      <div style={{ marginBottom: 24 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8, alignItems: 'baseline' }}>
          <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em' }}>ROLLING 180-DAY WINDOW</span>
          <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 22, fontWeight: 600, color: barColor }}>{daysUsed} / {WARN_DAYS}</span>
        </div>
        <div style={{ height: 8, borderRadius: 999, background: TOKENS.color.surface2, overflow: 'hidden' }}>
          <div style={{ height: '100%', width: `${pct}%`, borderRadius: 999, background: barColor, transition: `width ${TOKENS.anim.base}` }} />
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 6 }}>
          <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>
            {daysLeft > 0 ? `${daysLeft} days remaining this window` : '⚠️ Schengen limit reached'}
          </span>
          <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textTertiary }}>Max 90 days / 180-day period</span>
        </div>
      </div>

      {/* Entry list */}
      <div style={{ marginBottom: 16 }}>
        {entries.map(e => (
          <div key={e.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0', borderBottom: '1px solid #26262E' }}>
            <div style={{ flex: 1 }}>
              <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: 600, color: TOKENS.color.textPrimary }}>{e.country}</span>
              <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 12, color: TOKENS.color.textSecondary, marginLeft: 8 }}>
                {e.entry} → {e.exit || 'present'}
              </span>
            </div>
            <button onClick={() => removeEntry(e.id)} aria-label="Remove" style={{ background: 'none', border: 'none', cursor: 'pointer', color: TOKENS.color.error, fontFamily: "'JetBrains Mono',monospace", fontSize: 14, fontWeight: 600 }}>×</button>
          </div>
        ))}
      </div>

      {/* Add new entry */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10, padding: '16px', background: TOKENS.color.surface2, borderRadius: 12 }}>
        <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em' }}>ADD ENTRY</div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <input value={newCountry} onChange={e => setNewCountry(e.target.value)} placeholder="Country / city" style={{ flex: 1, minWidth: 140, height: 36, background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 8, padding: '0 10px', fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textPrimary, outline: 'none' }} />
          <input type="date" value={newEntry} onChange={e => setNewEntry(e.target.value)} placeholder="Entry date" style={{ flex: 1, minWidth: 130, height: 36, background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 8, padding: '0 10px', fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textPrimary, outline: 'none', colorScheme: 'dark' }} />
          <input type="date" value={newExit} onChange={e => setNewExit(e.target.value)} placeholder="Exit date" style={{ flex: 1, minWidth: 130, height: 36, background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 8, padding: '0 10px', fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textPrimary, outline: 'none', colorScheme: 'dark' }} />
          <Btn variant="primary" size="sm" onClick={addEntry} disabled={!newCountry || !newEntry}>Add</Btn>
        </div>
        <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textTertiary }}>Leave exit date empty if you're currently in the Schengen area.</div>
      </div>
    </ModalShell>
  );
}

// ── Web Expenses ──────────────────────────────────────────────────────────────
function WebExpenses({ onNavigate }) {
  const vw = useViewportWidth();
  const isMobile = vw < 768;
  const [addOpen, setAddOpen] = React.useState(false);
  const [openRow, setOpenRow] = React.useState(null);
  const [hoveredExpIdx, setHoveredExpIdx] = React.useState(null);
  const [budget, setBudget] = useLocalStorage('rmt:expenses:budget', 2000);
  const [editingBudget, setEditingBudget] = React.useState(false);
  const [budgetInput, setBudgetInput] = React.useState('');
  const categories = [
    { label: 'Food', color: TOKENS.color.accent, pct: 32, amount: '€398' },
    { label: 'Transport', color: TOKENS.color.info, pct: 22, amount: '€274' },
    { label: 'Accommodation', color: TOKENS.color.purple, pct: 28, amount: '€349' },
    { label: 'Workspace', color: TOKENS.color.warn, pct: 11, amount: '€137' },
    { label: 'Other', color: TOKENS.color.error, pct: 7, amount: '€89' },
  ];
  const expenses = [
    { emoji: '🍽️', vendor: 'Time Out Market', cat: 'Food', amount: '€24.50', date: 'Today 13:22' },
    { emoji: '🚇', vendor: 'Metro Lisboa', cat: 'Transport', amount: '€1.60', date: 'Today 11:05' },
    { emoji: '💻', vendor: 'Hub Lisbon', cat: 'Workspace', amount: '€15.00', date: 'Yesterday' },
    { emoji: '🏡', vendor: 'Airbnb — Lisbon stay', cat: 'Accommodation', amount: '€840.00', date: 'May 2' },
    { emoji: '☕', vendor: 'Dear Breakfast', cat: 'Food', amount: '€6.80', date: 'May 3' },
    { emoji: '🚌', vendor: 'Flixbus', cat: 'Transport', amount: '€22.00', date: 'May 4' },
    { emoji: '🍕', vendor: 'Pizzeria Lisboa', cat: 'Food', amount: '€18.40', date: 'May 4' },
    { emoji: '🛍️', vendor: 'Zara — clothing', cat: 'Other', amount: '€89.00', date: 'May 5' },
  ];
  const conicStops = categories.reduce((acc, cat) => {
    const start = acc.total;
    const end = start + cat.pct;
    acc.stops.push(`${cat.color} ${start}% ${end}%`);
    acc.total = end;
    return acc;
  }, { stops: [], total: 0 }).stops;

  return (
    <>
    <div style={{ flex: 1, display: 'flex', flexDirection: isMobile ? 'column' : 'row', overflow: 'hidden' }}>
      {/* Left: totals + chart (top on mobile) */}
      <div style={{ width: isMobile ? '100%' : 320, borderRight: isMobile ? 'none' : '1px solid #26262E', borderBottom: isMobile ? '1px solid #26262E' : 'none', padding: isMobile ? '16px 14px' : '28px 24px', display: 'flex', flexDirection: 'column', gap: isMobile ? 14 : 24, overflowY: 'auto', flexShrink: 0 }}>
        <div>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 6 }}>🇵🇹 LISBON · MAY 2025</div>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 40, fontWeight: 600, color: TOKENS.color.textPrimary, lineHeight: 1, marginBottom: 4 }}>€1,247</div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>12 expenses · 8 days · ~€155/day</div>
        </div>

        {/* Budget vs Actual */}
        {(() => {
          const actual = 1247;
          const pct = Math.min(100, (actual / budget) * 100);
          const over = actual > budget;
          const barColor = pct >= 100 ? TOKENS.color.error : pct >= 80 ? TOKENS.color.warn : TOKENS.color.accent;
          return (
            <div style={{ background: TOKENS.color.surface2, borderRadius: 12, padding: '14px 16px' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
                <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em' }}>BUDGET</span>
                {editingBudget ? (
                  <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
                    <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textSecondary }}>€</span>
                    <input
                      autoFocus
                      type="number"
                      value={budgetInput}
                      onChange={e => setBudgetInput(e.target.value)}
                      onBlur={() => { const v = parseInt(budgetInput); if (!isNaN(v) && v > 0) setBudget(v); setEditingBudget(false); }}
                      onKeyDown={e => { if (e.key === 'Enter') e.target.blur(); if (e.key === 'Escape') setEditingBudget(false); }}
                      style={{ width: 72, background: TOKENS.color.surface, border: `1px solid ${TOKENS.color.accent}`, borderRadius: 6, padding: '2px 6px', fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textPrimary, outline: 'none' }}
                    />
                  </div>
                ) : (
                  <button onClick={() => { setBudgetInput(String(budget)); setEditingBudget(true); }} style={{ background: 'none', border: 'none', cursor: 'pointer', fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textSecondary, textDecoration: 'underline dotted' }}>
                    €{budget.toLocaleString()}
                  </button>
                )}
              </div>
              <div style={{ height: 6, borderRadius: 999, background: TOKENS.color.surface, overflow: 'hidden', marginBottom: 8 }}>
                <div style={{ height: '100%', width: `${pct}%`, borderRadius: 999, background: barColor, transition: `width ${TOKENS.anim.base}` }} />
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: over ? TOKENS.color.error : TOKENS.color.textSecondary }}>
                  {over ? `€${(actual - budget).toLocaleString()} over budget` : `€${(budget - actual).toLocaleString()} remaining`}
                </span>
                <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 12, color: barColor }}>{Math.round(pct)}%</span>
              </div>
            </div>
          );
        })()}

        {/* Donut */}
        <div style={{ display: 'flex', justifyContent: 'center' }}>
          <div style={{ width: 160, height: 160, borderRadius: '50%', background: `conic-gradient(${conicStops.join(', ')})`, position: 'relative' }}>
            <div style={{ position: 'absolute', inset: 30, borderRadius: '50%', background: TOKENS.color.bg, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
              <MonoText size={11} color="#5C5C66">TOTAL</MonoText>
            </div>
          </div>
        </div>

        {/* Category breakdown */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {categories.map(cat => (
            <div key={cat.label} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <div style={{ width: 8, height: 8, borderRadius: '50%', background: cat.color }} />
                <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>{cat.label}</span>
              </div>
              <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
                <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textTertiary }}>{cat.pct}%</span>
                <MonoText size={13} weight={600} color={cat.color}>{cat.amount}</MonoText>
              </div>
            </div>
          ))}
        </div>

        <div style={{ display: 'flex', gap: 8 }}>
          <Btn variant="primary" size="sm" fullWidth onClick={() => onNavigate('currency')}>Convert</Btn>
          <Btn variant="secondary" size="sm" fullWidth onClick={() => toast('Exporting expenses to CSV…')}>Export</Btn>
        </div>
      </div>

      {/* Right: expense table */}
      <div style={{ flex: 1, overflowY: 'auto', padding: isMobile ? '16px 14px' : '28px 32px' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 20 }}>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em' }}>ALL EXPENSES</div>
          <Btn variant="primary" size="sm" icon={<PlusIcon size={14} color="#0A0A0B" />} onClick={() => setAddOpen(true)}>Add expense</Btn>
        </div>
        <Card noPad>
          <div style={{ display: 'grid', gridTemplateColumns: '2fr 140px auto 120px 80px', padding: '10px 16px', borderBottom: '1px solid #26262E' }}>
            {['VENDOR', 'CATEGORY', 'AMOUNT', 'DATE', ''].map((h, i) => (
              <div key={i} style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.06em' }}>{h}</div>
            ))}
          </div>
          {expenses.length === 0 && (
            <EmptyState
              icon={<DollarIcon size={48} />}
              headline="No expenses tracked"
              sub="Add your first expense to start tracking spend"
              cta="Add expense"
              onCta={() => setAddOpen(true)}
            />
          )}
          {expenses.map((exp, i) => (
            <div key={i} style={{
              display: 'grid', gridTemplateColumns: '2fr 140px auto 120px 80px',
              padding: '13px 16px', borderBottom: i < expenses.length - 1 ? '1px solid #26262E' : 'none',
              alignItems: 'center', cursor: 'pointer', transition: `background ${TOKENS.anim.fast}`,
              background: (openRow === i || hoveredExpIdx === i) ? TOKENS.color.surface2 : 'transparent',
            }}
              onClick={() => setOpenRow(openRow === i ? null : i)}
              onMouseEnter={() => setHoveredExpIdx(i)}
              onMouseLeave={() => setHoveredExpIdx(null)}
            >
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <div style={{ width: 32, height: 32, borderRadius: 8, background: TOKENS.color.surface2, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16 }}>{exp.emoji}</div>
                <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, fontWeight: 600, color: TOKENS.color.textPrimary }}>{exp.vendor}</span>
              </div>
              <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>{exp.cat}</span>
              <MonoText size={14} weight={600}>{exp.amount}</MonoText>
              <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textTertiary }}>{exp.date}</span>
              <div style={{ display: 'flex', gap: 6, justifyContent: 'flex-end' }} onClick={e => e.stopPropagation()}>
                <button onClick={() => { toast(`Editing ${exp.vendor}`); setOpenRow(null); }} aria-label="Edit" style={{ width: 28, height: 28, borderRadius: 6, background: TOKENS.color.surface2, border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', color: TOKENS.color.textSecondary, fontFamily: "'JetBrains Mono',monospace", fontSize: 11 }}>✎</button>
                <button onClick={() => { toast(`Deleted ${exp.vendor}`, 'error'); setOpenRow(null); }} aria-label="Delete" style={{ width: 28, height: 28, borderRadius: 6, background: TOKENS.color.surface2, border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', color: TOKENS.color.error, fontFamily: "'JetBrains Mono',monospace", fontSize: 12, fontWeight: 600 }}>×</button>
              </div>
            </div>
          ))}
        </Card>
      </div>
    </div>
    {addOpen && <AddExpenseModal onClose={() => setAddOpen(false)} />}
    </>
  );
}

// ── Web Profile / Settings ─────────────────────────────────────────────────────
function WebProfile() {
  const [notifs, setNotifs] = React.useState({ flights: true, workBlocks: true, docs: false, nearby: false });
  const [cal, setCal] = React.useState({ apple: true, google: false });
  const [modal, setModal] = React.useState(null); // 'edit' | 'password' | '2fa' | 'signout' | 'delete' | null
  const [hoveredSettingKey, setHoveredSettingKey] = React.useState(null);

  return (
    <div style={{ flex: 1, overflowY: 'auto', padding: '28px 32px' }}>
      <div style={{ maxWidth: 700 }}>
        {/* Profile header */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 24, marginBottom: 40, padding: '24px 28px', background: TOKENS.color.surface, borderRadius: 16, border: '1px solid #26262E' }}>
          <Avatar size={72} initials="AL" color="#00FF94" />
          <div style={{ flex: 1 }}>
            <h2 style={{ fontFamily: "'Inter',sans-serif", fontSize: 22, fontWeight: 700, color: TOKENS.color.textPrimary, margin: '0 0 4px' }}>Alex Lawson</h2>
            <p style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textSecondary, margin: 0 }}>alex@rmtctrl.app · Sydney, Australia 🇦🇺</p>
          </div>
          <Btn variant="secondary" size="sm" onClick={() => setModal('edit')}>Edit profile</Btn>
        </div>

        {/* Settings sections */}
        {[
          {
            header: 'PREFERENCES',
            rows: [
              { label: 'Home base', val: '🇦🇺 Sydney, Australia' },
              { label: 'Home currency', val: 'AUD A$' },
              { label: 'Work preferences', val: 'Quiet · Cowork · Fast Wi-Fi', link: true },
            ],
          },
          {
            header: 'NOTIFICATIONS',
            rows: [
              { label: 'Flight alerts', toggle: true, key: 'flights', state: notifs, setter: setNotifs },
              { label: 'Work block reminders', toggle: true, key: 'workBlocks', state: notifs, setter: setNotifs },
              { label: 'Document expiry alerts', toggle: true, key: 'docs', state: notifs, setter: setNotifs },
              { label: 'Nearby workspaces', toggle: true, key: 'nearby', state: notifs, setter: setNotifs },
            ],
          },
          {
            header: 'CALENDAR SYNC',
            rows: [
              { label: 'Apple Calendar', toggle: true, key: 'apple', state: cal, setter: setCal },
              { label: 'Google Calendar', toggle: true, key: 'google', state: cal, setter: setCal },
            ],
          },
          {
            header: 'ACCOUNT',
            rows: [
              { label: 'Change password', link: true, action: 'password' },
              { label: 'Two-factor authentication', val: 'Enabled', link: true, action: '2fa' },
              { label: 'Sign out', link: true, action: 'signout' },
              { label: 'Delete account', danger: true, action: 'delete' },
            ],
          },
        ].map(section => (
          <div key={section.header} style={{ marginBottom: 32 }}>
            <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 10 }}>{section.header}</div>
            <Card noPad>
              {section.rows.map((row, i) => {
                const clickable = !!row.action;
                const settingKey = `${section.header}:${i}`;
                return (
                  <div
                    key={i}
                    onClick={clickable ? () => setModal(row.action) : undefined}
                    onMouseEnter={clickable ? () => setHoveredSettingKey(settingKey) : undefined}
                    onMouseLeave={clickable ? () => setHoveredSettingKey(null) : undefined}
                    style={{
                      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                      height: 52, padding: '0 20px',
                      borderBottom: i < section.rows.length - 1 ? '1px solid #26262E' : 'none',
                      cursor: clickable ? 'pointer' : 'default',
                      background: (clickable && hoveredSettingKey === settingKey) ? TOKENS.color.surface2 : 'transparent',
                      transition: `background ${TOKENS.anim.fast}`,
                    }}
                  >
                    <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 15, color: row.danger ? TOKENS.color.error : TOKENS.color.textPrimary }}>{row.label}</span>
                    {row.toggle ? (
                      <Toggle on={row.state[row.key]} onChange={v => row.setter(p => ({ ...p, [row.key]: v }))} />
                    ) : row.val ? (
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textSecondary }}>{row.val}</span>
                        {row.link && <ChevronRightIcon size={16} />}
                      </div>
                    ) : (
                      <ChevronRightIcon size={16} color={row.danger ? TOKENS.color.error : TOKENS.color.textTertiary} />
                    )}
                  </div>
                );
              })}
            </Card>
          </div>
        ))}
      </div>

      {/* Modals */}
      {modal === 'edit'     && <EditProfileModal    onClose={() => setModal(null)} />}
      {modal === 'password' && <ChangePasswordModal onClose={() => setModal(null)} />}
      {modal === '2fa'      && <TwoFAModal          onClose={() => setModal(null)} />}
      {modal === 'signout'  && <SignOutModal        onClose={() => setModal(null)} />}
      {modal === 'delete'   && <DeleteAccountModal  onClose={() => setModal(null)} />}
    </div>
  );
}

Object.assign(window, { WebDashboard, WebTrips, WebWorkspaces, WebVault, WebExpenses, WebProfile });
