
// RMTCTRL — Web App Root (sidebar + router)

const WEB_TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "dashboardState": "mid-trip",
  "accentColor": "#00FF94"
}/*EDITMODE-END*/;

// ── Sidebar nav item ─────────────────────────────────────────────────────────
function SideNavItem({ icon, label, active, badge, onClick, danger }) {
  const [hovered, setHovered] = React.useState(false);
  const Icon = icon;
  return (
    <button
      onClick={onClick}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
      style={{
        width: '100%', height: 38, display: 'flex', alignItems: 'center', gap: 10,
        padding: '0 12px', borderRadius: 8, border: 'none', cursor: 'pointer',
        background: active ? 'rgba(0,255,148,0.1)' : hovered ? TOKENS.color.surface2 : 'transparent',
        transition: `background ${TOKENS.anim.fast}`,
        position: 'relative',
      }}
    >
      {active && (
        <div style={{ position: 'absolute', left: 0, top: '20%', bottom: '20%', width: 2, background: TOKENS.color.accent, borderRadius: 999 }} />
      )}
      <Icon size={16} color={active ? TOKENS.color.accent : danger ? TOKENS.color.error : hovered ? TOKENS.color.textPrimary : TOKENS.color.textSecondary} fill={active ? TOKENS.color.accent : 'none'} />
      <span style={{
        fontFamily: "'Inter',sans-serif", fontSize: 14, fontWeight: active ? 600 : 400,
        color: active ? TOKENS.color.textPrimary : danger ? TOKENS.color.error : hovered ? TOKENS.color.textPrimary : TOKENS.color.textSecondary,
        flex: 1, textAlign: 'left',
      }}>{label}</span>
      {badge && (
        <span style={{ background: TOKENS.color.accentBadge, color: TOKENS.color.accent, fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, padding: '2px 6px', borderRadius: 999 }}>{badge}</span>
      )}
    </button>
  );
}

// ── Top bar ───────────────────────────────────────────────────────────────────
function TopBar({ title, subtitle, actions, breadcrumb, isMobile, onMenu }) {
  return (
    <div style={{
      height: 56, flexShrink: 0, display: 'flex', alignItems: 'center',
      padding: isMobile ? '0 16px' : '0 28px', borderBottom: '1px solid #26262E', gap: 12,
      background: TOKENS.color.bg,
    }}>
      {isMobile && (
        <button onClick={onMenu} aria-label="Open menu" style={{
          width: 36, height: 36, borderRadius: 8, background: 'none', border: 'none', cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
        }}>
          <MenuIcon size={20} color={TOKENS.color.textPrimary} />
        </button>
      )}
      {breadcrumb && !isMobile && (
        <>
          <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textTertiary }}>{breadcrumb}</span>
          <ChevronRightIcon size={14} color="#5C5C66" />
        </>
      )}
      <div style={{ flex: 1, minWidth: 0 }}>
        <h1 style={{ fontFamily: "'Inter',sans-serif", fontSize: 16, fontWeight: 600, color: TOKENS.color.textPrimary, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', margin: 0 }}>{title}</h1>
        {subtitle && !isMobile && <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary }}>{subtitle}</div>}
      </div>
      {actions}
    </div>
  );
}

// ── Command bar modal ─────────────────────────────────────────────────────────
function CommandBar({ onClose, onNavigate }) {
  const [q, setQ] = React.useState('');
  const [selectedIndex, setSelectedIndex] = React.useState(0);
  const listRef = React.useRef(null);
  const inputId = 'cmd-input';
  const listId = 'cmd-results';

  const items = [
    { label: 'Dashboard', id: 'home', icon: CompassIcon },
    { label: 'Trips', id: 'trips', icon: RouteIcon },
    { label: 'Accommodation', id: 'accommodation', icon: BedIcon },
    { label: 'Workspace Map', id: 'workspaces', icon: MapPinIcon },
    { label: 'Vault', id: 'vault', icon: FolderIcon },
    { label: 'Expenses', id: 'expenses', icon: DollarIcon },
    { label: "What's On — Nomad Events", id: 'whatson', icon: CalendarIcon },
    { label: 'Remote Jobs Board', id: 'jobs', icon: BriefcaseIcon },
    { label: 'Location Info', id: 'location', icon: GlobeIcon },
    { label: 'Currency Converter', id: 'currency', icon: DollarIcon },
    { label: 'Profile & Settings', id: 'profile', icon: UserIcon },
    { label: 'Submit a Workspace', id: 'add-workspace', icon: PlusIcon },
  ];
  const filtered = items.filter(i => i.label.toLowerCase().includes(q.toLowerCase()));

  // Reset selection when query changes
  React.useEffect(() => { setSelectedIndex(0); }, [q]);

  const handleKeyDown = (e) => {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setSelectedIndex(i => Math.min(i + 1, filtered.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setSelectedIndex(i => Math.max(i - 1, 0));
    } else if (e.key === 'Enter') {
      e.preventDefault();
      if (filtered[selectedIndex]) { onNavigate(filtered[selectedIndex].id); onClose(); }
    } else if (e.key === 'Escape') {
      onClose();
    }
  };

  // Scroll selected item into view
  React.useEffect(() => {
    if (!listRef.current) return;
    const selected = listRef.current.querySelector('[aria-selected="true"]');
    if (selected) selected.scrollIntoView({ block: 'nearest' });
  }, [selectedIndex]);

  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-label="Command palette"
      style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.7)', zIndex: 1000, display: 'flex', alignItems: 'flex-start', justifyContent: 'center', paddingTop: '15vh' }}
      onClick={onClose}
    >
      <div style={{ width: 560, background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 16, overflow: 'hidden', boxShadow: '0 24px 60px rgba(0,0,0,0.6)' }} onClick={e => e.stopPropagation()}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '14px 16px', borderBottom: '1px solid #26262E' }}>
          <SearchIcon size={18} color={TOKENS.color.textSecondary} />
          <input
            id={inputId}
            autoFocus
            value={q}
            onChange={e => setQ(e.target.value)}
            onKeyDown={handleKeyDown}
            placeholder="Search screens, actions..."
            role="combobox"
            aria-expanded={filtered.length > 0}
            aria-haspopup="listbox"
            aria-controls={listId}
            aria-activedescendant={filtered[selectedIndex] ? `cmd-opt-${selectedIndex}` : undefined}
            aria-autocomplete="list"
            style={{ flex: 1, background: 'none', border: 'none', outline: 'none', fontFamily: "'Inter',sans-serif", fontSize: 16, color: TOKENS.color.textPrimary }}
          />
          <kbd style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, color: TOKENS.color.textTertiary, background: TOKENS.color.border, padding: '3px 6px', borderRadius: 4 }}>ESC</kbd>
        </div>
        <div id={listId} role="listbox" aria-label="Results" ref={listRef} style={{ maxHeight: 360, overflowY: 'auto' }}>
          {filtered.map((item, i) => {
            const Icon = item.icon;
            const isSelected = i === selectedIndex;
            return (
              <div
                key={i}
                id={`cmd-opt-${i}`}
                role="option"
                aria-selected={isSelected}
                onClick={() => { onNavigate(item.id); onClose(); }}
                style={{
                  width: '100%', display: 'flex', alignItems: 'center', gap: 12, padding: '12px 16px',
                  borderBottom: '1px solid #26262E', cursor: 'pointer',
                  background: isSelected ? TOKENS.color.surface2 : 'transparent',
                  transition: `background ${TOKENS.anim.fast}`,
                }}
                onMouseEnter={() => setSelectedIndex(i)}
              >
                <div style={{ width: 28, height: 28, borderRadius: 7, background: isSelected ? TOKENS.color.border : TOKENS.color.surface2, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <Icon size={14} color={isSelected ? TOKENS.color.textPrimary : TOKENS.color.textSecondary} />
                </div>
                <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: isSelected ? TOKENS.color.textPrimary : TOKENS.color.textSecondary }}>{item.label}</span>
              </div>
            );
          })}
          {filtered.length === 0 && (
            <div style={{ padding: '24px 16px', textAlign: 'center', fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textTertiary }}>
              No results for "{q}"
            </div>
          )}
        </div>
        <div style={{ padding: '10px 16px', display: 'flex', gap: 16 }}>
          {[['↑↓', 'Navigate'], ['↵', 'Open'], ['Esc', 'Close']].map(([key, desc]) => (
            <div key={key} style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
              <kbd style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: TOKENS.color.textTertiary, background: TOKENS.color.surface2, padding: '2px 5px', borderRadius: 3 }}>{key}</kbd>
              <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textTertiary }}>{desc}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ── Add Segment Modal (web) ───────────────────────────────────────────────────
