/* === LedgerApp — the main app shell shared between index.html (full
   product) and embed.html (iframe-embedded preview for the marketing
   landing page). Reads TWEAK_DEFAULTS, FONT_DISPLAY_OPTIONS,
   FONT_UI_OPTIONS from window globals declared in the entry point's
   inline script. === */

/* Dashboard-only FAB. Tapping the + opens a popover of quick-add shortcuts.
   Three options route to the relevant modal; the fourth is an inline form
   that drops a manual line into the current week's shopping list (extras). */
function DashboardFab({ setState, setOpenModal }) {
  const [open, setOpen] = React.useState(false);
  const [groceryMode, setGroceryMode] = React.useState(false);
  const [grocery, setGrocery] = React.useState('');
  const rootRef = React.useRef(null);
  const groceryInputRef = React.useRef(null);

  React.useEffect(() => {
    if (!open) return;
    const onDocClick = (e) => {
      if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
    };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDocClick);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDocClick);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  React.useEffect(() => {
    if (!open) { setGroceryMode(false); setGrocery(''); }
  }, [open]);

  React.useEffect(() => {
    if (groceryMode) groceryInputRef.current?.focus();
  }, [groceryMode]);

  const choose = (modal) => { setOpen(false); setOpenModal(modal); };

  const addGrocery = () => {
    const name = grocery.trim();
    if (!name) return;
    const key = name.toLowerCase();
    const planWeekKey = weekStartIso(isoDate(TODAY));
    setState(s => {
      const prev = s.shopping?.[planWeekKey] || { checked: {}, cleared: {}, extras: [] };
      const dup = (prev.extras || []).some(e => (e.name || '').toLowerCase() === key);
      const newCleared = { ...prev.cleared };
      delete newCleared[key];
      if (dup) {
        return { ...s, shopping: { ...(s.shopping || {}), [planWeekKey]: { ...prev, cleared: newCleared } } };
      }
      return {
        ...s,
        shopping: {
          ...(s.shopping || {}),
          [planWeekKey]: {
            ...prev,
            cleared: newCleared,
            extras: [...(prev.extras || []), { id: 'sx' + Date.now().toString(36), name }],
          },
        },
      };
    });
    setGrocery('');
    setOpen(false);
  };

  return (
    <div ref={rootRef}>
      {open && (
        <div className="fab-menu" role="menu" aria-label="Quick add">
          {groceryMode ? (
            <div className="fab-menu-grocery">
              <input
                ref={groceryInputRef}
                type="text"
                value={grocery}
                placeholder="Grocery item…"
                onChange={(e) => setGrocery(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter') addGrocery();
                  else if (e.key === 'Escape') { setGroceryMode(false); setGrocery(''); }
                }}
              />
              <button onClick={addGrocery}>Add</button>
            </div>
          ) : (
            <React.Fragment>
              <button className="fab-menu-item" onClick={() => choose('expense')} role="menuitem">
                <span className="fab-menu-glyph">≡</span> Expense
              </button>
              <button className="fab-menu-item" onClick={() => choose('brain-note')} role="menuitem">
                <span className="fab-menu-glyph">✱</span> Commonplace entry
              </button>
              <button className="fab-menu-item" onClick={() => choose('recipe')} role="menuitem">
                <span className="fab-menu-glyph">◫</span> Recipe
              </button>
              <button className="fab-menu-item" onClick={() => setGroceryMode(true)} role="menuitem">
                <span className="fab-menu-glyph">◧</span> Grocery item
              </button>
            </React.Fragment>
          )}
        </div>
      )}
      <button
        className={'fab' + (open ? ' is-open' : '')}
        onClick={() => setOpen(o => !o)}
        title="Quick add"
        aria-label="Quick add"
        aria-expanded={open}
      >
        <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round">
          <line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
        </svg>
      </button>
    </div>
  );
}