// Date formatting helpers — used throughout modals to avoid hardcoded strings
const FMT_DATE = { day: '2-digit', month: 'short', year: 'numeric' };
const FMT_MONTH_DAY = { day: '2-digit', month: 'short' };
const FMT_TIME = { hour: '2-digit', minute: '2-digit', hour12: false };
function fmtDate(date, fmt = FMT_DATE) { return date.toLocaleDateString('en-GB', fmt).toUpperCase(); }
function fmtTime(date) { return date.toLocaleTimeString('en-GB', FMT_TIME); }
function addDays(date, n) { const d = new Date(date); d.setDate(d.getDate() + n); return d; }

function WebAddSegment({ onClose }) {
  const today = React.useMemo(() => new Date(), []);
  const [segType, setSegType] = React.useState('flight');
  const [flightNum, setFlightNum] = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const [found, setFound] = React.useState(false);
  const [notFound, setNotFound] = React.useState(false);
  const [hotelName, setHotelName] = React.useState('');
  const [hotelAddress, setHotelAddress] = React.useState('');
  const [workTitle, setWorkTitle] = React.useState('');
  const [workLocation, setWorkLocation] = React.useState('');
  const [otherTitle, setOtherTitle] = React.useState('');
  const [otherLocation, setOtherLocation] = React.useState('');
  const tabs = ['flight', 'hotel', 'work block', 'other'];

  const summary = () => {
    if (segType === 'flight')     return flightNum    ? `Flight ${flightNum}` : 'Flight';
    if (segType === 'hotel')      return hotelName    ? `Hotel ${hotelName}` : 'Hotel';
    if (segType === 'work block') return workTitle    ? `Work block ${workTitle}` : 'Work block';
    return otherTitle ? `Segment ${otherTitle}` : 'Segment';
  };
  const canSave = () => (
    (segType === 'flight'     && !!found) ||
    (segType === 'hotel'      && !!hotelName) ||
    (segType === 'work block' && !!workTitle) ||
    (segType === 'other'      && !!otherTitle)
  );
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.7)', zIndex: 999, display: 'flex', alignItems: 'center', justifyContent: 'center' }} onClick={onClose}>
      <div style={{ width: 560, background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 20, overflow: 'hidden', boxShadow: '0 24px 60px rgba(0,0,0,0.6)' }} onClick={e => e.stopPropagation()}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '20px 24px', borderBottom: '1px solid #26262E' }}>
          <h2 style={{ fontFamily: "'Inter',sans-serif", fontSize: 18, fontWeight: 700, color: TOKENS.color.textPrimary, margin: 0 }}>Add segment</h2>
          <button onClick={onClose} style={{ width: 32, height: 32, borderRadius: 8, background: TOKENS.color.surface2, border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <XIcon size={16} />
          </button>
        </div>
        {/* Type tabs */}
        <div style={{ display: 'flex', background: TOKENS.color.bg, padding: '12px 24px', gap: 6 }}>
          {tabs.map(t => (
            <button key={t} onClick={() => { setSegType(t); setFound(false); setNotFound(false); }} style={{
              height: 34, padding: '0 14px', borderRadius: 8, border: 'none', cursor: 'pointer',
              background: segType === t ? TOKENS.color.surface2 : 'transparent',
              fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: segType === t ? 600 : 400,
              color: segType === t ? TOKENS.color.textPrimary : TOKENS.color.textTertiary, textTransform: 'capitalize', transition: `all ${TOKENS.anim.fast}`,
            }}>{t}</button>
          ))}
        </div>
        <div style={{ padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 14 }}>
          {segType === 'flight' && (
            <>
              <Input label="Flight number" value={flightNum} onChange={setFlightNum} mono helper="e.g. QF1, BA267, EK401" />
              <div style={{ display: 'flex', gap: 12 }}>
                <div style={{ flex: 1, background: TOKENS.color.surface2, borderRadius: 12, border: '1.5px solid #26262E', padding: '12px 16px' }}>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary, marginBottom: 4 }}>Date</div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 15, color: TOKENS.color.textPrimary }}>{fmtDate(today)}</div>
                </div>
                <Btn variant="primary" size="md" onClick={() => {
                  setLoading(true); setFound(false); setNotFound(false);
                  setTimeout(() => {
                    setLoading(false);
                    // Simulate: valid IATA format (2-letter code + digits) → found; otherwise → not found
                    if (/^[A-Z]{1,2}\d{1,4}$/i.test(flightNum.trim())) { setFound(true); } else { setNotFound(true); }
                  }, 1400);
                }} disabled={!flightNum}>
                  {loading ? 'Searching…' : 'Find flight'}
                </Btn>
              </div>
              {loading && (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  <Skeleton height={20} width="60%" />
                  <Skeleton height={80} />
                </div>
              )}
              {notFound && !loading && (
                <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10, padding: '24px 16px', background: TOKENS.color.surface, borderRadius: 12, border: '1px solid #26262E', textAlign: 'center' }}>
                  <span style={{ fontSize: 36 }}>✈️</span>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 15, fontWeight: 600, color: TOKENS.color.textPrimary }}>Flight not found</div>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>Check the flight number and try again — e.g. QF1, BA267, EK401</div>
                  <Btn variant="ghost" size="sm" onClick={() => { setNotFound(false); setFlightNum(''); }}>Clear</Btn>
                </div>
              )}
              {found && (
                <Card style={{ padding: 16 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 12 }}>
                    <StatusPill label="ON TIME" />
                    <MonoText size={11} color="#9999A1">QANTAS · QF1</MonoText>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
                    <div style={{ textAlign: 'center' }}><MonoText size={22} weight={600}>SYD</MonoText><div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary }}>Sydney</div></div>
                    <div style={{ flex: 1, height: 1, background: TOKENS.color.border, position: 'relative' }}>
                      <div style={{ position: 'absolute', top: -6, left: '50%', transform: 'translateX(-50%)' }}><PlaneIcon size={14} color="#00FF94" /></div>
                    </div>
                    <div style={{ textAlign: 'center' }}><MonoText size={22} weight={600}>LIS</MonoText><div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary }}>Lisbon</div></div>
                  </div>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 10 }}>
                    <MonoText size={13} color="#9999A1">06:00 → 21:30</MonoText>
                    <MonoText size={13} color="#9999A1">GATE B12</MonoText>
                  </div>
                </Card>
              )}
            </>
          )}
          {segType === 'hotel' && (
            <>
              <Input label="Hotel name" value={hotelName} onChange={setHotelName} />
              <Input label="Address" value={hotelAddress} onChange={setHotelAddress} helper="Autocomplete" />
              <div style={{ display: 'flex', gap: 12 }}>
                <div style={{ flex: 1, background: TOKENS.color.surface2, borderRadius: 12, border: '1.5px solid #26262E', padding: '12px 16px' }}>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary, marginBottom: 4 }}>Check-in</div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textPrimary }}>{fmtDate(today, FMT_MONTH_DAY)}</div>
                </div>
                <div style={{ flex: 1, background: TOKENS.color.surface2, borderRadius: 12, border: '1.5px solid #26262E', padding: '12px 16px' }}>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary, marginBottom: 4 }}>Check-out</div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textPrimary }}>{fmtDate(addDays(today, 14), FMT_MONTH_DAY)}</div>
                </div>
              </div>
            </>
          )}
          {segType === 'work block' && (
            <>
              <Input label="Title" value={workTitle} onChange={setWorkTitle} helper="e.g. Deep work · Client call" />
              <Input label="Workspace / location" value={workLocation} onChange={setWorkLocation} />
              <div style={{ display: 'flex', gap: 12 }}>
                <div style={{ flex: 1, background: TOKENS.color.surface2, borderRadius: 12, border: '1.5px solid #26262E', padding: '12px 16px' }}>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary, marginBottom: 4 }}>Start</div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textPrimary }}>09:00</div>
                </div>
                <div style={{ flex: 1, background: TOKENS.color.surface2, borderRadius: 12, border: '1.5px solid #26262E', padding: '12px 16px' }}>
                  <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary, marginBottom: 4 }}>End</div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textPrimary }}>13:00</div>
                </div>
              </div>
            </>
          )}
          {segType === 'other' && (
            <>
              <Input label="Title" value={otherTitle} onChange={setOtherTitle} />
              <Input label="Location" value={otherLocation} onChange={setOtherLocation} />
            </>
          )}
        </div>
        <div style={{ padding: '0 24px 24px', display: 'flex', gap: 10 }}>
          <Btn variant="primary" size="md" fullWidth disabled={!canSave()} onClick={() => { toast(`${summary()} added to trip`); onClose(); }}>Save segment</Btn>
          <Btn variant="secondary" size="md" onClick={onClose}>Cancel</Btn>
        </div>
      </div>
    </div>
  );
}

// ── Slide-out detail panel ─────────────────────────────────────────────────────
function SlideOutPanel({ isOpen, onClose, children, width, title }) {
  const vw = useViewportWidth();
  const isMobile = vw < 768;
  const panelRef = React.useRef(null);
  const titleId = React.useId ? React.useId() : 'slideout-title';

  React.useEffect(() => {
    if (!isOpen) return;
    // Focus first focusable element on open
    const panel = panelRef.current;
    if (panel) {
      const focusable = panel.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
      if (focusable.length) focusable[0].focus();
    }
    const onKey = (e) => {
      if (e.key === 'Escape') { e.stopPropagation(); onClose(); return; }
      // Focus trap
      if (e.key === 'Tab' && panel) {
        const focusable = Array.from(panel.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'));
        const first = focusable[0], last = focusable[focusable.length - 1];
        if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
        else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [isOpen, onClose]);

  if (!isOpen) return null;
  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 50, display: 'flex' }}>
      {!isMobile && (
        <div onClick={onClose} aria-hidden="true" style={{ flex: 1, background: 'rgba(0,0,0,0.5)', cursor: 'pointer', animation: `slideout-fade ${TOKENS.anim.fast} ease` }} />
      )}
      <div
        ref={panelRef}
        role="dialog"
        aria-modal="true"
        aria-label={title || 'Detail panel'}
        style={{
          width: isMobile ? '100%' : (width || 'min(720px, 60%)'),
          minWidth: isMobile ? 0 : 480,
          background: TOKENS.color.surfacePanel,
          borderLeft: isMobile ? 'none' : '1px solid #26262E',
          overflowY: 'auto',
          boxShadow: isMobile ? 'none' : '-12px 0 40px rgba(0,0,0,0.5)',
          animation: `slideout-slide ${TOKENS.anim.base} cubic-bezier(0.2, 0.8, 0.2, 1)`,
          display: 'flex',
          flexDirection: 'column',
        }}
      >
        {children}
      </div>
    </div>
  );
}

// ── Detail hero (gradient banner used in slide-out details) ───────────────────
function DetailHero({ color, emoji, height = 220, fontSize = 80, gradientEnd = '55', badge, onClose }) {
  return (
    <div style={{ height, background: `linear-gradient(135deg, ${color}22, ${color}${gradientEnd})`, position: 'relative', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize }}>
      {emoji}
      <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to bottom, transparent 40%, #0F0F12 100%)' }} />
      {badge && (
        <span style={{ position: 'absolute', top: 16, left: 16, background: `${color}cc`, color: TOKENS.color.bg, fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 700, padding: '4px 10px', borderRadius: 999 }}>{badge}</span>
      )}
      <button onClick={onClose} style={{ position: 'absolute', top: 16, right: 16, width: 32, height: 32, borderRadius: '50%', background: 'rgba(0,0,0,0.55)', border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <XIcon size={14} />
      </button>
    </div>
  );
}

// ── Detail stats grid (3-4 stat cards in a row) ───────────────────────────────
function DetailStatsGrid({ stats, valueSize = 13 }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: `repeat(${stats.length}, 1fr)`, gap: 8 }}>
      {stats.map(s => (
        <Card key={s.label} style={{ padding: '10px 12px', textAlign: 'center' }}>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 9, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 3 }}>{s.label}</div>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: valueSize, fontWeight: 600, color: s.color }}>{s.val}</div>
        </Card>
      ))}
    </div>
  );
}

// ── Tabs (used inside slide-out detail) ────────────────────────────────────────
function DetailTabs({ tabs, active, onChange }) {
  return (
    <div style={{ display: 'flex', gap: 4, borderBottom: '1px solid #26262E', padding: '0 24px', flexShrink: 0 }}>
      {tabs.map(t => (
        <button key={t} onClick={() => onChange(t)} style={{
          height: 42, padding: '0 14px', background: 'none', border: 'none', cursor: 'pointer',
          fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: active === t ? 600 : 500,
          color: active === t ? TOKENS.color.accent : TOKENS.color.textSecondary,
          borderBottom: active === t ? '2px solid #00FF94' : '2px solid transparent',
          marginBottom: -1, transition: `all ${TOKENS.anim.fast}`,
        }}>{t}</button>
      ))}
    </div>
  );
}

// ── Toast system ───────────────────────────────────────────────────────────────
function toast(message, type = 'success') {
  if (typeof window !== 'undefined' && window.__rmtToast) window.__rmtToast(message, type);
}

function Toast({ message, type, onDismiss }) {
  React.useEffect(() => {
    const t = setTimeout(onDismiss, 3000);
    return () => clearTimeout(t);
  }, [onDismiss]);
  const accent = type === 'error' ? TOKENS.color.error : TOKENS.color.accent;
  return (
    <div style={{
      background: TOKENS.color.surface, border: `1px solid ${accent}66`, borderLeft: `3px solid ${accent}`,
      borderRadius: 10, padding: '12px 16px', minWidth: 260, maxWidth: 380,
      display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer',
      boxShadow: '0 12px 30px rgba(0,0,0,0.5)',
    }} onClick={onDismiss}>
      <CheckIcon size={14} color={accent} />
      <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textPrimary, flex: 1 }}>{message}</span>
    </div>
  );
}