function LedgerApp({ state, setState, signOut, theme, setTheme, isDemo, userId, initialRoute, navHidden }) {
  const [tweak, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [route, setRoute] = React.useState(initialRoute || 'home');
  const [openModal, setOpenModal] = React.useState(null);
  const [editingExpense, setEditingExpense] = React.useState(null);
  const [editingGoal, setEditingGoal] = React.useState(null);
  const [editingFixed, setEditingFixed] = React.useState(null);
  const [editingRecipe, setEditingRecipe] = React.useState(null);
  const [editingTask, setEditingTask] = React.useState(null);
  const [editingBrainNote, setEditingBrainNote] = React.useState(null);
  // Handoff slot — the inline brain-dump composer drops its in-progress
  // draft here when the user clicks "Expand", and BrainNoteModal reads
  // it on mount so typing continues in the bigger surface without a
  // round-trip save. Cleared when the modal closes.
  const [pendingBrainDraft, setPendingBrainDraft] = React.useState(null);
  const [editingIncomeSource, setEditingIncomeSource] = React.useState(null);
  const [editingAccount, setEditingAccount] = React.useState(null);
  const [accountMode, setAccountMode] = React.useState('full'); // 'full' | 'balance'
  const [activeSource, setActiveSource] = React.useState(null);
  const [activeMonth, setActiveMonth] = React.useState(currentMonthKey());
  const [monthMenuOpen, setMonthMenuOpen] = React.useState(false);
  const [mobileNavOpen, setMobileNavOpen] = React.useState(false);
  // dayTick is a render-trigger for components that read TODAY.getDate() and
  // friends. Bumped whenever the focus handler detects the calendar day has
  // moved. Nothing reads its value — only its identity matters for re-renders.
  const [, setDayTick] = React.useState(0);

  // Detect day / month rollover on focus and visibility change. The desktop
  // app stays open for days, so the in-memory TODAY freezes on whatever date
  // the script first loaded. When the user comes back to the window we
  // refresh TODAY in place; if the calendar day has changed we force a
  // re-render, and if the active month was the one that just ended we
  // auto-advance to the new month.
  React.useEffect(() => {
    let lastSeenIso = isoDate(TODAY);
    const onPotentialRollover = () => {
      refreshToday();
      const todayIso = isoDate(TODAY);
      if (todayIso === lastSeenIso) return;
      lastSeenIso = todayIso;
      // Force any TODAY-dependent UI to recompute (daily allowance, days
      // remaining, "today" badges, etc.).
      setDayTick(t => t + 1);
      // Only auto-advance the month when the user was sitting on the
      // immediately-preceding month. If they're deliberately browsing a
      // historical month we leave them where they are.
      const newMonth = currentMonthKey();
      setActiveMonth(prev => prev === shiftMonth(newMonth, -1) ? newMonth : prev);
    };
    const onVisibility = () => { if (!document.hidden) onPotentialRollover(); };
    window.addEventListener('focus', onPotentialRollover);
    document.addEventListener('visibilitychange', onVisibility);
    return () => {
      window.removeEventListener('focus', onPotentialRollover);
      document.removeEventListener('visibilitychange', onVisibility);
    };
  }, []);

  // Filter state for current month view
  const monthExpenses = state.expenses.filter(e => monthKey(e.date) === activeMonth);
  const monthExtra = (state.extra || []).filter(x => monthKey(x.date) === activeMonth);
  // `state.income` is now derived from incomeSources (+ paychecks, for past months
  // with variable sources). Use the CONFIRMED figure as the single income number
  // legacy readers see — better to under-report than to plan against money that
  // hasn't landed. `projected` exposes the optimistic figure for context.
  const monthIncome = computeMonthIncome(state, activeMonth);
  const viewState = { ...state, income: monthIncome.confirmed, projectedIncome: monthIncome.projected, expenses: monthExpenses, extra: monthExtra, _allExpenses: state.expenses, _allExtra: state.extra, activeMonth };

  // Available months from data (descending)
  const availableMonths = React.useMemo(() => {
    const set = new Set(state.expenses.map(e => monthKey(e.date)));
    set.add(currentMonthKey());
    return [...set].sort().reverse();
  }, [state.expenses]);

  // Counts for sidebar. Expenses are scoped to the active month — showing
  // the all-time total made the count read as last month's residue on the
  // 1st of a new month. Fixed costs / family / tags / etc. aren't
  // month-scoped, so they keep their global counts.
  const counts = {
    expenses: monthExpenses.length,
    fixed: state.fixed.length,
    family: state.family.length,
    tasks: (state.tasks || []).filter(t => !t.done).length,
    meals: (state.recipes || []).length,
    tags: state.tags.length,
    goals: state.goals.length,
  };

  // Apply font tweaks
  React.useEffect(() => {
    document.documentElement.style.setProperty('--font-display', `"${tweak.displayFont}", Georgia, serif`);
    document.documentElement.style.setProperty('--font-ui', `"${tweak.uiFont}", -apple-system, sans-serif`);
    document.documentElement.style.setProperty('--terracotta', tweak.accent);
  }, [tweak.displayFont, tweak.uiFont, tweak.accent]);

  // Density
  React.useEffect(() => {
    const root = document.documentElement;
    if (tweak.density === 'compact') {
      root.style.fontSize = '13px';
    } else if (tweak.density === 'comfy') {
      root.style.fontSize = '15px';
    } else {
      root.style.fontSize = '14px';
    }
  }, [tweak.density]);

  // Cmd+N to add expense, Cmd+\ to toggle privacy mode. The keydown path is
  // the browser fallback; in Electron the menu (main.js) owns the accelerators
  // and dispatches `ledger:*` custom events so the system File / View menu
  // doesn't swallow the keystroke first.
  React.useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'n') {
        e.preventDefault();
        setOpenModal('expense');
      }
      if ((e.metaKey || e.ctrlKey) && e.key === '\\') {
        e.preventDefault();
        togglePrivacyMode();
      }
      if (e.key === 'Escape' && openModal) setOpenModal(null);
    };
    const onNewExpense = () => setOpenModal('expense');
    const onTogglePrivacy = () => togglePrivacyMode();
    window.addEventListener('keydown', onKey);
    window.addEventListener('ledger:new-expense', onNewExpense);
    window.addEventListener('ledger:toggle-privacy', onTogglePrivacy);
    return () => {
      window.removeEventListener('keydown', onKey);
      window.removeEventListener('ledger:new-expense', onNewExpense);
      window.removeEventListener('ledger:toggle-privacy', onTogglePrivacy);
    };
  }, [openModal]);

  const privacyMode = usePrivacyMode();

  // Routes that don't scope content by month — the month switcher and
  // the "Jump to current" pill are hidden on these.
  const MONTHLESS_ROUTES = new Set(['home', 'brain', 'tasks', 'settings', 'share', 'onboarding']);
  const hasMonthContext = !MONTHLESS_ROUTES.has(route);

  const renderScreen = () => {
    switch (route) {
      case 'home':      return <HomeScreen state={state} setState={setState} setOpenModal={setOpenModal} setEditingBrainNote={setEditingBrainNote} userId={userId} setRoute={setRoute} />;
      case 'dashboard': return <DashboardScreen state={viewState} setState={setState} setOpenModal={setOpenModal} setRoute={setRoute} />;
      case 'expenses':  return <ExpensesScreen state={viewState} setState={setState} setOpenModal={setOpenModal} setEditingExpense={setEditingExpense} />;
      case 'baseline':  return <BaselineScreen state={viewState} setState={setState} setOpenModal={setOpenModal} setEditingFixed={setEditingFixed} setEditingIncomeSource={setEditingIncomeSource} setActiveSource={setActiveSource} />;
      case 'family':    return <FamilyScreen state={viewState} setState={setState} setOpenModal={setOpenModal} userId={userId} />;
      case 'tasks':     return <TasksScreen state={state} setState={setState} setOpenModal={setOpenModal} setEditingTask={setEditingTask} />;
      case 'brain':     return <BrainDumpScreen state={state} setState={setState} setOpenModal={setOpenModal} setEditingBrainNote={setEditingBrainNote} setPendingBrainDraft={setPendingBrainDraft} userId={userId} />;
      case 'meals':     return <MealsScreen state={state} setState={setState} setOpenModal={setOpenModal} setEditingRecipe={setEditingRecipe} />;
      case 'goals':     return <GoalsScreen state={viewState} setState={setState} setOpenModal={setOpenModal} setEditingGoal={setEditingGoal} setEditingAccount={setEditingAccount} setAccountMode={setAccountMode} />;
      case 'share':     return <ShareHomebookScreen state={state} setState={setState} isDemo={isDemo} />;
      case 'onboarding': return <OnboardingScreen state={state} setState={setState} setRoute={setRoute} />;
      case 'settings':  return <SettingsScreen state={state} setState={setState} theme={theme} setTheme={setTheme} userId={userId} setOpenModal={setOpenModal} />;
      default: return null;
    }
  };

  const titleMap = {
    home: 'Dashboard',
    dashboard: 'Overview',
    expenses: 'Expenses',
    baseline: 'Baseline',
    family: 'Family',
    tasks: 'Chores',
    meals: 'Meals',
    brain: 'Commonplace',
    goals: 'Savings',
    share: 'Share homebook',
    onboarding: 'Get started',
    settings: 'Settings',
  };

  return (
    <>
      <div className={`mac-window ${mobileNavOpen ? 'mobile-nav-open' : ''} ${navHidden ? 'nav-hidden' : ''}`} data-screen-label="Ledger window">
        {/* Draggable strip that lets the user move the Electron window
            from the empty space next to the native traffic lights. */}
        <div className="window-drag" />

        {/* Mobile nav backdrop */}
        <div className="sidebar-backdrop" onClick={() => setMobileNavOpen(false)} />

        {/* Sidebar */}
          <div className="sidebar">
            <div className="sidebar-brand">
              {(() => {
                const primary = (state.family || []).find(m => m.role === 'primary') || (state.family || []).find(m => m.id === 'self');
                const parts = (primary?.name || '').trim().split(/\s+/);
                const last = parts.length > 1 ? parts[parts.length - 1].toLowerCase() : '';
                return (
                  <div className="mark">
                    {last && <span className="mark-line">{last}</span>}
                    <span className="mark-line">homebook</span>
                  </div>
                );
              })()}
              <div className="sub">{TODAY.toLocaleDateString('en', { month: 'long', day: 'numeric', year: 'numeric' })}</div>
            </div>

            {/* "Get started" pin — visible only to users who landed on
                the onboarding screen at least once (new signups), and
                only while any step is still unaddressed. Existing
                households keep their dashboard clean — they never
                visited onboarding, so onboardingShown stays false and
                this never appears. */}
            {shouldShowOnboardingPin(state) && (
              <div className={`nav-item nav-item-onboarding ${route === 'onboarding' ? 'active' : ''}`}
                   onClick={() => { setRoute('onboarding'); setMobileNavOpen(false); }}>
                <span className="nav-glyph">✦</span>
                <span>Get started</span>
                <span className="nav-count nav-count-onboarding">
                  {ONBOARDING_STEPS.filter(s => !isStepAddressed(state, s.id)).length}
                </span>
              </div>
            )}

            {/* Home / Dashboard — top-level, no section header. NAV[0]. */}
            {NAV.slice(0, 1).map(n => (
              <div key={n.id}
                   className={`nav-item ${route === n.id ? 'active' : ''}`}
                   onClick={() => { setRoute(n.id); setMobileNavOpen(false); }}>
                <span className="nav-glyph">{n.glyph}</span>
                <span>{n.label}</span>
              </div>
            ))}

            <div className="sidebar-section">Ledger</div>
            {NAV.slice(1, 5).map(n => (
              <div key={n.id}
                   className={`nav-item ${route === n.id ? 'active' : ''}`}
                   onClick={() => { setRoute(n.id); setMobileNavOpen(false); }}>
                <span className="nav-glyph">{n.glyph}</span>
                <span>{n.label}</span>
                {counts[n.id] && <span className="nav-count">{counts[n.id]}</span>}
              </div>
            ))}

            <div className="sidebar-section">Toolbox</div>
            {NAV.slice(5, 8).map(n => (
              <div key={n.id}
                   className={`nav-item ${route === n.id ? 'active' : ''}`}
                   onClick={() => { setRoute(n.id); setMobileNavOpen(false); }}>
                <span className="nav-glyph">{n.glyph}</span>
                <span>{n.label}</span>
                {counts[n.id] && <span className="nav-count">{counts[n.id]}</span>}
              </div>
            ))}

            <div className="sidebar-section">App</div>
            {/* Share homebook is hidden in demo mode — there's nothing to
                actually share when there's no real account. */}
            {NAV.slice(8).filter(n => !(isDemo && n.id === 'share')).map(n => (
              <div key={n.id}
                   className={`nav-item ${route === n.id ? 'active' : ''}`}
                   onClick={() => { setRoute(n.id); setMobileNavOpen(false); }}>
                <span className="nav-glyph">{n.glyph}</span>
                <span>{n.label}</span>
              </div>
            ))}

            <div className="sidebar-footer">
              <span>v1.0</span>
              <div className="sidebar-footer-actions">
                <button className="theme-toggle" onClick={signOut} title="Sign out">⎋</button>
                <button className="theme-toggle" onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
                  title="Toggle theme">
                  {theme === 'light' ? '◐' : '◑'}
                </button>
              </div>
            </div>
          </div>

          {/* Main content */}
          <div className="main">
            <div className="toolbar">
              <button className="hamburger" onClick={() => setMobileNavOpen(true)} title="Menu" aria-label="Open menu">
                <span></span><span></span><span></span>
              </button>
              <div className="toolbar-title">
                <span className="toolbar-title-text">{titleMap[route]}</span>
                {/* The month switcher only makes sense on screens that
                    actually scope by month. Dashboard summarizes "now,"
                    Commonplace is non-temporal, and Chores rolls on a
                    weekly cadence — none of them need a month picker.
                    Settings / Share / Onboarding ditto. */}
                {hasMonthContext && (
                <div className={`month-switcher ${monthMenuOpen ? 'open' : ''}`}>
                  <button className="month-btn prev" onClick={() => setActiveMonth(shiftMonth(activeMonth, -1))}
                    title="Previous month">‹</button>
                  <button className="month-label" onClick={() => setMonthMenuOpen(!monthMenuOpen)}>
                    <span>{formatMonthKey(activeMonth)}</span>
                    {isCurrentMonth(activeMonth) && <span className="month-badge dot" title="Current month"></span>}
                    <span className="caret">▾</span>
                  </button>
                  <button className="month-btn next" onClick={() => setActiveMonth(shiftMonth(activeMonth, 1))}
                    disabled={isFutureMonth(shiftMonth(activeMonth, 1))}
                    title="Next month">›</button>
                  {monthMenuOpen && (
                    <>
                      <div className="month-menu-backdrop" onClick={() => setMonthMenuOpen(false)} />
                      <div className="month-menu">
                        <div className="month-menu-head">
                          <span>Jump to month</span>
                          <span>{availableMonths.length}</span>
                        </div>
                        {availableMonths.map(mk => {
                          const monthSpend = state.expenses.filter(e => monthKey(e.date) === mk).reduce((s, e) => s + e.amount, 0);
                          return (
                            <button key={mk}
                              className={`month-menu-item ${mk === activeMonth ? 'active' : ''}`}
                              onClick={() => { setActiveMonth(mk); setMonthMenuOpen(false); }}>
                              <span>{formatMonthKey(mk)}</span>
                              <span className="mm-amount">{fmt(monthSpend, { cents: false })}</span>
                              {isCurrentMonth(mk) ? <span className="month-badge dot"></span> : <span style={{ width: 6 }}></span>}
                            </button>
                          );
                        })}
                        <div className="month-menu-foot mono">
                          New month auto-starts on the 1st
                        </div>
                      </div>
                    </>
                  )}
                </div>
                )}
              </div>
              <div className="toolbar-actions">
                <button
                  className={`privacy-toggle ${privacyMode ? 'on' : ''}`}
                  onClick={togglePrivacyMode}
                  aria-pressed={privacyMode}
                  aria-label="Toggle privacy mode">
                  <span className="privacy-toggle-label">Privacy</span>
                  <span className="kbd privacy-toggle-kbd">⌘\</span>
                  <span className="privacy-toggle-switch">
                    <span className="privacy-toggle-knob" />
                  </span>
                </button>
                {hasMonthContext && !isCurrentMonth(activeMonth) && (
                  <button className="btn ghost" onClick={() => setActiveMonth(currentMonthKey())}>
                    Jump to current
                  </button>
                )}
                {(route === 'dashboard' || route === 'expenses') && (
                  <button className="btn primary" onClick={() => setOpenModal('expense')}>
                    + New expense <span className="kbd" style={{ color: 'rgba(255,255,255,0.6)' }}>⌘N</span>
                  </button>
                )}
              </div>
            </div>

            <div className="scrollarea" key={route + activeMonth}>
              {renderScreen()}
            </div>
          </div>

        {/* Floating action button. Visible on every breakpoint now. On the
            dashboard it opens a quick-add menu (expense / commonplace / recipe
            / grocery); on every other screen it fires the route's primary
            add modal directly. */}
        {(() => {
          const FAB_ACTIONS = {
            dashboard: { modal: 'expense', label: 'New expense' },
            expenses:  { modal: 'expense', label: 'New expense' },
            baseline:  { modal: 'extra',   label: 'Log extra income' },
            goals:     { modal: 'goal',    label: 'New savings goal' },
            tasks:     { modal: 'task',    label: 'New chore' },
            meals:     { modal: 'recipe',  label: 'New recipe' },
            brain:     { modal: 'brain-note', label: 'Jot a note' },
            family:    { modal: 'member',  label: 'Add family member' },
          };
          if (route === 'home') {
            return <DashboardFab setState={setState} setOpenModal={setOpenModal} />;
          }
          const action = FAB_ACTIONS[route];
          if (!action) return null;
          return (
            <button className="fab" onClick={() => setOpenModal(action.modal)} title={action.label} aria-label={action.label}>
              <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round">
                <line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
              </svg>
            </button>
          );
        })()}

        {/* Modals */}
        {openModal === 'expense' && <ExpenseModal state={state} setState={setState} setActiveMonth={setActiveMonth} editing={editingExpense} onClose={() => { setOpenModal(null); setEditingExpense(null); }} />}
        {openModal === 'fixed'   && <FixedModal   state={state} setState={setState} editing={editingFixed} onClose={() => { setOpenModal(null); setEditingFixed(null); }} />}
        {openModal === 'extra'   && <ExtraIncomeModal state={state} setState={setState} setActiveMonth={setActiveMonth} onClose={() => setOpenModal(null)} />}
        {openModal === 'goal'    && <GoalModal    state={state} setState={setState} editing={editingGoal} onClose={() => { setOpenModal(null); setEditingGoal(null); }} />}
        {openModal === 'task'    && <TaskModal    state={state} setState={setState} editing={editingTask} onClose={() => { setOpenModal(null); setEditingTask(null); }} />}
        {openModal === 'brain-note' && <BrainNoteModal state={state} setState={setState} editingId={editingBrainNote} userId={userId} pendingDraft={pendingBrainDraft} onClose={() => { setOpenModal(null); setEditingBrainNote(null); setPendingBrainDraft(null); }} />}
        {openModal === 'member'  && <MemberModal  state={state} setState={setState} onClose={() => setOpenModal(null)} />}
        {openModal === 'recipe'  && <RecipeModal  state={state} setState={setState} editing={editingRecipe} onClose={() => { setOpenModal(null); setEditingRecipe(null); }} />}
        {openModal === 'income-source' && <IncomeSourceModal state={state} setState={setState} editing={editingIncomeSource} onClose={() => { setOpenModal(null); setEditingIncomeSource(null); }} />}
        {openModal === 'paycheck' && activeSource && <PaycheckModal state={state} setState={setState} source={activeSource} onClose={() => { setOpenModal(null); setActiveSource(null); }} />}
        {openModal === 'account' && <AccountModal state={state} setState={setState} editing={editingAccount} mode={accountMode} onClose={() => { setOpenModal(null); setEditingAccount(null); setAccountMode('full'); }} />}
        {openModal === 'invite'  && <InviteToHouseholdModal state={state} onClose={() => setOpenModal(null)} />}
        {openModal === 'primary-invite' && <PrimaryInviteModal state={state} onClose={() => setOpenModal(null)} />}
        {openModal === 'reconcile' && <ReconcileModal state={state} setState={setState} activeMonth={activeMonth} onClose={() => setOpenModal(null)} />}
      </div>

      {/* Tweaks */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="Theme" />
        <TweakColor label="Accent" value={tweak.accent} onChange={v => setTweak('accent', v)} />
        <TweakRadio label="Mode" value={theme} options={['light', 'dark']}
                    onChange={v => setTheme(v)} />

        <TweakSection label="Typography" />
        <TweakSelect label="Display" value={tweak.displayFont}
                     options={FONT_DISPLAY_OPTIONS}
                     onChange={v => setTweak('displayFont', v)} />
        <TweakSelect label="UI" value={tweak.uiFont}
                     options={FONT_UI_OPTIONS}
                     onChange={v => setTweak('uiFont', v)} />

        <TweakSection label="Layout" />
        <TweakRadio label="Density" value={tweak.density}
                    options={['compact', 'regular', 'comfy']}
                    onChange={v => setTweak('density', v)} />

        <TweakSection label="Data" />
        <TweakButton label="Open expense modal" onClick={() => setOpenModal('expense')} />
        <TweakButton label="Wipe all data" secondary onClick={() => {
          if (confirm('Erase every expense, fixed cost, goal, and member? This cannot be undone.')) {
            setState(INITIAL_STATE);
          }
        }} />
      </TweaksPanel>

      {/* Floating exit-demo pill — fixed bottom-left while in demo mode.
          Visible on every screen + viewport so users can always leave demo
          without hunting through the sidebar. Red (--neg) signals "leave"
          semantically; entry via teal "View demo" remains separate. */}
      {isDemo && !navHidden && (
        <button className="demo-exit-pill" onClick={signOut}
                title="Exit demo and return to sign in"
                aria-label="Exit demo mode">
          <span className="demo-exit-dot" aria-hidden />
          <span>Exit demo</span>
        </button>
      )}
    </>
  );
}

Object.assign(window, { LedgerApp });