function ToastContainer() {
  const [toasts, setToasts] = React.useState([]);
  React.useEffect(() => {
    window.__rmtToast = (message, type = 'success') => {
      setToasts(prev => [...prev, { id: Date.now() + Math.random(), message, type }]);
    };
    return () => { delete window.__rmtToast; };
  }, []);
  return (
    <div style={{ position: 'fixed', bottom: 24, right: 24, zIndex: 1100, display: 'flex', flexDirection: 'column-reverse', gap: 10 }}>
      {toasts.map(t => (
        <Toast key={t.id} message={t.message} type={t.type} onDismiss={() => setToasts(prev => prev.filter(x => x.id !== t.id))} />
      ))}
    </div>
  );
}

// ── Modal shell ────────────────────────────────────────────────────────────────
function ModalShell({ title, onClose, children, width = 480, footer }) {
  const modalRef = React.useRef(null);
  const titleId = `modal-title-${title ? title.replace(/\s+/g, '-').toLowerCase() : 'dlg'}`;

  React.useEffect(() => {
    const modal = modalRef.current;
    // Focus the modal container on mount so keyboard users are placed correctly
    if (modal) modal.focus();

    const onKey = (e) => {
      if (e.key === 'Escape') { e.stopPropagation(); onClose(); return; }
      // Focus trap — keep Tab cycling within the modal
      if (e.key === 'Tab' && modal) {
        const focusable = Array.from(modal.querySelectorAll(
          'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
        ));
        if (!focusable.length) return;
        const first = focusable[0], last = focusable[focusable.length - 1];
        if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
        else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);

  return (
    <div
      style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.7)', zIndex: 999, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
      onClick={onClose}
      aria-hidden="false"
    >
      <div
        ref={modalRef}
        role="dialog"
        aria-modal="true"
        aria-labelledby={titleId}
        tabIndex={-1}
        style={{ width, maxHeight: '90vh', display: 'flex', flexDirection: 'column', background: TOKENS.color.surface, border: '1px solid #26262E', borderRadius: 20, overflow: 'hidden', boxShadow: '0 24px 60px rgba(0,0,0,0.6)', outline: 'none' }}
        onClick={e => e.stopPropagation()}
      >
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '20px 24px', borderBottom: '1px solid #26262E', flexShrink: 0 }}>
          <h2 id={titleId} style={{ fontFamily: "'Inter',sans-serif", fontSize: 18, fontWeight: 700, color: TOKENS.color.textPrimary, margin: 0 }}>{title}</h2>
          <button onClick={onClose} aria-label="Close" style={{ width: 32, height: 32, borderRadius: 8, background: TOKENS.color.surface2, border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <XIcon size={16} />
          </button>
        </div>
        <div style={{ padding: '20px 24px', overflowY: 'auto', flex: 1 }}>
          {children}
        </div>
        {footer && (
          <div style={{ padding: '16px 24px', borderTop: '1px solid #26262E', display: 'flex', gap: 10, flexShrink: 0 }}>
            {footer}
          </div>
        )}
      </div>
    </div>
  );
}

// ── Edit Profile (pre-filled) ──────────────────────────────────────────────────
function EditProfileModal({ onClose }) {
  const [name, setName] = React.useState('Alex Lawson');
  const [email, setEmail] = React.useState('alex@rmtctrl.app');
  const [city, setCity] = React.useState('Sydney');
  const [country, setCountry] = React.useState('Australia');
  const [currency, setCurrency] = React.useState('AUD');
  const [bio, setBio] = React.useState('Remote-first product designer. Always chasing fast Wi-Fi and good coffee.');
  return (
    <ModalShell title="Edit profile" onClose={onClose} width={560} footer={
      <>
        <Btn variant="primary" size="md" fullWidth onClick={() => { toast('Profile updated'); onClose(); }}>Save changes</Btn>
        <Btn variant="secondary" size="md" onClick={onClose}>Cancel</Btn>
      </>
    }>
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: 18 }}>
        <Avatar size={64} initials="AL" color="#00FF94" />
        <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary }}>Profile photo · Click avatar to upload</div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <Input label="Full name" value={name} onChange={setName} />
        <Input label="Email" value={email} onChange={setEmail} type="email" />
        <div style={{ display: 'flex', gap: 12 }}>
          <div style={{ flex: 1 }}><Input label="City" value={city} onChange={setCity} /></div>
          <div style={{ flex: 1 }}><Input label="Country" value={country} onChange={setCountry} /></div>
        </div>
        <Input label="Home currency" value={currency} onChange={setCurrency} mono />
        <div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 6 }}>Bio</div>
          <textarea value={bio} onChange={e => setBio(e.target.value)} rows={3} style={{ width: '100%', background: TOKENS.color.surface2, border: '1.5px solid #26262E', borderRadius: 12, padding: '12px 14px', fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary, outline: 'none', resize: 'vertical' }} />
        </div>
      </div>
    </ModalShell>
  );
}

// ── Change Password ────────────────────────────────────────────────────────────
function ChangePasswordModal({ onClose }) {
  const [current, setCurrent] = React.useState('');
  const [next, setNext] = React.useState('');
  const [confirm, setConfirm] = React.useState('');
  const mismatch = next && confirm && next !== confirm;
  return (
    <ModalShell title="Change password" onClose={onClose} width={460} footer={
      <>
        <Btn variant="primary" size="md" fullWidth onClick={() => { toast('Password updated'); onClose(); }} disabled={!current || !next || mismatch}>Update password</Btn>
        <Btn variant="secondary" size="md" onClick={onClose}>Cancel</Btn>
      </>
    }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <Input label="Current password" value={current} onChange={setCurrent} type="password" />
        <Input label="New password" value={next} onChange={setNext} type="password" helper="Min 12 characters · Use a passphrase" />
        <Input label="Confirm new password" value={confirm} onChange={setConfirm} type="password" error={mismatch ? "Passwords don't match" : null} />
      </div>
    </ModalShell>
  );
}

// ── Two-factor Authentication ──────────────────────────────────────────────────
function TwoFAModal({ onClose }) {
  const [code, setCode] = React.useState('');
  return (
    <ModalShell title="Two-factor authentication" onClose={onClose} width={460} footer={
      <>
        <Btn variant="primary" size="md" fullWidth onClick={() => { toast('Two-factor authentication enabled'); onClose(); }}>Verify & enable</Btn>
        <Btn variant="secondary" size="md" onClick={onClose}>Cancel</Btn>
      </>
    }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
        <p style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textSecondary, margin: 0, lineHeight: 1.6 }}>
          Scan the QR code with your authenticator app (1Password, Authy, Google Authenticator), then enter the 6-digit code below.
        </p>
        <div style={{ alignSelf: 'center', width: 160, height: 160, background: TOKENS.color.textPrimary, borderRadius: 12, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <div style={{ width: 132, height: 132, background: 'repeating-linear-gradient(0deg, #0A0A0B 0 6px, transparent 6px 10px), repeating-linear-gradient(90deg, #0A0A0B 0 6px, transparent 6px 10px)' }} />
        </div>
        <div style={{ background: TOKENS.color.surface2, border: '1px solid #26262E', borderRadius: 10, padding: '10px 14px', fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: TOKENS.color.textSecondary, textAlign: 'center', letterSpacing: '0.1em' }}>
          JBSW Y3DP EHPK 3PXP
        </div>
        <Input label="Verification code" value={code} onChange={setCode} mono helper="6-digit code" />
      </div>
    </ModalShell>
  );
}

// ── Sign Out ───────────────────────────────────────────────────────────────────
function SignOutModal({ onClose }) {
  return (
    <ModalShell title="Sign out" onClose={onClose} width={420} footer={
      <>
        <Btn variant="primary" size="md" fullWidth onClick={() => { toast('Signed out'); onClose(); }}>Sign out</Btn>
        <Btn variant="secondary" size="md" onClick={onClose}>Cancel</Btn>
      </>
    }>
      <p style={{ fontFamily: "'Inter',sans-serif", fontSize: 15, color: TOKENS.color.textSecondary, margin: 0, lineHeight: 1.6 }}>
        You'll be signed out of RMTCTRL on this device. Any unsynced changes will be saved automatically.
      </p>
    </ModalShell>
  );
}

// ── Delete Account ─────────────────────────────────────────────────────────────
function DeleteAccountModal({ onClose }) {
  const [confirm, setConfirm] = React.useState('');
  return (
    <ModalShell title="Delete account" onClose={onClose} width={460} footer={
      <>
        <button onClick={() => { toast('Account deletion requested', 'error'); onClose(); }} disabled={confirm !== 'DELETE'} style={{ flex: 1, height: 42, borderRadius: 10, border: 'none', cursor: confirm === 'DELETE' ? 'pointer' : 'not-allowed', background: confirm === 'DELETE' ? TOKENS.color.error : TOKENS.color.errorMuted, color: confirm === 'DELETE' ? '#fff' : TOKENS.color.textSecondary, fontFamily: "'Inter',sans-serif", fontSize: 14, fontWeight: 600 }}>
          Permanently delete account
        </button>
        <Btn variant="secondary" size="md" onClick={onClose}>Cancel</Btn>
      </>
    }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div style={{ background: 'rgba(255,59,92,0.08)', border: '1px solid rgba(255,59,92,0.3)', borderRadius: 10, padding: '12px 14px' }}>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: 600, color: TOKENS.color.error, marginBottom: 4 }}>This action is irreversible</div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textSecondary, lineHeight: 1.5 }}>All trips, vault documents, expenses and saved workspaces will be permanently deleted.</div>
        </div>
        <Input label='Type "DELETE" to confirm' value={confirm} onChange={setConfirm} mono />
      </div>
    </ModalShell>
  );
}

// ── Add Expense ────────────────────────────────────────────────────────────────
function AddExpenseModal({ onClose }) {
  const [vendor, setVendor] = React.useState('');
  const [amount, setAmount] = React.useState('');
  const [cat, setCat] = React.useState('Food');
  const [note, setNote] = React.useState('');
  const cats = ['Food', 'Transport', 'Accommodation', 'Workspace', 'Other'];
  return (
    <ModalShell title="Add expense" onClose={onClose} width={500} footer={
      <>
        <Btn variant="primary" size="md" fullWidth onClick={() => { toast(`Expense saved · €${amount} ${vendor}`); onClose(); }} disabled={!vendor || !amount}>Save expense</Btn>
        <Btn variant="secondary" size="md" onClick={onClose}>Cancel</Btn>
      </>
    }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <Input label="Vendor / merchant" value={vendor} onChange={setVendor} helper="e.g. Time Out Market" />
        <div style={{ display: 'flex', gap: 12 }}>
          <div style={{ flex: 1 }}>
            <Input label="Amount" value={amount} onChange={setAmount} mono prefix="€" />
          </div>
          <div style={{ flex: 1, background: TOKENS.color.surface2, borderRadius: 12, border: '1.5px solid #26262E', padding: '8px 14px' }}>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 11, color: TOKENS.color.textSecondary, marginBottom: 4 }}>Date</div>
            <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 14, color: TOKENS.color.textPrimary }}>TODAY · 14:32</div>
          </div>
        </div>
        <div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 8 }}>Category</div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {cats.map(c => <FilterChip key={c} label={c} selected={cat === c} onClick={() => setCat(c)} />)}
          </div>
        </div>
        <div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 6 }}>Note (optional)</div>
          <textarea value={note} onChange={e => setNote(e.target.value)} rows={2} style={{ width: '100%', background: TOKENS.color.surface2, border: '1.5px solid #26262E', borderRadius: 12, padding: '10px 14px', fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary, outline: 'none', resize: 'vertical' }} />
        </div>
      </div>
    </ModalShell>
  );
}

// ── Post Job ───────────────────────────────────────────────────────────────────
function PostJobModal({ onClose }) {
  const [title, setTitle] = React.useState('');
  const [company, setCompany] = React.useState('');
  const [type, setType] = React.useState('Full-time');
  const [cat, setCat] = React.useState('Engineering');
  const [tz, setTz] = React.useState('Any');
  const [salary, setSalary] = React.useState('');
  const [desc, setDesc] = React.useState('');
  const types = ['Full-time', 'Part-time', 'Contract', 'Freelance'];
  const cats = ['Engineering', 'Design', 'Marketing', 'Product', 'Writing', 'Support', 'Finance'];
  const tzs = ['Any', 'Americas', 'EMEA', 'APAC'];
  return (
    <ModalShell title="Post a job" onClose={onClose} width={560} footer={
      <>
        <Btn variant="primary" size="md" fullWidth onClick={() => { toast(`Job posted · ${title} at ${company}`); onClose(); }} disabled={!title || !company || !desc}>Post job</Btn>
        <Btn variant="secondary" size="md" onClick={onClose}>Cancel</Btn>
      </>
    }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <Input label="Role title" value={title} onChange={setTitle} helper="e.g. Senior Full-Stack Engineer" />
        <Input label="Company" value={company} onChange={setCompany} />
        <div style={{ display: 'flex', gap: 12 }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 6 }}>Contract type</div>
            <select value={type} onChange={e => setType(e.target.value)} style={{ width: '100%', height: 40, background: TOKENS.color.surface2, border: '1.5px solid #26262E', borderRadius: 12, padding: '0 14px', fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary, outline: 'none', cursor: 'pointer' }}>
              {types.map(t => <option key={t} value={t}>{t}</option>)}
            </select>
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 6 }}>Category</div>
            <select value={cat} onChange={e => setCat(e.target.value)} style={{ width: '100%', height: 40, background: TOKENS.color.surface2, border: '1.5px solid #26262E', borderRadius: 12, padding: '0 14px', fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary, outline: 'none', cursor: 'pointer' }}>
              {cats.map(c => <option key={c} value={c}>{c}</option>)}
            </select>
          </div>
        </div>
        <div style={{ display: 'flex', gap: 12 }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 6 }}>Timezone</div>
            <select value={tz} onChange={e => setTz(e.target.value)} style={{ width: '100%', height: 40, background: TOKENS.color.surface2, border: '1.5px solid #26262E', borderRadius: 12, padding: '0 14px', fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary, outline: 'none', cursor: 'pointer' }}>
              {tzs.map(t => <option key={t} value={t}>{t}</option>)}
            </select>
          </div>
          <div style={{ flex: 1 }}><Input label="Salary range" value={salary} onChange={setSalary} mono helper="$130k – $160k" /></div>
        </div>
        <div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 6 }}>Role description</div>
          <textarea value={desc} onChange={e => setDesc(e.target.value)} rows={4} placeholder="What will the person do? Who's the team? What's the impact?" style={{ width: '100%', background: TOKENS.color.surface2, border: '1.5px solid #26262E', borderRadius: 12, padding: '12px 14px', fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary, outline: 'none', resize: 'vertical' }} />
        </div>
      </div>
    </ModalShell>
  );
}

// ── Currency Converter ─────────────────────────────────────────────────────────
function CurrencyModal({ onClose }) {
  // TODO: set EXCHANGERATE_API_KEY to your key from exchangerate-api.com (free tier available)
  const EXCHANGERATE_API_KEY = '';

  const FALLBACK_RATES = { AUD: 1, EUR: 0.608, USD: 0.66, GBP: 0.52, JPY: 99.5, SGD: 0.88, THB: 23.4, CAD: 0.90, CHF: 0.58, NZD: 1.09 };
  const [amount, setAmount] = React.useState('100');
  const [from, setFrom] = React.useState({ flag: '🇦🇺', code: 'AUD' });
  const [to, setTo] = React.useState({ flag: '🇪🇺', code: 'EUR' });
  const [rates, setRates] = React.useState(FALLBACK_RATES);
  const [ratesLoading, setRatesLoading] = React.useState(false);
  const [ratesLive, setRatesLive] = React.useState(false);

  // Fetch live rates on mount if key is configured
  React.useEffect(() => {
    if (!EXCHANGERATE_API_KEY) return;
    setRatesLoading(true);
    fetch(`https://v6.exchangerate-api.com/v6/${EXCHANGERATE_API_KEY}/latest/AUD`)
      .then(r => r.json())
      .then(data => {
        if (data.result === 'success' && data.conversion_rates) {
          setRates(data.conversion_rates);
          setRatesLive(true);
        }
      })
      .catch(() => {}) // silently fall back to hardcoded rates
      .finally(() => setRatesLoading(false));
  }, []);

  const recentCurrencies = [
    { flag: '🇪🇺', code: 'EUR' },
    { flag: '🇺🇸', code: 'USD' },
    { flag: '🇬🇧', code: 'GBP' },
    { flag: '🇯🇵', code: 'JPY' },
    { flag: '🇸🇬', code: 'SGD' },
    { flag: '🇹🇭', code: 'THB' },
  ];
  const rate = (rates[to.code] || 1) / (rates[from.code] || 1);
  const converted = (parseFloat(amount || 0) * rate).toFixed(2);
  const swap = () => { const tmp = from; setFrom(to); setTo(tmp); };
  return (
    <ModalShell title="Currency converter" onClose={onClose} width={460} footer={
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, width: '100%' }}>
        <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: ratesLive ? TOKENS.color.accent : TOKENS.color.textTertiary, flex: 1 }}>
          {ratesLoading ? 'Fetching live rates…' : ratesLive ? '● Live rates' : '● Fallback rates — add API key for live data'}
        </span>
        <Btn variant="secondary" size="md" onClick={onClose}>Done</Btn>
      </div>
    }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <Card style={{ padding: 16 }}>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 8 }}>FROM</div>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, background: TOKENS.color.surface2, borderRadius: 10, padding: '6px 10px' }}>
              <span style={{ fontSize: 18 }}>{from.flag}</span>
              <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 14, fontWeight: 600, color: TOKENS.color.textPrimary }}>{from.code}</span>
            </div>
            <input type="number" value={amount} onChange={e => setAmount(e.target.value)} style={{ background: 'none', border: 'none', outline: 'none', textAlign: 'right', fontFamily: "'JetBrains Mono',monospace", fontSize: 26, fontWeight: 600, color: TOKENS.color.textPrimary, width: '60%' }} />
          </div>
        </Card>

        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
          <button onClick={swap} aria-label="Swap currencies" style={{ width: 32, height: 32, borderRadius: 8, background: TOKENS.color.surface, border: `1px solid ${TOKENS.color.border}`, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', color: TOKENS.color.textSecondary, fontFamily: "'JetBrains Mono',monospace", fontSize: 14 }}>⇅</button>
          <MonoText size={11} color="#5C5C66">1 {from.code} = {rate.toFixed(4)} {to.code}</MonoText>
        </div>

        <Card style={{ padding: 16 }}>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 8 }}>TO</div>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, background: TOKENS.color.surface2, borderRadius: 10, padding: '6px 10px' }}>
              <span style={{ fontSize: 18 }}>{to.flag}</span>
              <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 14, fontWeight: 600, color: TOKENS.color.textPrimary }}>{to.code}</span>
            </div>
            <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 26, fontWeight: 600, color: TOKENS.color.accent, textAlign: 'right' }}>{converted}</div>
          </div>
        </Card>

        <div>
          <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600, color: TOKENS.color.textTertiary, letterSpacing: '0.08em', marginBottom: 8 }}>QUICK SWITCH</div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {recentCurrencies.map(c => (
              <button key={c.code} onClick={() => setTo(c)} style={{ background: to.code === c.code ? 'rgba(0,255,148,0.1)' : TOKENS.color.surface2, border: `1px solid ${to.code === c.code ? TOKENS.color.accent : TOKENS.color.border}`, borderRadius: 999, padding: '5px 10px', fontFamily: "'JetBrains Mono',monospace", fontSize: 12, fontWeight: 600, color: to.code === c.code ? TOKENS.color.accent : TOKENS.color.textSecondary, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 5 }}>
                <span>{c.flag}</span>{c.code}
              </button>
            ))}
          </div>
        </div>
      </div>
    </ModalShell>
  );
}

// ── Submit Event ───────────────────────────────────────────────────────────────
function SubmitEventModal({ onClose }) {
  const [title, setTitle] = React.useState('');
  const [type, setType] = React.useState('Meetup');
  const [city, setCity] = React.useState('');
  const [loc, setLoc] = React.useState('');
  const [date, setDate] = React.useState('');
  const [desc, setDesc] = React.useState('');
  const types = ['Meetup', 'Conference', 'Workshop', 'Co-working Day', 'Social'];
  return (
    <ModalShell title="Submit an event" onClose={onClose} width={560} footer={
      <>
        <Btn variant="primary" size="md" fullWidth onClick={() => { toast('Event submitted for review'); onClose(); }} disabled={!title || !city || !date}>Submit event</Btn>
        <Btn variant="secondary" size="md" onClick={onClose}>Cancel</Btn>
      </>
    }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <Input label="Event title" value={title} onChange={setTitle} helper="e.g. Lisbon Digital Nomad Meetup" />
        <div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 8 }}>Event type</div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {types.map(t => <FilterChip key={t} label={t} selected={type === t} onClick={() => setType(t)} />)}
          </div>
        </div>
        <div style={{ display: 'flex', gap: 12 }}>
          <div style={{ flex: 1 }}><Input label="City" value={city} onChange={setCity} /></div>
          <div style={{ flex: 1 }}><Input label="Date & time" value={date} onChange={setDate} mono helper="e.g. Thu 8 May · 19:00" /></div>
        </div>
        <Input label="Venue / location" value={loc} onChange={setLoc} helper="e.g. Hub Lisbon, Chiado" />
        <div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 12, color: TOKENS.color.textSecondary, marginBottom: 6 }}>Description</div>
          <textarea value={desc} onChange={e => setDesc(e.target.value)} rows={4} placeholder="What's the event about? Who should come?" style={{ width: '100%', background: TOKENS.color.surface2, border: '1.5px solid #26262E', borderRadius: 12, padding: '12px 14px', fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textPrimary, outline: 'none', resize: 'vertical' }} />
        </div>
      </div>
    </ModalShell>
  );
}

// ── Error boundary ─────────────────────────────────────────────────────────────
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }
  componentDidCatch(error, info) {
    console.error('[RMTCTRL] Uncaught error:', error, info.componentStack);
  }
  render() {
    if (this.state.hasError) {
      const { fallback, onReset } = this.props;
      if (fallback) return fallback(this.state.error, () => this.setState({ hasError: false, error: null }));
      return (
        <div style={{
          flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
          padding: '40px 24px', gap: 16, textAlign: 'center',
        }}>
          <div style={{ fontSize: 48 }}>⚠️</div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 18, fontWeight: 700, color: TOKENS.color.textPrimary }}>
            Something went wrong
          </div>
          <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 14, color: TOKENS.color.textSecondary, maxWidth: 320 }}>
            {this.state.error?.message || 'An unexpected error occurred in this screen.'}
          </div>
          <button
            onClick={() => { this.setState({ hasError: false, error: null }); if (onReset) onReset(); }}
            style={{
              height: 40, padding: '0 20px', borderRadius: 10, border: 'none', cursor: 'pointer',
              background: TOKENS.color.accent, color: TOKENS.color.bg,
              fontFamily: "'Inter',sans-serif", fontSize: 14, fontWeight: 600,
            }}
          >Try again</button>
        </div>
      );
    }
    return this.props.children;
  }
}

// ── Main Web App ───────────────────────────────────────────────────────────────
// ── Hash routing helpers ──────────────────────────────────────────────────────
// Parses the current URL hash (#home, #trips, etc.) into a screen name.
// Falls back to 'home' for empty or unknown hashes.
const VALID_SCREENS = new Set([
  'home','trips','workspaces','accommodation','vault','expenses',
  'location','whatson','jobs','profile','add-segment','workspace-detail',
  'doc-detail','add-workspace','submit-review',
]);
function screenFromHash() {
  const hash = window.location.hash.replace('#', '') || 'home';
  return VALID_SCREENS.has(hash) ? hash : 'home';
}

function WebApp() {
  const [screen, setScreen] = React.useState(screenFromHash);
  const [dashState, setDashState] = React.useState(WEB_TWEAK_DEFAULTS.dashboardState);
  const [cmdOpen, setCmdOpen] = React.useState(false);
  const [addSegOpen, setAddSegOpen] = React.useState(false);
  const [postJobOpen, setPostJobOpen] = React.useState(false);
  const [submitEventOpen, setSubmitEventOpen] = React.useState(false);
  const [currencyOpen, setCurrencyOpen] = React.useState(false);
  const [profileBtnHovered, setProfileBtnHovered] = React.useState(false);

  React.useEffect(() => {
    const handler = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); setCmdOpen(o => !o); }
      if (e.key === 'Escape') setCmdOpen(false);
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, []);

  // Sync screen ↔ URL hash. Back/forward buttons now work correctly.
  React.useEffect(() => {
    const onHashChange = () => {
      const next = screenFromHash();
      setScreen(next);
    };
    window.addEventListener('hashchange', onHashChange);
    return () => window.removeEventListener('hashchange', onHashChange);
  }, []);

  const vw = useViewportWidth();
  const isMobile = vw < 768;
  const [mobileNavOpen, setMobileNavOpen] = React.useState(false);

  const navigate = (to, extra) => {
    if (to === 'add-segment') { setAddSegOpen(true); return; }
    if (to === 'currency')    { setCurrencyOpen(true); return; }
    // Push to browser history so back/forward work
    window.location.hash = to === 'home' ? '' : to;
    setScreen(to);
    if (extra) setDashState(extra);
    setCmdOpen(false);
    setMobileNavOpen(false);
  };

  const topNav = [
    { icon: CompassIcon, label: 'Dashboard', id: 'home' },
    { icon: RouteIcon, label: 'Trips', id: 'trips', badge: '3' },
    { icon: BedIcon, label: 'Accommodation', id: 'accommodation' },
    { icon: MapPinIcon, label: 'Workspaces', id: 'workspaces' },
    { icon: GlobeIcon, label: 'Location Info', id: 'location' },
    { icon: CalendarIcon, label: "What's On", id: 'whatson' },
    { icon: BriefcaseIcon, label: 'Remote Jobs', id: 'jobs' },
  ];
  const bottomNav = [
    { icon: FolderIcon, label: 'Vault', id: 'vault', badge: '2⚠' },
    { icon: DollarIcon, label: 'Expenses', id: 'expenses' },
  ];

  const screenTitles = {
    home: { title: 'Dashboard', subtitle: 'Your live travel command centre' },
    trips: { title: 'Trips', subtitle: 'Plan, track and review your journeys' },
    accommodation: { title: 'Accommodation', subtitle: 'Search, filter and save places to stay' },
    workspaces: { title: 'Workspaces', subtitle: 'Discover places to work nearby' },
    vault: { title: 'Vault', subtitle: 'Documents, visas and travel essentials' },
    expenses: { title: 'Expenses', subtitle: 'Track spending across trips' },
    location: { title: 'Location Info', subtitle: 'Visa, cost of living, internet and safety data' },
    whatson: { title: "What's On", subtitle: 'Events and meetups for digital nomads' },
    jobs: { title: 'Remote Jobs', subtitle: 'Find your next fully remote opportunity' },
    profile: { title: 'Profile & Settings', subtitle: '' },
    'add-workspace': { title: 'Submit a Workspace', subtitle: '' },
    currency: { title: 'Currency Converter', subtitle: 'Live rates' },
    'workspace-detail': { title: 'Hub Lisbon', subtitle: 'Cowork · Chiado', breadcrumb: 'Workspaces' },
    'doc-detail': { title: 'Australian Passport', subtitle: 'Expires Mar 2027', breadcrumb: 'Vault' },
    'submit-review': { title: 'Write a Review', subtitle: 'Hub Lisbon', breadcrumb: 'Workspaces' },
  };
  const st = screenTitles[screen] || { title: screen, subtitle: '' };

  const renderActions = () => {
    if (screen === 'trips') return (
      <div style={{ display: 'flex', gap: 8 }}>
        <Btn variant="primary" size="sm" onClick={() => setAddSegOpen(true)} icon={<PlusIcon size={14} color="#0A0A0B" />}>New trip</Btn>
      </div>
    );
    if (screen === 'workspaces') return (
      <div style={{ display: 'flex', gap: 8 }}>
        <Btn variant="secondary" size="sm" onClick={() => navigate('add-workspace')}>Submit workspace</Btn>
      </div>
    );
    if (screen === 'accommodation') return (
      <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
        <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 11, fontWeight: 600, color: TOKENS.color.textTertiary }}>LISBON · MAY 2–16</span>
      </div>
    );
    if (screen === 'jobs') return (
      <div style={{ display: 'flex', gap: 8 }}>
        <Btn variant="secondary" size="sm" onClick={() => setPostJobOpen(true)}>Post a job</Btn>
      </div>
    );
    if (screen === 'whatson') return (
      <div style={{ display: 'flex', gap: 8 }}>
        <Btn variant="secondary" size="sm" onClick={() => setSubmitEventOpen(true)}>Submit event</Btn>
      </div>
    );
    if (screen === 'home') return (
      <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
        {['pre-flight','mid-trip','no-trip'].map(s => (
          <button key={s} onClick={() => setDashState(s)} style={{
            height: 28, padding: '0 10px', borderRadius: 6, border: `1px solid ${dashState === s ? TOKENS.color.accent : TOKENS.color.border}`,
            background: dashState === s ? 'rgba(0,255,148,0.1)' : 'transparent',
            cursor: 'pointer', fontFamily: "'JetBrains Mono',monospace", fontSize: 10, fontWeight: 600,
            color: dashState === s ? TOKENS.color.accent : TOKENS.color.textTertiary, letterSpacing: '0.05em', textTransform: 'uppercase',
          }}>{s.replace('-',' ')}</button>
        ))}
      </div>
    );
    return null;
  };

  const renderScreen = () => {
    switch (screen) {
      case 'home':             return <WebDashboard onNavigate={navigate} dashState={dashState} sidebarOpen={!isMobile} />;
      case 'trips':            return <WebTrips onNavigate={navigate} />;
      case 'accommodation':    return <AccommodationScreen />;
      case 'workspaces':       return <WebWorkspaces onNavigate={navigate} />;
      case 'vault':            return <WebVault onNavigate={navigate} />;
      case 'expenses':         return <WebExpenses onNavigate={navigate} />;
      case 'location':         return <LocationInfo onBack={() => navigate('home')} onNavigate={navigate} />;
      case 'whatson':          return <WhatsOnScreen />;
      case 'jobs':             return <RemoteJobsScreen />;
      case 'profile':          return <WebProfile />;
      case 'workspace-detail': return <WorkspaceDetail onBack={() => navigate('workspaces')} onNavigate={navigate} />;
      case 'doc-detail':       return <DocDetail onBack={() => navigate('vault')} />;
      case 'add-workspace':    return <AddWorkspace onBack={() => navigate('workspaces')} />;
      case 'currency':         return <CurrencyConverter onBack={() => navigate('expenses')} />;
      case 'submit-review':    return <SubmitReview onBack={() => navigate('workspace-detail')} />;
      default:                 return <WebDashboard onNavigate={navigate} dashState={dashState} sidebarOpen={!isMobile} />;
    }
  };

  const sidebarVisible = !isMobile || mobileNavOpen;
  return (
    <div style={{ height: '100vh', display: 'flex', background: TOKENS.color.bg, color: TOKENS.color.textPrimary, overflow: 'hidden', position: 'relative' }}>
      {/* Mobile backdrop */}
      {isMobile && mobileNavOpen && (
        <div onClick={() => setMobileNavOpen(false)} style={{
          position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', zIndex: 90, cursor: 'pointer',
        }} />
      )}
      {/* Sidebar */}
      <div style={{
        width: 220, flexShrink: 0, borderRight: '1px solid #26262E',
        display: sidebarVisible ? 'flex' : 'none', flexDirection: 'column',
        background: TOKENS.color.bg,
        position: isMobile ? 'fixed' : 'relative',
        top: 0, bottom: 0, left: 0, zIndex: 100,
        boxShadow: isMobile ? '4px 0 30px rgba(0,0,0,0.5)' : 'none',
      }}>
        {/* Logo */}
        <div style={{ height: 56, display: 'flex', alignItems: 'center', padding: '0 16px', borderBottom: '1px solid #26262E', gap: 10, flexShrink: 0 }}>
          <div style={{ width: 28, height: 28, borderRadius: 7, background: 'rgba(0,255,148,0.12)', border: '1px solid rgba(0,255,148,0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <CompassIcon size={16} color="#00FF94" />
          </div>
          <span style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 14, fontWeight: 600, color: TOKENS.color.textPrimary, letterSpacing: '0.08em' }}>RMTCTRL</span>
        </div>

        {/* Search / cmd */}
        <div style={{ padding: '12px 10px 8px' }}>
          <button onClick={() => setCmdOpen(true)} style={{
            width: '100%', height: 34, background: TOKENS.color.surface2, border: '1px solid #26262E',
            borderRadius: 8, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8,
            padding: '0 10px',
          }}>
            <SearchIcon size={14} />
            <span style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, color: TOKENS.color.textTertiary, flex: 1, textAlign: 'left' }}>Search</span>
            <kbd style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: TOKENS.color.textTertiary, background: TOKENS.color.border, padding: '1px 5px', borderRadius: 3 }}>⌘K</kbd>
          </button>
        </div>

        {/* Top nav (scrollable) */}
        <div style={{ flex: 1, overflowY: 'auto', padding: '4px 10px', display: 'flex', flexDirection: 'column', gap: 2 }}>
          {topNav.map(item => (
            <SideNavItem key={item.id} icon={item.icon} label={item.label} badge={item.badge}
              active={screen === item.id} onClick={() => navigate(item.id)} />
          ))}
        </div>

        {/* Bottom-pinned nav: Vault + Expenses */}
        <div style={{ padding: '8px 10px', borderTop: '1px solid #26262E', display: 'flex', flexDirection: 'column', gap: 2 }}>
          {bottomNav.map(item => (
            <SideNavItem key={item.id} icon={item.icon} label={item.label} badge={item.badge}
              active={screen === item.id} onClick={() => navigate(item.id)} />
          ))}
        </div>

        {/* Bottom: user */}
        <div style={{ borderTop: '1px solid #26262E', padding: '10px' }}>
          <button
            onClick={() => navigate('profile')}
            aria-label="Go to Profile & Settings"
            aria-current={screen === 'profile' ? 'page' : undefined}
            onMouseEnter={() => setProfileBtnHovered(true)}
            onMouseLeave={() => setProfileBtnHovered(false)}
            style={{
              width: '100%', height: 46, display: 'flex', alignItems: 'center', gap: 10,
              background: (screen === 'profile' || profileBtnHovered) ? TOKENS.color.surface2 : 'transparent',
              borderRadius: 8, border: 'none', cursor: 'pointer', padding: '0 10px',
              transition: `background ${TOKENS.anim.fast}`,
            }}
          >
            <Avatar size={28} initials="AL" color="#00FF94" />
            <div style={{ flex: 1, textAlign: 'left' }}>
              <div style={{ fontFamily: "'Inter',sans-serif", fontSize: 13, fontWeight: 600, color: TOKENS.color.textPrimary }}>Alex Lawson</div>
              <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 10, color: TOKENS.color.textTertiary }}>PRO</div>
            </div>
            <ChevronRightIcon size={14} />
          </button>
        </div>
      </div>

      {/* Main area */}
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
        <TopBar
          title={st.title}
          subtitle={st.subtitle}
          breadcrumb={st.breadcrumb}
          actions={renderActions()}
          isMobile={isMobile}
          onMenu={() => setMobileNavOpen(true)}
        />
        <div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>
          <ErrorBoundary key={screen} onReset={() => navigate('home')}>
            {renderScreen()}
          </ErrorBoundary>
        </div>
      </div>

      {/* Overlays */}
      {cmdOpen && <CommandBar onClose={() => setCmdOpen(false)} onNavigate={navigate} />}
      {addSegOpen && <WebAddSegment onClose={() => setAddSegOpen(false)} />}
      {postJobOpen && <PostJobModal onClose={() => setPostJobOpen(false)} />}
      {submitEventOpen && <SubmitEventModal onClose={() => setSubmitEventOpen(false)} />}
      {currencyOpen && <CurrencyModal onClose={() => setCurrencyOpen(false)} />}
      <ToastContainer />
    </div>
  );
}

// Delay the React mount so that every type="text/babel" script (especially
// webapp-screens.jsx which defines WebDashboard) finishes compiling first.
// Babel-standalone processes <script type="text/babel"> tags in parallel and
// doesn't guarantee execution order; mounting immediately caused
// "ReferenceError: WebDashboard is not defined" when webapp.jsx evaluated
// before webapp-screens.jsx. Real fix is the Vite migration in VITE-MIGRATION.md
// which removes Babel-in-browser entirely.
window.addEventListener('load', () => {
  setTimeout(() => {
    ReactDOM.createRoot(document.getElementById('root')).render(<WebApp />);
  }, 250);
});
