/* Nutri unified App — mobile-only, Firebase-auth-gated, Firestore-backed.
   Pre-auth: LoginScreen. Post-auth: the same multi-project shell. */

function NutriApp() {
  const auth = useAuth();
  const store = useStore();
  const [theme, setTheme] = React.useState(window.__nutriTheme || 'light');
  React.useEffect(() => {
    const onTheme = (e) => setTheme(e.detail);
    window.addEventListener('nutri:theme', onTheme);
    return () => window.removeEventListener('nutri:theme', onTheme);
  }, []);

  // ── Gate: show splash while auth is resolving, login when signed out ──
  if (auth.loading) return <SplashScreen/>;
  if (!auth.user)   return <LoginScreen/>;
  if (!store.uid()) return <SplashScreen msg="Loading your data…"/>;

  return <NutriShell theme={theme}/>;
}

function SplashScreen({ msg }) {
  return (
    <div style={{
      flex: 1, display: 'grid', placeItems: 'center', padding: 24,
      background: 'radial-gradient(80% 60% at 50% -10%, rgba(31,61,46,0.08), transparent 70%), var(--bg)',
    }}>
      <div style={{ textAlign: 'center' }}>
        <div style={{
          display: 'inline-block', margin: '0 auto 14px',
          animation: 'pulse 1.4s ease-in-out infinite',
        }}>
          <BrandMark size={72} radius={18}/>
        </div>
        <div style={{ fontSize: 13, color: 'var(--muted)' }}>{msg || 'Starting Nutri…'}</div>
      </div>
    </div>
  );
}

function NutriShell({ theme }) {
  const store = useStore();
  const [projectId, setProjectId] = React.useState('calories');
  const [tab, setTab] = React.useState('home');
  const pendingTabRef = React.useRef(null);
  // On project switch, reset to the project's default tab — unless a deep-link set a pending tab.
  React.useEffect(() => { setTab(pendingTabRef.current || 'home'); pendingTabRef.current = null; }, [projectId]);
  // One-time: merge the full default finance category seed into the user's data.
  React.useEffect(() => {
    if (store.uid && store.uid() && store.ensureSeedCategories) {
      store.ensureSeedCategories(NF_CATEGORIES_SEED, NF_CATEGORIES_LEGACY);
    }
    // One-time Movie Night repair: dedupe + set watched status on CSV-imported movies.
    if (store.uid && store.uid() && store.ensureMoviesRepaired) {
      store.ensureMoviesRepaired();
    }
  }, [store.uid && store.uid()]);

  // Re-sync from cloud when the tab regains visibility (user switches back from another tab/device).
  // Lightweight: only fires if hidden for > 3 minutes to avoid hammering Firestore.
  React.useEffect(() => {
    if (!store.uid || !store.uid()) return;
    let hiddenAt = null;
    const onVis = () => {
      if (document.visibilityState === 'hidden') { hiddenAt = Date.now(); return; }
      if (!hiddenAt) return;
      const awayMs = Date.now() - hiddenAt;
      hiddenAt = null;
      if (awayMs < 3 * 60 * 1000) return; // < 3 min: no refresh needed
      // Reload all collections (finance, diary, goals, movies, settings) for cross-device sync.
      if (store.reloadAllFromCloud) {
        store.reloadAllFromCloud().catch(() => {});
      } else if (store.reloadMoviesFromCloud) {
        store.reloadMoviesFromCloud().catch(() => {});
      }
    };
    document.addEventListener('visibilitychange', onVis);
    return () => document.removeEventListener('visibilitychange', onVis);
  }, [store.uid && store.uid()]);

  // Deep-link navigation (e.g. tapping a notification) → switch project + tab.
  React.useEffect(() => {
    const onNav = (e) => {
      const d = (e && e.detail) || {};
      if (!d.project) return;
      if (d.project === projectId) { if (d.tab) setTab(d.tab); }
      else { pendingTabRef.current = d.tab || null; setProjectId(d.project); }
    };
    window.addEventListener('nutri:navigate', onNav);
    return () => window.removeEventListener('nutri:navigate', onNav);
  }, [projectId]);

  // Local notification scheduler — fires Diary "On This Day" + Goals reminders while the app
  // is open (web/PWA has no server push). Checks each minute; once-per-day via localStorage.
  React.useEffect(() => {
    if (!store.uid || !store.uid()) return;
    const tick = () => { try { runNutriNotificationCheck(store); } catch (_) {} };
    const t0 = setTimeout(tick, 4000);            // shortly after load
    const iv = setInterval(tick, 60000);          // every minute
    return () => { clearTimeout(t0); clearInterval(iv); };
  }, [store.uid && store.uid()]);

  // ── PWA update banner ──────────────────────────────────────────────
  const [updateReady, setUpdateReady] = React.useState(false);
  React.useEffect(() => {
    const onUpdate = () => setUpdateReady(true);
    window.addEventListener('nutri:update-ready', onUpdate);
    return () => window.removeEventListener('nutri:update-ready', onUpdate);
  }, []);
  function applyUpdate() {
    if (navigator.serviceWorker && navigator.serviceWorker.controller) {
      navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING' });
    } else {
      window.location.reload();
    }
  }

  const [sheet, setSheet] = React.useState(null);
  const [toast, setToast] = React.useState(null);
  const [dupDialog, setDupDialog] = React.useState(null); // { row, date, existingCount, toastMsg }
  const [rateRefreshing, setRateRefreshing] = React.useState(false);
  const [rateError, setRateError] = React.useState(null);
  const [goldRefreshing, setGoldRefreshing] = React.useState(false);
  const [goldError, setGoldError] = React.useState(null);
  const closeSheet = () => setSheet(null);
  function flashToast(text, kind='ok') {
    setToast({ text, kind });
    setTimeout(() => setToast(null), 2200);
  }

  // ── Live data: each render recomputes; Store mutations re-render us ──
  const calorieEntries = store.meals().map(e => {
    const items = Array.isArray(e.items) ? e.items : [];
    const calFromItems = items.reduce((s, it) => s + (+it.calories || +it.cal || 0), 0);
    return {
      id: e.id,
      date: e.date,
      meal: (e.meal || 'snack').toLowerCase(),
      name: e.name || items.map(i => i.name).filter(Boolean).join(', ') || (e.meal || 'Meal'),
      emoji: e.emoji || NUTRI_MEAL_ICON[(e.meal || '').toLowerCase()] || '🍽️',
      time: e.time || '',
      calories: +e.calories || calFromItems || 0,
      protein_g: +e.protein_g || 0,
      carbs_g: +e.carbs_g || 0,
      fat_g: +e.fat_g || 0,
      amount: +e.amount || null,
      unit:   e.unit   || 'g',
      note:   e.note   || '',
      source: e.source || 'manual',
      sourceId: e.sourceId || null,
      items: items.map(it => ({
        name: it.name, qty: it.amount || it.qty || '',
        cal: +it.calories || +it.cal || 0,
        protein_g: it.protein_g !== undefined ? it.protein_g : '',
        carbs_g:   it.carbs_g   !== undefined ? it.carbs_g   : '',
        fat_g:     it.fat_g     !== undefined ? it.fat_g     : '',
      })),
    };
  });
  const calorieGoals = (() => {
    const g = store.macroGoals() || {};
    return {
      calories: +g.calories || NUTRI_GOALS.calories,
      protein_g: +g.protein_g || NUTRI_GOALS.protein_g,
      carbs_g: +g.carbs_g || NUTRI_GOALS.carbs_g,
      fat_g: +g.fat_g || NUTRI_GOALS.fat_g,
    };
  })();
  // ── USD/EGP rate (stored in settings) ──────────────────────────────
  const _settings = store.settings() || {};
  const usdRate = _settings.usdEgpRate ? +_settings.usdEgpRate : null;
  const usdRateUpdatedAt = _settings.usdEgpRateUpdatedAt || null;

  // ── Gold prices & holdings ──────────────────────────────────────────
  const goldPrices = _settings.goldPrices || null;
  const goldPricesUpdatedAt = _settings.goldPricesUpdatedAt || null;
  const goldHoldings = store.goldHoldings ? store.goldHoldings() : [];
  const financeProjects = store.financeProjects ? store.financeProjects() : [];
  const financeFixed = store.financeFixed ? store.financeFixed() : [];

  // ── Live finance categories & accounts ─────────────────────────────
  // Merge the full default seed into the user's categories (never deletes custom
  // cats; legacy ids preserved for users with existing txns). Persisted once via effect.
  const _storedCats = store.financeCategories();
  const _hasFinTxns = (store.finance() || []).length > 0;
  const _catBase = _storedCats ? _storedCats : (_hasFinTxns ? NF_CATEGORIES_LEGACY : []);
  const financeCategories = mergeFinanceCategories(_catBase, NF_CATEGORIES_SEED);
  const financeAccountsRaw = store.financeAccounts() || NF_ACCOUNTS;

  // Sync globals so TxnCard / TxnDetailSheet use live data
  // (safe to do before JSX — these are module-level `let` vars in nutri-data.jsx)
  if (window.NF_CATEGORIES !== financeCategories) window.NF_CATEGORIES = NF_CATEGORIES = financeCategories;

  const financeTxns = store.finance().map(e => {
    const catId = financeCategories.find(c => c.name === e.category || c.id === e.category);
    const kind = e.kind || 'expense';
    const txnCurrency = e.currency || '';
    const rawAmount = Math.abs(+e.amount);
    // Compute EGP equivalent for non-EGP transactions
    let egpEquivalent = null;
    if (kind !== 'transfer') {
      if (!txnCurrency || txnCurrency === 'EGP') {
        egpEquivalent = rawAmount;
      } else if (e.egpEquivalent) {
        egpEquivalent = +e.egpEquivalent;
      } else if (e.exchangeRate && +e.exchangeRate > 0) {
        egpEquivalent = +(rawAmount * +e.exchangeRate).toFixed(2);
      } else if (txnCurrency === 'USD' && usdRate) {
        egpEquivalent = +(rawAmount * usdRate).toFixed(2);
      } else {
        egpEquivalent = rawAmount;
      }
    }
    return {
      id: e.id, date: e.date, title: e.title || e.category || 'Transaction',
      category: catId ? catId.id : (e.category || 'shopping'),
      subcategory: e.subcategory || '',
      amount: kind === 'income' ? +e.amount : kind === 'transfer' ? +e.amount : -Math.abs(+e.amount),
      kind,
      account: e.account || e.fromAccount || 'cash',
      fromAccount: e.fromAccount || '',
      toAccount:   e.toAccount   || '',
      convertedAmount: +(e.convertedAmount || e.amount) || 0,
      exchangeRate:    +(e.exchangeRate)    || 1,
      currency:   txnCurrency,
      toCurrency: e.toCurrency || '',
      egpEquivalent,
      time: e.time || '',
      notes: e.notes || '',
      project: e.project || '',
      projectIds: txnProjectIds(e),
      fixedId: e.fixedId || '',
      fixedDueDate: e.fixedDueDate || '',
    };
  });

  // Compute per-account income/expense totals + balance (transfers move money between accounts)
  const financeAccountsWithBalance = financeAccountsRaw.map(a => {
    const acctCurrency = a.currency || 'EGP';
    const income      = financeTxns.filter(t => t.account === a.id && t.kind === 'income').reduce((s,t) => s + Math.abs(t.amount), 0);
    const expenses    = financeTxns.filter(t => t.account === a.id && t.kind === 'expense').reduce((s,t) => s + Math.abs(t.amount), 0);
    const transferOut = financeTxns.filter(t => t.kind === 'transfer' && t.fromAccount === a.id).reduce((s,t) => s + Math.abs(t.amount), 0);
    const transferIn  = financeTxns.filter(t => t.kind === 'transfer' && t.toAccount   === a.id).reduce((s,t) => s + Math.abs(t.convertedAmount || t.amount), 0);
    const balance = (a.startingBalance || 0) + income - expenses - transferOut + transferIn;
    // EGP equivalent balance for net worth calculations
    let egpBalance = balance;
    if (acctCurrency !== 'EGP') {
      if (acctCurrency === 'USD' && usdRate) egpBalance = balance * usdRate;
      // other currencies: keep as-is (no rate available)
    }
    return { ...a, income, expenses, balance, egpBalance };
  });
  if (window.NF_ACCOUNTS !== financeAccountsWithBalance) window.NF_ACCOUNTS = NF_ACCOUNTS = financeAccountsWithBalance;
  const goalsList = store.goals().map(g => {
    const startDate  = g.startDate || NUTRI_TODAY;
    const durDays    = +g.durationDays || 0;
    let   endDate    = g.endDate || null;
    if (!endDate && durDays > 0) {
      const d = new Date(startDate + 'T00:00:00');
      d.setDate(d.getDate() + durDays);
      endDate = isoOf(d);
    }
    return {
      id: g.id,
      title: g.name || g.title || 'Goal',
      emoji: g.emoji || '🎯',
      target:      +g.timesPerDay || +g.target || 1,
      unit:         g.unit || (+g.timesPerDay > 1 ? 'times' : 'session'),
      timesPerDay: +g.timesPerDay || +g.target || 1,
      streak: 0,
      color: g.color || '#1F3D2E',
      weekdays: Array.isArray(g.weekdays) ? g.weekdays : [0,1,2,3,4,5,6],
      startDate,
      endDate,
      durationDays: durDays,
      _raw: g,
    };
  });
  const goalLogs = (() => {
    const out = {};
    store.goals().forEach(g => {
      const log = g.log || {};
      Object.entries(log).forEach(([date, slots]) => {
        const arr = Array.isArray(slots) ? slots : [];
        const doneCount = arr.filter(s => s === 'done').length;
        if (!out[date]) out[date] = {};
        out[date][g.id] = doneCount;
      });
    });
    return out;
  })();
  const diaryEntries = store.diary().map(e => {
    const pad = n => String(n).padStart(2, '0');
    const dateKey = e.dateKey
      || (typeof e.day === 'number' && typeof e.month === 'number' && typeof e.year === 'number'
            ? `${e.year}-${pad(e.month)}-${pad(e.day)}`
            : (e.date || ''));
    return {
      id: e.id, date: dateKey,
      title: e.title && e.title.trim() !== 'Untitled' ? e.title : '',
      mood: e.mood || ((!e.title || e.title.trim() === 'Untitled' || e.title.trim() === '') ? 'great' : 'okay'),
      tags: Array.isArray(e.tags) ? e.tags : [],
      text: e.eventText || e.text || '',
      entryMode: e.entryMode || 'full',
      movieId: e.movieId || null,
    };
  });

  // Custom diary tags: from settings + any tags used in past entries that aren't built-in
  const diaryCustomTags = React.useMemo(() => {
    const fromSettings = ((store.settings() || {}).diaryCustomTags) || [];
    const fromEntries = [...new Set(diaryEntries.flatMap(e => e.tags || []))]
      .filter(t => !ND_TAG_COLORS[t]);
    return [...new Set([...fromSettings, ...fromEntries])];
  }, [diaryEntries]);

  const localLibraryItems = store.localLibrary ? store.localLibrary() : [];

  // ── Project & tabs ──
  const project = NUTRI_PROJECTS.find(p => p.id === projectId);
  const TABS_BY_PROJECT = {
    calories: [
      { id: 'home',     label: 'Today',    icon: HomeIcon },
      { id: 'calendar', label: 'Calendar', icon: CalendarIcon },
      { id: 'progress', label: 'Stats',    icon: ChartIcon },
      { id: 'inbody',   label: 'InBody',   icon: ScaleIcon },
      { id: 'recipes',  label: 'Recipes',  icon: RecipeIcon },
      { id: 'paste',    label: 'Paste',    icon: ListIcon },
    ],
    finance: [
      { id: 'home',       label: 'Home',     icon: HomeIcon    },
      { id: 'txns',       label: 'Txns',     icon: ListIcon    },
      { id: 'plan',       label: 'Plan',     icon: CalendarIcon},
      { id: 'accounts',   label: 'Accounts', icon: WalletIcon  },
      { id: 'categories', label: 'Categ.',   icon: TagIcon     },
      { id: 'insights',   label: 'Insights', icon: ChartIcon   },
      { id: 'paste',      label: 'Paste',    icon: ListIcon    },
    ],
    goals: [
      { id: 'home',     label: 'Today',    icon: HomeIcon },
      { id: 'review',   label: 'Review',   icon: EditIcon },
      { id: 'calendar', label: 'Calendar', icon: CalendarIcon },
      { id: 'progress', label: 'Progress', icon: ChartIcon },
      { id: 'paste',    label: 'Paste',    icon: ListIcon },
    ],
    diary: [
      { id: 'home',     label: 'Home',     icon: HomeIcon },
      { id: 'calendar', label: 'Calendar', icon: CalendarIcon },
      { id: 'timeline', label: 'Timeline', icon: ListIcon },
      { id: 'stats',    label: 'Stats',    icon: ChartIcon },
      { id: 'paste',    label: 'Paste',    icon: ListIcon },
    ],
    movies: [
      { id: 'browse',    label: 'Browse',    icon: SearchIcon },
      { id: 'cards',     label: 'Cards',     icon: GridIcon },
      { id: 'watchlist', label: 'Watchlist', icon: BookmarkNavIcon },
      { id: 'lists',     label: 'Lists',     icon: ListIcon },
      { id: 'liked',     label: 'Liked',     icon: HeartNavIcon },
      { id: 'watched',   label: 'Watched',   icon: EyeNavIcon },
    ],
    assistant: [{ id: 'home', label: 'Chat',     icon: SparklesIcon }],
    settings:  [{ id: 'home', label: 'Settings', icon: SettingsIcon }],
  };

  // Always use the live local date — never the module-level NUTRI_TODAY constant which
  // is stale if the page was loaded yesterday (e.g. user opened app before midnight).
  const [selectedDate, setSelectedDate] = React.useState(() => todayISO());

  // Midnight-boundary guard: if the user keeps the app open and the clock ticks past
  // midnight, advance selectedDate to the new "today" so Home/Calendar stay in sync.
  React.useEffect(() => {
    let timer = null;
    function scheduleNextCheck() {
      const now = new Date();
      const msUntilMidnight =
        (24 * 60 * 60 * 1000) -
        (now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds()) * 1000 -
        now.getMilliseconds() + 1000; // +1s buffer after midnight
      timer = setTimeout(() => {
        // Capture the old date right before advancing (the date that just ended).
        const prevToday = isoOf(new Date(Date.now() - 2000)); // 2s before = still "old today"
        const newToday = todayISO();
        // Only advance if the user was on "yesterday" (the just-ended day) — don't
        // clobber a deliberately selected past/future date.
        setSelectedDate(prev => prev === prevToday ? newToday : prev);
        scheduleNextCheck();
      }, msUntilMidnight);
    }
    scheduleNextCheck();
    return () => clearTimeout(timer);
  }, []);

  function renderScreen() {
    if (projectId === 'calories') {
      if (tab === 'home') return (
        <CaloriesHome
          entries={calorieEntries}
          goals={calorieGoals}
          localLibrary={localLibraryItems}
          selectedDate={selectedDate}
          setSelectedDate={setSelectedDate}
          onAdd={openAddPicker}
          onEditGoals={() => setSheet({ kind: 'editGoals' })}
          onLogFood={(food) => setSheet({ kind: 'logFood', data: food })}
          onLogAgain={(entry) => setSheet({ kind: 'logFood', data: entry })}
          onOpenMeal={(m) => setSheet({ kind: 'meal', data: m })}
          onAddLocalItem={() => setSheet({ kind: 'addLocalItem' })}
          onEditLocalItem={(item) => setSheet({ kind: 'addLocalItem', data: item })}
          onDeleteLocalItem={(id) => { store.deleteLocalItem(id); flashToast('Removed'); }}/>
      );
      if (tab === 'calendar') return (
        <CaloriesCalendar
          entries={calorieEntries}
          goals={calorieGoals}
          notes={((store.settings() || {}).notes) || {}}
          onLogFood={(food) => setSheet({ kind: 'logFood', data: food })}
          onOpenMeal={(m) => setSheet({ kind: 'meal', data: m })}/>
      );
      if (tab === 'progress') return (
        <CaloriesStats
          entries={calorieEntries}
          goals={calorieGoals}
          weights={((store.settings() || {}).weights) || {}}
          onCleanup={() => setSheet({ kind: 'cleanupCalories' })}/>
      );
      if (tab === 'recipes') return (
        <RecipesScreen
          localLibrary={localLibraryItems}
          entries={calorieEntries}
          selectedDate={selectedDate}
          onLogMeals={(row) => _checkAndSaveMeal(row, 'Recipe logged ✓')}
        />
      );
      if (tab === 'inbody') return <InBodyScreen flashToast={flashToast}/>;
      if (tab === 'paste') return <PasteParseScreen projectType="diet"/>;
    }
    if (projectId === 'finance') {
      if (tab === 'home') return (
        <FinanceHome
          txns={financeTxns}
          accounts={financeAccountsWithBalance}
          onOpenTxn={(t) => setSheet({ kind: 'txn', data: t })}
          onAdd={openAddPicker}
          onEditAccount={(a) => setSheet({ kind: 'editAccount', data: a })}
          loading={store.syncStatus && store.syncStatus() === 'loading'}/>
      );
      if (tab === 'txns') return (
        <FinanceTxns txns={financeTxns}
          categories={financeCategories}
          accounts={financeAccountsWithBalance}
          onOpenTxn={(t) => setSheet({ kind: 'txn', data: t })}
          onAdd={openAddPicker}/>
      );
      if (tab === 'plan') return (
        <FinancePlanTab
          projects={financeProjects}
          fixed={financeFixed}
          txns={financeTxns}
          categories={financeCategories}
          accounts={financeAccountsWithBalance}
          usdRate={usdRate}
          onAddProject={() => setSheet({ kind: 'editProject', data: { isNew: true } })}
          onEditProject={(p) => setSheet({ kind: 'editProject', data: { project: p, isNew: false } })}
          onOpenProject={(p) => setSheet({ kind: 'projectDetail', data: p })}
          onAddFixed={() => setSheet({ kind: 'editFixed', data: { isNew: true } })}
          onEditFixed={(fx) => setSheet({ kind: 'editFixed', data: { fixed: fx, isNew: false } })}
          onMarkPaid={(fixedId, dueDate) => setSheet({ kind: 'markPaid', data: { fixedId, dueDate } })}/>
      );
      if (tab === 'accounts') return (
        <FinanceAccountsTab
          accounts={financeAccountsWithBalance}
          txns={financeTxns}
          onAddAccount={() => setSheet({ kind: 'addAccount' })}
          onEditAccount={(a) => setSheet({ kind: 'editAccount', data: a })}
          onAdjustAccount={(a) => setSheet({ kind: 'adjustAccount', data: a })}
          usdRate={usdRate}
          usdRateUpdatedAt={usdRateUpdatedAt}
          rateRefreshing={rateRefreshing}
          rateError={rateError}
          onRefreshRate={refreshUsdRate}
          onManualRate={setManualUsdRate}
          goldHoldings={goldHoldings}
          goldPrices={goldPrices}
          goldPricesUpdatedAt={goldPricesUpdatedAt}
          goldRefreshing={goldRefreshing}
          goldError={goldError}
          onRefreshGold={refreshGoldPrices}
          onManualGoldPrice={setManualGoldPrice}
          onAddGolding={() => setSheet({ kind: 'addGold' })}
          onEditGolding={(h) => setSheet({ kind: 'editGold', data: h })}/>
      );
      if (tab === 'categories') return (
        <FinanceCategories
          categories={financeCategories}
          txns={financeTxns}
          onAddCategory={() => setSheet({ kind: 'editCategory', data: { isNew: true } })}
          onEditCategory={(cat) => setSheet({ kind: 'editCategory', data: { cat, isNew: false } })}
          onDeleteCategory={deleteFinanceCategory}
          onAddSubcategory={(catId) => setSheet({ kind: 'editSubcategory', data: { catId, isNew: true } })}
          onEditSubcategory={(catId, sub) => setSheet({ kind: 'editSubcategory', data: { catId, sub, isNew: false } })}
          onDeleteSubcategory={deleteFinanceSubcategory}
          onManageIncomeSources={() => setTab('income-sources')}/>
      );
      if (tab === 'income-sources') return (
        <IncomeSourcesTab
          categories={financeCategories}
          txns={financeTxns}
          onAddSource={quickAddIncomeSource}
          onRenameSource={(subId, newName) => renameFinanceSubcategory('income', subId, newName)}
          onDeleteSource={(catId, subId) => deleteFinanceSubcategory(catId, subId)}
          onBack={() => setTab('categories')}/>
      );
      if (tab === 'insights') return <FinanceInsights txns={financeTxns} accounts={financeAccountsWithBalance} categories={financeCategories} onCleanup={() => setSheet({ kind: 'cleanupFinance' })}/>;
      if (tab === 'paste') return <PasteParseScreen projectType="finance"/>;
    }
    if (projectId === 'goals') {
      const goalInc = (g) => adjustGoalLogToday(g.id, +1);
      const goalDec = (g) => adjustGoalLogToday(g.id, -1);
      const goalOpen = (g) => setSheet({ kind: 'goal', data: g });
      const goalPickDate = (date) => { setSelectedDate(date); setTab('review'); };
      if (tab === 'home') return (
        <GoalsHome goals={goalsList} logs={goalLogs}
          onOpenGoal={goalOpen}
          onAdd={openAddPicker}
          onInc={goalInc} onDec={goalDec}
          onPickDate={goalPickDate}/>
      );
      if (tab === 'review') return (
        <GoalsDayReview goals={goalsList} logs={goalLogs}
          selectedDate={selectedDate}
          onSetDate={setSelectedDate}
          onAdjust={(goalId, date, delta) => adjustGoalLogForDate(goalId, date, delta)}
          onSkip={(goalId, date) => markGoalSkipped(goalId, date)}
          onOpenGoal={goalOpen}/>
      );
      if (tab === 'calendar') return (
        <GoalsCalendar goals={goalsList} logs={goalLogs}
          selectedDate={selectedDate}
          onPickDate={goalPickDate}
          onAdjust={(goalId, date, delta) => adjustGoalLogForDate(goalId, date, delta)}/>
      );
      if (tab === 'progress') return <GoalsProgress goals={goalsList} logs={goalLogs}/>;
      if (tab === 'paste') return <PasteParseScreen projectType="goal"/>;
    }
    if (projectId === 'diary') {
      const diaryOpen = (e) => setSheet({ kind: 'diary', data: e });
      const diaryCompose = () => setSheet({ kind: 'addDiary' });
      if (tab === 'home') return (
        <DiaryHome entries={diaryEntries}
          onOpen={diaryOpen}
          onCompose={diaryCompose}/>
      );
      if (tab === 'calendar') return (
        <DiaryCalendar entries={diaryEntries}
          onOpen={diaryOpen}
          onCompose={diaryCompose}/>
      );
      if (tab === 'timeline') return (
        <DiaryTimeline entries={diaryEntries}
          onOpen={diaryOpen}
          onCompose={diaryCompose}/>
      );
      if (tab === 'stats') return (
        <DiaryStats entries={diaryEntries}/>
      );
      if (tab === 'paste') return <PasteParseScreen projectType="diary"/>;
    }
    if (projectId === 'movies') {
      const movieCb = {
        onOpenMovie:    (m) => setSheet({ kind: 'movieDetail', data: m }),
        onOpenSettings: () => setSheet({ kind: 'movieSettings' }),
        onAddManual:    () => setSheet({ kind: 'addMovie' }),
        onImportCsv:    () => setSheet({ kind: 'movieCsvImport' }),
        onPickList:     (movieId, opts) => setSheet({ kind: 'movieListPicker', data: { movieId, onResolve: opts && opts.onResolve } }),
        flashToast,
      };
      let inner = null;
      if (tab === 'browse' || tab === 'home') inner = <MoviesBrowse {...movieCb}/>;
      else if (tab === 'cards')     inner = <MoviesCards {...movieCb}/>;
      else if (tab === 'watchlist') inner = <MoviesWatchlist {...movieCb}/>;
      else if (tab === 'lists')     inner = <MoviesLists {...movieCb}/>;
      else if (tab === 'liked')     inner = <MoviesLiked {...movieCb}/>;
      else if (tab === 'watched')   inner = <MoviesWatched {...movieCb}/>;
      if (inner) return <><MovieSyncBanner/>{inner}</>;
    }
    if (projectId === 'assistant') return <AssistantScreen/>;
    if (projectId === 'settings')  return <SettingsScreen/>;
    return <div style={{ padding: 24, color: 'var(--muted)' }}>No screen for {projectId}/{tab}</div>;
  }

  function openAddPicker() {
    if (projectId === 'assistant' || projectId === 'settings') return;
    setSheet({ kind: 'addPicker' });
  }
  function pickPaste() { setSheet(null); setTab('paste'); }
  function pickManual() {
    if (projectId === 'calories')      setSheet({ kind: 'addCal' });
    else if (projectId === 'finance')  setSheet({ kind: 'addTxn' });
    else if (projectId === 'goals')    setSheet({ kind: 'addGoal' });
    else if (projectId === 'diary')    setSheet({ kind: 'addDiary' });
    else if (projectId === 'movies')   setSheet({ kind: 'addMovie' });
  }
  function pickTransfer() { setSheet({ kind: 'addTransfer' }); }

  // ── Shared duplicate-check helper ──
  function _checkAndSaveMeal(row, toastMsg) {
    const date = row.date || NUTRI_TODAY;
    const existing = store.meals().filter(m => m.date === date);
    if (existing.length > 0) {
      closeSheet();
      setDupDialog({ row, date, existingCount: existing.length, toastMsg: toastMsg || 'Logged ✓' });
    } else {
      store.addMeals([row]);
      flashToast(toastMsg || 'Logged ✓');
      closeSheet();
    }
  }

  // ── Save handlers (write through Store, which syncs to Firestore) ──
  function saveCalorieEntry(form) {
    const id = 'cal_' + Date.now().toString(36) + Math.random().toString(36).slice(2,5);
    const row = {
      id,
      date: form.date || NUTRI_TODAY,
      meal: (form.meal || 'snack').toLowerCase(),
      name: form.name || '',
      emoji: form.emoji || NUTRI_MEAL_ICON[form.meal] || '🍽️',
      time: form.time || new Date().toTimeString().slice(0,5),
      calories: +form.calories || 0,
      carbs_g:   +form.carbs_g   || 0,
      protein_g: +form.protein_g || 0,
      fat_g:     +form.fat_g     || 0,
      items: form.items || [],
    };
    _checkAndSaveMeal(row, 'Meal saved');
  }

  // ── Log food from library (LogFoodSheet output) ──
  function handleSaveLoggedFood(form) {
    const id = 'cal_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 5);
    const row = {
      id,
      date:      form.date     || NUTRI_TODAY,
      meal:      (form.meal    || 'snack').toLowerCase(),
      name:      form.name     || 'Meal',
      emoji:     form.emoji    || NUTRI_MEAL_ICON[(form.meal || '').toLowerCase()] || '🍽️',
      time:      form.time     || new Date().toTimeString().slice(0, 5),
      calories:  +form.calories  || 0,
      carbs_g:   +form.carbs_g   || 0,
      protein_g: +form.protein_g || 0,
      fat_g:     +form.fat_g     || 0,
      amount:    form.amount   || null,
      unit:      form.unit     || null,
      note:      form.note     || '',
      source:    form.source   || 'manual',
      sourceId:  form.sourceId || null,
      items: [],
    };
    _checkAndSaveMeal(row, 'Logged ✓');
  }

  // ── Macro goals ──
  function saveMacroGoals(goals) {
    store.setMacroGoals(goals);
    flashToast('Goals updated');
    closeSheet();
  }

  // ── Local library item save/update ──
  function saveLocalItem(item) {
    const lib = store.localLibrary ? store.localLibrary() : [];
    if (item.id && lib.find(i => i.id === item.id)) {
      store.updateLocalItem(item.id, item);
      flashToast('Item updated');
    } else {
      store.addLocalItem(item);
      flashToast('Item saved ⭐');
    }
    closeSheet();
  }

  function saveFinanceTxn(form) {
    const amount = Math.abs(parseFloat(form.amount));
    if (!amount || isNaN(amount)) { flashToast('Enter an amount', 'err'); return; }
    const catObj = financeCategories.find(c => c.id === form.cat);
    const txnCurrency = form.currency || 'EGP';
    const isForeign = txnCurrency !== 'EGP';
    const projIds = Array.from(new Set([...(form.projectIds || []), ...(form.project ? [form.project] : [])].filter(Boolean)));
    // Fields the form controls — shared by add + edit so the two paths never drift.
    const fields = {
      date:         form.date || todayISO(),
      kind:         form.kind === 'income' ? 'income' : 'expense',
      amount,
      category:     form.cat || (catObj ? catObj.id : ''),
      subcategory:  form.subcategory || '',
      title:        form.title || (catObj ? catObj.name : 'Transaction'),
      account:      form.acct || (financeAccountsWithBalance[0] || {}).id || 'cash',
      notes:        form.notes || '',
      currency:     txnCurrency,
      projectIds:   projIds,    // multi-project membership lives here
      project:      '',         // clear legacy single-project field
      exchangeRate:  isForeign && form.exchangeRate  ? form.exchangeRate  : null,
      egpEquivalent: isForeign && form.egpEquivalent ? form.egpEquivalent : null,
    };
    if (form.id) {
      // EDIT in place — same id, preserve createdAt + original time (omitted from patch), bump updatedAt.
      // No duplicate; balances/stats/charts recompute from store.finance() on the next render.
      store.updateTransaction(form.id, { ...fields, updatedAt: new Date().toISOString() });
      flashToast('Transaction updated');
    } else {
      const id = 'fin_' + Date.now().toString(36) + Math.random().toString(36).slice(2,5);
      store.addTransactions([{ id, ...fields, time: form.time || new Date().toTimeString().slice(0,5), createdAt: new Date().toISOString() }]);
      flashToast('Transaction saved');
    }
    closeSheet();
  }

  async function refreshUsdRate() {
    setRateRefreshing(true);
    setRateError(null);
    try {
      const res = await fetch('https://open.er-api.com/v6/latest/USD');
      if (!res.ok) throw new Error('HTTP ' + res.status);
      const data = await res.json();
      if (data && data.result === 'success' && data.rates && data.rates.EGP) {
        const r = +data.rates.EGP;
        store.setUsdEgpRate(r);
        flashToast('1 USD = ' + r.toFixed(2) + ' EGP ✓');
      } else {
        throw new Error('Unexpected response format');
      }
    } catch (e) {
      const msg = 'Fetch failed — enter rate manually';
      setRateError(msg);
      flashToast('Rate update failed', 'err');
    } finally {
      setRateRefreshing(false);
    }
  }

  function setManualUsdRate(r) {
    const rate = parseFloat(r);
    if (!rate || rate <= 0) { flashToast('Invalid rate', 'err'); return; }
    store.setUsdEgpRate(rate);
    flashToast('1 USD = ' + rate.toFixed(2) + ' EGP saved');
  }

  async function refreshGoldPrices() {
    setGoldRefreshing(true);
    setGoldError(null);
    try {
      // api.gold-api.com returns price in USD per troy oz (free, no key needed)
      const res = await fetch('https://api.gold-api.com/price/XAU');
      if (!res.ok) throw new Error('HTTP ' + res.status);
      const data = await res.json();
      const usdPerOz = data && +data.price;
      if (!usdPerOz || usdPerOz <= 0) throw new Error('No price in response');
      // Need EGP/USD rate to convert — use stored rate or fetch it
      let egpPerUsd = usdRate;
      if (!egpPerUsd) {
        const rateRes = await fetch('https://open.er-api.com/v6/latest/USD');
        if (rateRes.ok) {
          const rd = await rateRes.json();
          if (rd && rd.rates && rd.rates.EGP) egpPerUsd = +rd.rates.EGP;
        }
      }
      if (!egpPerUsd) throw new Error('EGP rate unavailable — set USD/EGP rate first');
      const egpPerGram24k = (usdPerOz * egpPerUsd) / 31.1035;
      const prices = {
        '24k': +egpPerGram24k.toFixed(2),
        '21k': +(egpPerGram24k * 21/24).toFixed(2),
        '18k': +(egpPerGram24k * 18/24).toFixed(2),
        '14k': +(egpPerGram24k * 14/24).toFixed(2),
      };
      store.setGoldPrices(prices);
      flashToast('Gold: 24K = ' + Math.round(prices['24k']).toLocaleString() + ' EGP/g ✓');
    } catch (e) {
      setGoldError((e && e.message) || 'Fetch failed — enter price manually');
      flashToast('Gold price update failed', 'err');
    } finally {
      setGoldRefreshing(false);
    }
  }

  function setManualGoldPrice(karat, price) {
    const p = parseFloat(price);
    if (!p || p <= 0) { flashToast('Invalid price', 'err'); return; }
    const existing = goldPrices || {};
    // Auto-compute other karats from the one entered (using purity ratios)
    const karMeta = GOLD_KARATS.find(k => k.id === karat);
    const base24k = karMeta ? p / karMeta.purity : p;
    const updated = {
      ...existing,
      '24k': +(base24k.toFixed(2)),
      '21k': +(base24k * 21/24).toFixed(2),
      '18k': +(base24k * 18/24).toFixed(2),
      '14k': +(base24k * 14/24).toFixed(2),
      [karat]: p,
    };
    store.setGoldPrices(updated);
    flashToast(karat + ' price saved — all karats updated');
  }

  function saveGoldHolding(form) {
    if (form.id) {
      store.updateGoldHolding(form.id, form);
      flashToast('Gold holding updated');
    } else {
      const id = 'gold_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 5);
      store.addGoldHolding({ ...form, id });
      flashToast('Gold holding saved 🥇');
    }
    closeSheet();
  }

  function deleteGoldHolding(id) {
    if (!confirm('Delete this gold holding? This cannot be undone.')) return;
    store.deleteGoldHolding(id);
    flashToast('Deleted');
    closeSheet();
  }

  function saveFinanceTransfer(form) {
    const amount = Math.abs(parseFloat(form.amount));
    if (!amount || isNaN(amount)) { flashToast('Enter an amount', 'err'); return; }
    if (!form.fromAccount || !form.toAccount || form.fromAccount === form.toAccount) {
      flashToast('Select two different accounts', 'err'); return;
    }
    const fromAcct = financeAccountsWithBalance.find(a => a.id === form.fromAccount) || {};
    const toAcct   = financeAccountsWithBalance.find(a => a.id === form.toAccount)   || {};
    const fields = {
      kind:             'transfer',
      amount,
      fromAccount:      form.fromAccount,
      toAccount:        form.toAccount,
      account:          form.fromAccount,  // backward compat
      title:            `${fromAcct.name || 'Account'} → ${toAcct.name || 'Account'}`,
      date:             form.date || todayISO(),
      currency:         form.currency   || '',
      toCurrency:       form.toCurrency || '',
      exchangeRate:     form.exchangeRate     || null,
      convertedAmount:  form.convertedAmount  || amount,
      notes:            form.notes || '',
      category:         '',
      subcategory:      '',
    };
    if (form.id) {
      store.updateTransaction(form.id, { ...fields, updatedAt: new Date().toISOString() });
      flashToast('Transfer updated');
    } else {
      const id = 'fin_' + Date.now().toString(36) + Math.random().toString(36).slice(2,5);
      store.addTransactions([{ id, ...fields, time: new Date().toTimeString().slice(0,5), createdAt: new Date().toISOString() }]);
      flashToast('Transfer saved');
    }
    closeSheet();
  }

  // ── Finance projects ───────────────────────────────────────────────
  function saveFinanceProject(form) {
    if (!form.name || !form.name.trim()) { flashToast('Enter a project name', 'err'); return; }
    if (form.id) {
      store.updateFinanceProject(form.id, form);
      flashToast('Project updated');
    } else {
      const id = 'proj_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 5);
      store.addFinanceProject({ ...form, id, createdAt: new Date().toISOString() });
      flashToast('Project added 📁');
    }
    closeSheet();
  }
  function deleteFinanceProjectSafe(project) {
    const linked = financeTxns.filter(t => t.project === project.id).length;
    const linkedFixed = financeFixed.filter(f => f.project === project.id).length;
    if (linked > 0 || linkedFixed > 0) {
      const msg = 'This project has ' + linked + ' linked expense(s)' + (linkedFixed ? ' and ' + linkedFixed + ' fixed expense(s)' : '') +
        '.\n\nDeleting keeps those records but unlinks them. Archive instead to keep the link.\n\nDelete anyway?';
      if (!confirm(msg)) return;
    }
    store.deleteFinanceProject(project.id);
    flashToast('Project deleted (expenses kept)');
    closeSheet();
  }

  // ── Finance fixed expenses / installments ──────────────────────────
  function saveFinanceFixed(form) {
    if (!form.name || !form.name.trim()) { flashToast('Enter a name', 'err'); return; }
    if (!(+form.amount > 0)) { flashToast('Enter an amount', 'err'); return; }
    if (form.id) {
      store.updateFinanceFixed(form.id, form);
      flashToast('Fixed expense updated');
    } else {
      const id = 'fx_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 5);
      store.addFinanceFixed({ ...form, id, paidDates: [], createdAt: new Date().toISOString() });
      flashToast('Fixed expense added 🧾');
    }
    closeSheet();
  }
  function deleteFinanceFixedSafe(fx) {
    const paid = (fx.paidDates || []).length;
    const msg = paid > 0
      ? 'This schedule has ' + paid + ' paid payment(s) already logged as expenses.\n\nDeleting removes the schedule only — the paid expense(s) stay in your history.\n\nDelete schedule?'
      : 'Delete this fixed expense schedule?';
    if (!confirm(msg)) return;
    store.deleteFinanceFixed(fx.id);
    flashToast('Fixed expense deleted');
    closeSheet();
  }
  function confirmMarkPaid(opts) {
    const data = sheet && sheet.data ? sheet.data : {};
    const res = store.markFixedPaid(data.fixedId, data.dueDate, opts);
    if (res && res.ok) flashToast('Payment logged ✓');
    else if (res && res.reason === 'already-paid') flashToast('Already paid for that date', 'err');
    else flashToast('Could not mark paid', 'err');
    closeSheet();
  }

  // ── Finance account management ──────────────────────────────────────
  function saveFinanceAccount(form) {
    const existing = store.financeAccounts() || NF_ACCOUNTS;
    if (form.id) {
      const updated = existing.map(a => a.id === form.id ? { ...a, ...form } : a);
      store.setFinanceAccounts(updated);
      flashToast('Account updated');
    } else {
      const slug = form.name.toLowerCase().replace(/\s+/g,'_').replace(/[^a-z0-9_]/g,'');
      const newId = (slug || 'acct') + '_' + Date.now().toString(36).slice(-4);
      const newAcct = {
        id: newId, name: form.name, kind: form.kind || 'cash',
        icon: form.icon || '💵', startingBalance: +form.startingBalance || 0,
        currency: form.currency || 'EGP',
        ...(form.notes ? { notes: form.notes } : {}),
      };
      store.setFinanceAccounts([...existing, newAcct]);
      flashToast('Account added');
    }
    closeSheet();
  }

  function saveAccountAdjustment(form) {
    const res = store.addFinanceAdjustment(form);
    if (res && res.skipped) { flashToast('No change to record'); closeSheet(); return; }
    flashToast('Fixing Data saved — ' + (res.kind === 'income' ? '+' : '−') + Math.abs(res.amount).toLocaleString());
    closeSheet();
  }

  function quickAddIncomeSource(name, cb) {
    const existing = store.financeCategories() || NF_CATEGORIES;
    const incCat = existing.find(c => c.id === 'income');
    if (!incCat) { flashToast('No income category found', 'err'); return; }
    const newId = 'sub_' + Date.now().toString(36);
    const updated = existing.map(c => c.id === 'income'
      ? { ...c, subcategories: [...(c.subcategories || []), { id: newId, name }] }
      : c
    );
    store.setFinanceCategories(updated);
    cb && cb(newId);
  }

  // ── Quick-add from inside forms (no sheet close, auto-select via cb) ──
  function quickAddFinanceCategory(name, cb) {
    const newId = 'cat_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 4);
    store.setFinanceCategories([...financeCategories, { id: newId, name: name.trim(), color: '#7A766C', icon: '📦', subcategories: [] }]);
    flashToast('Category added');
    cb && cb(newId);
  }
  function quickAddFinanceSubcategoryInline(catId, name, cb) {
    const newId = 'sub_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 4);
    store.setFinanceCategories(financeCategories.map(c => c.id === catId
      ? { ...c, subcategories: [...(c.subcategories || []), { id: newId, name: name.trim() }] } : c));
    flashToast('Subcategory added');
    cb && cb(newId);
  }
  function quickAddFinanceAccountInline(name, cb) {
    const raw = (store.financeAccounts() || NF_ACCOUNTS).map(a => ({
      id: a.id, name: a.name, kind: a.kind || 'cash', icon: a.icon || '💵',
      startingBalance: +a.startingBalance || 0, currency: a.currency || 'EGP', ...(a.notes ? { notes: a.notes } : {}),
    }));
    const slug = name.trim().toLowerCase().replace(/\s+/g, '_').replace(/[^a-z0-9_]/g, '');
    const newId = (slug || 'acct') + '_' + Date.now().toString(36).slice(-4);
    store.setFinanceAccounts([...raw, { id: newId, name: name.trim(), kind: 'cash', icon: '💵', startingBalance: 0, currency: 'EGP' }]);
    flashToast('Account added');
    cb && cb(newId);
  }
  function quickAddFinanceProjectInline(payload, cb) {
    const p = typeof payload === 'string' ? { name: payload } : (payload || {});
    const name = (p.name || '').trim();
    if (!name) return;
    const id = 'proj_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 4);
    store.addFinanceProject({
      id, name, status: 'active',
      ...(p.notes ? { notes: p.notes } : {}),
      ...(p.budget != null && +p.budget > 0 ? { budget: +p.budget } : {}),
      ...(p.startDate ? { startDate: p.startDate } : {}),
      createdAt: new Date().toISOString(),
    });
    flashToast('Project added');
    cb && cb(id);
  }

  function renameFinanceSubcategory(catId, subId, newName) {
    const existing = store.financeCategories() || NF_CATEGORIES;
    store.setFinanceCategories(existing.map(c => c.id !== catId ? c : {
      ...c, subcategories: (c.subcategories || []).map(s => s.id === subId ? { ...s, name: newName } : s),
    }));
    flashToast('Source renamed');
  }

  // ── Finance category management ─────────────────────────────────────
  function saveFinanceCategory(form) {
    const existing = store.financeCategories() || NF_CATEGORIES;
    if (form.id) {
      store.setFinanceCategories(existing.map(c => c.id === form.id ? { ...c, name: form.name, color: form.color, icon: form.icon, spendingType: form.spendingType || undefined } : c));
      flashToast('Category updated');
    } else {
      const newId = 'cat_' + Date.now().toString(36);
      store.setFinanceCategories([...existing, { id: newId, name: form.name, color: form.color || '#7A766C', icon: form.icon || '📦', spendingType: form.spendingType || undefined, subcategories: [] }]);
      flashToast('Category added');
    }
    closeSheet();
  }

  function saveFinanceSubcategory(form) {
    const existing = store.financeCategories() || NF_CATEGORIES;
    const updated = existing.map(c => {
      if (c.id !== form.catId) return c;
      if (form.id) {
        return { ...c, subcategories: (c.subcategories||[]).map(s => s.id === form.id ? { ...s, name: form.name } : s) };
      }
      const newSub = { id: 'sub_' + Date.now().toString(36), name: form.name };
      return { ...c, subcategories: [...(c.subcategories||[]), newSub] };
    });
    store.setFinanceCategories(updated);
    flashToast(form.id ? 'Subcategory updated' : 'Subcategory added');
    closeSheet();
  }

  function deleteFinanceCategory(catId) {
    const used = financeTxns.filter(t => t.category === catId).length;
    if (used > 0) { flashToast(`Used in ${used} txns — reassign first`, 'err'); return; }
    const cats = store.financeCategories() || NF_CATEGORIES;
    store.setFinanceCategories(cats.filter(c => c.id !== catId));
    flashToast('Category deleted');
  }

  function deleteFinanceSubcategory(catId, subId) {
    const used = financeTxns.filter(t => t.subcategory === subId).length;
    if (used > 0) { flashToast(`Used in ${used} txns — reassign first`, 'err'); return; }
    const cats = store.financeCategories() || NF_CATEGORIES;
    store.setFinanceCategories(cats.map(c => c.id === catId
      ? { ...c, subcategories: (c.subcategories||[]).filter(s => s.id !== subId) }
      : c
    ));
    flashToast('Subcategory deleted');
  }

  function saveGoalDef(form) {
    if (!form.title || !form.title.trim()) { flashToast('Goal needs a title', 'err'); return; }
    const id = 'goal_' + Date.now().toString(36) + Math.random().toString(36).slice(2,5);
    const newGoal = {
      id,
      name:        form.title.trim(),
      emoji:       form.emoji    || '🎯',
      timesPerDay: +form.timesPerDay || 1,
      target:      +form.timesPerDay || 1,
      unit:         form.unit || (+form.timesPerDay > 1 ? 'times' : 'session'),
      weekdays: Array.isArray(form.weekdays) && form.weekdays.length ? form.weekdays : [0,1,2,3,4,5,6],
      repeatType:      form.repeatType     || 'weekly',
      repeatInterval:  form.repeatInterval || null,
      startDate:   form.startDate    || NUTRI_TODAY,
      endDate:     form.endDate      || null,
      durationDays: +form.durationDays || 0,
      color:       form.color        || '#1F3D2E',
      log: {},
    };
    store.setGoals([...(store.goals() || []), newGoal]);
    flashToast('Goal added');
    closeSheet();
  }

  function adjustGoalLogToday(goalId, delta) {
    adjustGoalLogForDate(goalId, NUTRI_TODAY, delta);
  }

  function adjustGoalLogForDate(goalId, date, delta) {
    const goals = store.goals().map(g => ({ ...g }));
    const g = goals.find(x => x.id === goalId);
    if (!g) return;
    const tpd = +g.timesPerDay || 1;
    const log = { ...(g.log || {}) };
    // If currently all 'skip', reset to all 'miss' before adjusting
    const existing = Array.isArray(log[date]) ? log[date] : [];
    const wasSkipped = existing.length > 0 && existing.every(s => s === 'skip');
    const slots = wasSkipped ? new Array(tpd).fill('miss') : (existing.length > 0 ? existing.slice() : new Array(tpd).fill('miss'));
    while (slots.length < tpd) slots.push('miss');
    const doneCount = slots.filter(s => s === 'done').length;
    const nextDone = Math.max(0, Math.min(tpd, doneCount + delta));
    for (let i = 0; i < tpd; i++) slots[i] = i < nextDone ? 'done' : 'miss';
    log[date] = slots;
    g.log = log;
    store.setGoals(goals);
  }

  function markGoalSkipped(goalId, date) {
    const goals = store.goals().map(g => ({ ...g }));
    const g = goals.find(x => x.id === goalId);
    if (!g) return;
    const tpd = +g.timesPerDay || 1;
    const log = { ...(g.log || {}) };
    // Toggle: if already skipped, reset to all-miss (un-skip)
    const existing = Array.isArray(log[date]) ? log[date] : [];
    const isSkipped = existing.length > 0 && existing.every(s => s === 'skip');
    log[date] = isSkipped ? new Array(tpd).fill('miss') : new Array(tpd).fill('skip');
    g.log = log;
    store.setGoals(goals);
  }

  function handleAddCustomTag(tag) {
    const trimmed = (tag || '').trim();
    if (!trimmed) return;
    const current = ((store.settings() || {}).diaryCustomTags) || [];
    if (!current.includes(trimmed)) {
      store.setDiaryCustomTags([...current, trimmed]);
    }
  }

  function saveDiaryEntry(form) {
    const today = new Date();
    const pad = n => String(n).padStart(2, '0');
    const dateKey = form.date || `${today.getFullYear()}-${pad(today.getMonth()+1)}-${pad(today.getDate())}`;
    const [y,m,d] = dateKey.split('-').map(Number);
    const id = 'dia_' + Date.now().toString(36) + Math.random().toString(36).slice(2,5);
    const row = {
      id,
      day: d, month: m, year: y,
      dateKey,
      title: (form.title || '').trim(),
      eventText: form.text || '',
      mood: form.mood || 'okay',
      tags: Array.isArray(form.tags) ? form.tags : [],
      entryMode: form.entryMode || 'full',
      ...(form.movieId ? { movieId: form.movieId } : {}),
      source: 'manual',
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    };
    store.addDiaryEntries([row]);
    flashToast('Memory saved');
    closeSheet();
  }

  function updateDiaryEntry(original, form) {
    const pad = n => String(n).padStart(2, '0');
    const today = new Date();
    const dateKey = form.date
      || (typeof original.day === 'number'
          ? `${original.year || today.getFullYear()}-${pad(original.month || today.getMonth()+1)}-${pad(original.day || today.getDate())}`
          : (original.date || NUTRI_TODAY));
    const [y, m, d] = dateKey.split('-').map(Number);
    const updatedRow = {
      id:         original.id,
      day: d, month: m, year: y,
      dateKey,
      title:      (form.title || '').trim(),
      eventText:  form.text || '',
      mood:       form.mood || 'okay',
      tags:       Array.isArray(form.tags) ? form.tags : [],
      entryMode:  form.entryMode || original.entryMode || 'full',
      ...(form.movieId ? { movieId: form.movieId } : {}),
      source:     'manual',
      createdAt:  original.createdAt || new Date().toISOString(),
      updatedAt:  new Date().toISOString(),
    };
    store.deleteDiaryEntry(original.id);
    store.addDiaryEntries([updatedRow]);
    flashToast('Entry updated');
    closeSheet();
  }

  function deleteCurrent(kind, item) {
    if (!item || !item.id) { closeSheet(); return; }
    if (!confirm('Delete this item? This cannot be undone.')) return;
    if (kind === 'meal')  store.deleteMeal(item.id);
    if (kind === 'txn')   store.deleteTransaction(item.id);
    if (kind === 'diary') store.deleteDiaryEntry(item.id);
    flashToast('Deleted');
    closeSheet();
  }

  function deleteGoal(goal) {
    if (!goal || !goal.id) { closeSheet(); return; }
    if (!confirm('Delete this goal and all its logs?')) return;
    store.deleteGoal(goal.id);
    flashToast('Goal deleted');
    closeSheet();
  }

  function renderSheet() {
    if (!sheet) return null;
    if (sheet.kind === 'addPicker') return (
      <AddActionSheet
        projectId={projectId}
        onPickPaste={pickPaste}
        onPickManual={pickManual}
        onPickTransfer={pickTransfer}
        onClose={closeSheet}/>
    );
    // ── Calories-specific sheets ──
    if (sheet.kind === 'editGoals') return (
      <MacroGoalEditorSheet
        goals={calorieGoals}
        onSave={saveMacroGoals}
        onClose={closeSheet}/>
    );
    if (sheet.kind === 'logFood') return (
      <LogFoodSheet
        food={sheet.data}
        defaultDate={selectedDate}
        defaultMeal="breakfast"
        onSave={handleSaveLoggedFood}
        onClose={closeSheet}/>
    );
    if (sheet.kind === 'addLocalItem') return (
      <AddLocalItemSheet
        existing={sheet.data || null}
        onSave={saveLocalItem}
        onClose={closeSheet}/>
    );
    if (sheet.kind === 'meal') return (
      <MealDetailSheet entry={sheet.data} goals={calorieGoals}
        onClose={closeSheet}
        onDelete={() => deleteCurrent('meal', sheet.data)}/>
    );
    if (sheet.kind === 'addCal') return (
      <CalorieAddSheet
        defaultDate={NUTRI_TODAY}
        onClose={closeSheet}
        onSave={saveCalorieEntry}/>
    );
    if (sheet.kind === 'txn') return (
      <TxnDetailSheet txn={sheet.data}
        onClose={closeSheet}
        onEdit={(t) => setSheet({ kind: t.kind === 'transfer' ? 'addTransfer' : 'addTxn', data: t })}
        onDelete={() => deleteCurrent('txn', sheet.data)}/>
    );
    if (sheet.kind === 'addTxn') return (
      <FinanceAddSheet
        editTxn={sheet.data}
        categories={financeCategories}
        accounts={financeAccountsWithBalance}
        projects={financeProjects}
        onClose={closeSheet}
        onSave={saveFinanceTxn}
        onAddIncomeSource={quickAddIncomeSource}
        onQuickAddCategory={quickAddFinanceCategory}
        onQuickAddSubcategory={quickAddFinanceSubcategoryInline}
        onQuickAddAccount={quickAddFinanceAccountInline}
        onQuickAddProject={quickAddFinanceProjectInline}
        usdRate={usdRate}/>
    );
    if (sheet.kind === 'addTransfer') return (
      <TransferAddSheet
        editTxn={sheet.data}
        accounts={financeAccountsWithBalance}
        onClose={closeSheet}
        onSave={saveFinanceTransfer}/>
    );
    if (sheet.kind === 'addAccount') return (
      <AccountAddEditSheet onClose={closeSheet} onSave={saveFinanceAccount}/>
    );
    if (sheet.kind === 'editAccount') return (
      <AccountAddEditSheet account={sheet.data} onClose={closeSheet} onSave={saveFinanceAccount}/>
    );
    if (sheet.kind === 'adjustAccount') return (
      <AccountAdjustSheet account={sheet.data} onClose={closeSheet} onSave={saveAccountAdjustment}/>
    );
    if (sheet.kind === 'editCategory') return (
      <CategoryAddEditSheet
        category={sheet.data.isNew ? null : sheet.data.cat}
        onClose={closeSheet}
        onSave={saveFinanceCategory}/>
    );
    if (sheet.kind === 'editSubcategory') return (
      <SubcategoryAddEditSheet
        catId={sheet.data.catId}
        subcategory={sheet.data.isNew ? null : sheet.data.sub}
        onClose={closeSheet}
        onSave={saveFinanceSubcategory}/>
    );
    if (sheet.kind === 'addGold') return (
      <GoldAddEditSheet
        goldPrices={goldPrices}
        onClose={closeSheet}
        onSave={saveGoldHolding}/>
    );
    if (sheet.kind === 'editGold') return (
      <GoldAddEditSheet
        holding={sheet.data}
        goldPrices={goldPrices}
        onClose={closeSheet}
        onSave={saveGoldHolding}
        onDelete={() => deleteGoldHolding(sheet.data.id)}/>
    );
    if (sheet.kind === 'editProject') return (
      <ProjectAddEditSheet
        project={sheet.data.isNew ? null : sheet.data.project}
        onClose={closeSheet}
        onSave={saveFinanceProject}
        onDelete={deleteFinanceProjectSafe}/>
    );
    if (sheet.kind === 'projectDetail') return (
      <ProjectDetailSheet
        project={sheet.data}
        txns={financeTxns}
        fixed={financeFixed}
        categories={financeCategories}
        onClose={closeSheet}
        onEdit={(p) => setSheet({ kind: 'editProject', data: { project: p, isNew: false } })}
        onOpenTxn={(t) => setSheet({ kind: 'txn', data: t })}/>
    );
    if (sheet.kind === 'editFixed') return (
      <FixedAddEditSheet
        fixed={sheet.data.isNew ? null : sheet.data.fixed}
        categories={financeCategories}
        accounts={financeAccountsWithBalance}
        projects={financeProjects}
        usdRate={usdRate}
        onClose={closeSheet}
        onSave={saveFinanceFixed}
        onDelete={deleteFinanceFixedSafe}
        onQuickAddCategory={quickAddFinanceCategory}
        onQuickAddSubcategory={quickAddFinanceSubcategoryInline}
        onQuickAddAccount={quickAddFinanceAccountInline}
        onQuickAddProject={quickAddFinanceProjectInline}/>
    );
    if (sheet.kind === 'markPaid') {
      const fx = financeFixed.find(f => f.id === sheet.data.fixedId);
      if (!fx) return null;
      return (
        <MarkPaidSheet
          fixed={fx}
          dueDate={sheet.data.dueDate}
          accounts={financeAccountsWithBalance}
          usdRate={usdRate}
          onClose={closeSheet}
          onConfirm={confirmMarkPaid}/>
      );
    }
    if (sheet.kind === 'goal') return (
      <GoalDetailSheet goal={sheet.data} logs={goalLogs}
        logDate={selectedDate}
        onClose={closeSheet}
        onInc={() => adjustGoalLogForDate(sheet.data.id, selectedDate, +1)}
        onDec={() => adjustGoalLogForDate(sheet.data.id, selectedDate, -1)}
        onDelete={() => deleteGoal(sheet.data)}/>
    );
    if (sheet.kind === 'addGoal') return (
      <GoalAddSheet onClose={closeSheet} onSave={saveGoalDef}/>
    );
    if (sheet.kind === 'diary') return (
      <DiaryEntrySheet entry={sheet.data}
        onClose={closeSheet}
        onDelete={() => deleteCurrent('diary', sheet.data)}
        onEdit={() => setSheet({ kind: 'editDiary', data: sheet.data })}/>
    );
    if (sheet.kind === 'editDiary') return (
      <DiaryEditSheet entry={sheet.data} onClose={closeSheet}
        onSave={(form) => updateDiaryEntry(sheet.data, form)}
        customTags={diaryCustomTags}
        onAddCustomTag={handleAddCustomTag}/>
    );
    if (sheet.kind === 'addDiary') return (
      <DiaryCompose onClose={closeSheet} onSave={saveDiaryEntry}
        customTags={diaryCustomTags}
        onAddCustomTag={handleAddCustomTag}/>
    );
    // ── Movie Night sheets ──
    if (sheet.kind === 'movieDetail') return (
      <MovieDetailSheet
        key={'mov-' + (sheet.data.id || sheet.data.tmdbId || 'x')}
        movie={sheet.data}
        onClose={closeSheet}
        onPickList={(movieId) => setSheet({ kind: 'movieListPicker', data: { movieId } })}
        onOpenMovie={(m) => setSheet({ kind: 'movieDetail', data: m })}
        flashToast={flashToast}/>
    );
    if (sheet.kind === 'addMovie') return (
      <AddMovieSheet
        onClose={closeSheet}
        onPickList={(movieId) => setSheet({ kind: 'movieListPicker', data: { movieId } })}
        flashToast={flashToast}/>
    );
    if (sheet.kind === 'movieSettings') return (
      <MovieSettingsSheet onClose={closeSheet} flashToast={flashToast}/>
    );
    if (sheet.kind === 'movieListPicker') return (
      <MovieListPickerSheet movieId={sheet.data.movieId} onResolve={sheet.data.onResolve} onClose={closeSheet} flashToast={flashToast}/>
    );
    if (sheet.kind === 'movieCsvImport') return (
      <MovieCsvImportSheet onClose={closeSheet} flashToast={flashToast}/>
    );
    if (sheet.kind === 'cleanupCalories') return (
      <DataCleanupSheet mode="calories" onClose={closeSheet} flashToast={flashToast}/>
    );
    if (sheet.kind === 'cleanupFinance') return (
      <DataCleanupSheet mode="finance" onClose={closeSheet} flashToast={flashToast}/>
    );
    return null;
  }

  const projectTabs = TABS_BY_PROJECT[projectId] || [{ id: 'home', label: 'Home', icon: HomeIcon }];

  return (
    <div style={{
      display: 'flex', flexDirection: 'column',
      width: '100%', height: '100%',
      background: 'var(--bg)', color: 'var(--text)',
      paddingTop: 'var(--safe-top)',
    }}>
      {/* ── PWA update banner ── */}
      {updateReady && (
        <div style={{
          background: 'var(--accent)', color: 'var(--on-accent)',
          padding: '10px 16px', display: 'flex', alignItems: 'center', gap: 10,
          flexShrink: 0, zIndex: 200,
        }}>
          <span style={{ fontSize: 16 }}>✨</span>
          <span style={{ flex: 1, fontSize: 13.5, fontWeight: 500 }}>Update available</span>
          <button onClick={applyUpdate} style={{
            padding: '5px 14px', borderRadius: 6, border: 'none', cursor: 'pointer',
            background: 'rgba(255,255,255,0.2)', color: 'var(--on-accent)',
            font: 'inherit', fontSize: 12.5, fontWeight: 600,
          }}>Update now</button>
          <button onClick={() => setUpdateReady(false)} style={{
            padding: '5px 10px', borderRadius: 6, border: 'none', cursor: 'pointer',
            background: 'transparent', color: 'rgba(244,239,227,0.65)',
            font: 'inherit', fontSize: 12.5,
          }}>Later</button>
        </div>
      )}
      {/* TOP: brand + project switcher */}
      <div style={{
        padding: '10px 14px 6px',
        background: 'var(--bg)',
        borderBottom: '1px solid var(--border)',
        flexShrink: 0,
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
          <BrandMark size={30} radius={8}/>
          <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em', flex: 1 }}>
            Nutri <span style={{ color: 'var(--muted)', fontWeight: 400, fontSize: 12.5 }}>· lifestyle</span>
          </div>
          <UserAvatar onClick={() => { setProjectId('settings'); setTab('home'); }}/>
        </div>
        <div style={{
          display: 'flex', gap: 6, overflowX: 'auto',
          paddingBottom: 2, scrollbarWidth: 'none',
        }} className="nutri-scroll-x">
          {NUTRI_PROJECTS.map(p => {
            const active = p.id === projectId;
            return (
              <button key={p.id} onClick={()=>setProjectId(p.id)} style={{
                display: 'inline-flex', alignItems: 'center', gap: 6,
                padding: '7px 12px', borderRadius: 999,
                background: active ? 'var(--accent)' : 'var(--surface)',
                color: active ? 'var(--on-accent)' : 'var(--text)',
                border: active ? '1px solid var(--accent)' : '1px solid var(--border-2)',
                font: 'inherit', fontSize: 12, fontWeight: 600, cursor: 'pointer',
                whiteSpace: 'nowrap', flexShrink: 0,
                transition: 'background 0.15s ease, color 0.15s ease, border-color 0.15s ease',
              }}>
                <span style={{ fontSize: 13 }}>{p.emoji}</span>{p.label}
              </button>
            );
          })}
        </div>
      </div>

      {/* MAIN content */}
      <div style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden', position: 'relative' }}>
        {renderScreen()}
      </div>

      {/* BOTTOM nav */}
      <div style={{
        background: 'var(--surface)',
        borderTop: '1px solid var(--border)',
        paddingBottom: 'var(--safe-bottom)',
        boxShadow: '0 -6px 24px -12px rgba(20,20,15,0.18)',
        flexShrink: 0,
      }}>
        <ProjectBottomNav
          tabs={projectTabs}
          active={tab}
          onChange={setTab}/>
      </div>

      {/* Sheet overlay */}
      {sheet && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 50 }}>
          {renderSheet()}
        </div>
      )}

      {/* Duplicate-day dialog */}
      {dupDialog && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 60 }}>
          <DuplicateEntryDialog
            date={(() => { try { return new Date(dupDialog.date + 'T00:00:00').toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); } catch { return dupDialog.date; } })()}
            existingCount={dupDialog.existingCount}
            onReplace={() => {
              store.replaceMealsForDate(dupDialog.date, [dupDialog.row]);
              flashToast('Replaced ✓');
              setDupDialog(null);
            }}
            onMerge={() => {
              const existing = store.meals().filter(m => m.date === dupDialog.date);
              const fp = new Set(existing.map(m => `${m.meal}:${m.name}:${Math.round(m.calories)}`));
              const key = `${dupDialog.row.meal}:${dupDialog.row.name}:${Math.round(dupDialog.row.calories)}`;
              if (fp.has(key)) {
                flashToast('Already logged');
              } else {
                store.addMeals([dupDialog.row]);
                flashToast(dupDialog.toastMsg || 'Added ✓');
              }
              setDupDialog(null);
            }}
            onKeep={() => {
              flashToast('Kept existing');
              setDupDialog(null);
            }}
            onClose={() => setDupDialog(null)}
          />
        </div>
      )}

      {/* Toast */}
      {toast && (
        <div style={{
          position: 'fixed', left: '50%', bottom: 92, transform: 'translateX(-50%)',
          padding: '10px 18px', borderRadius: 999,
          background: toast.kind === 'err' ? '#A33' : 'var(--text)',
          color: toast.kind === 'err' ? '#fff' : 'var(--bg)',
          fontSize: 13, fontWeight: 600,
          boxShadow: 'var(--shadow-lg)',
          zIndex: 200,
          animation: 'toastIn .2s ease',
        }}>{toast.text}</div>
      )}
    </div>
  );
}

function UserAvatar({ onClick }) {
  const store = useStore();
  const p = store.profile() || {};
  const initials = (p.displayName || p.email || '?').trim()
    .split(/\s+/).map(s => s[0]).join('').slice(0, 2).toUpperCase();
  return (
    <button onClick={onClick} style={{
      width: 32, height: 32, borderRadius: '50%',
      background: 'linear-gradient(135deg, #C6BFA8, #8C8669)',
      color: '#fff', border: '1px solid var(--border-2)',
      display: 'grid', placeItems: 'center',
      fontSize: 12, fontWeight: 700, cursor: 'pointer',
      overflow: 'hidden', padding: 0, font: 'inherit',
    }}>
      {p.photoURL
        ? <img src={p.photoURL} alt="avatar" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
        : initials || 'N'}
    </button>
  );
}

function ProjectBottomNav({ tabs, active, onChange }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'stretch', justifyContent: 'space-between',
      padding: '8px 14px 6px',
      gap: 4,
    }}>
      {tabs.map(t => {
        const isActive = active === t.id;
        return (
          <button key={t.id} onClick={() => onChange(t.id)} style={{
            flex: 1, minWidth: 0,
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
            padding: '4px 0 0',
            background: 'transparent', border: 0, cursor: 'pointer',
            color: isActive ? 'var(--accent)' : 'var(--muted)',
            fontFamily: 'inherit',
            transition: 'color 0.15s ease',
          }}>
            <t.icon size={20}/>
            <span style={{ fontSize: 10, fontWeight: isActive ? 600 : 500 }}>{t.label}</span>
            {/* Active indicator — always rendered at fixed height to prevent layout jump */}
            <div style={{
              height: 2.5,
              width: 24,
              borderRadius: 99,
              background: 'var(--accent)',
              marginTop: 4,
              opacity: isActive ? 1 : 0,
              transform: isActive ? 'scaleX(1)' : 'scaleX(0)',
              transition: 'opacity 0.18s ease, transform 0.18s cubic-bezier(.4,0,.2,1)',
              transformOrigin: 'center',
            }}/>
          </button>
        );
      })}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Add-action sheet (Plus button)
// ─────────────────────────────────────────────────────────────
function AddActionSheet({ projectId, onPickPaste, onPickManual, onPickTransfer, onClose }) {
  const labelByProject = {
    calories: { manual: 'Log a meal', sub: 'Calories + macros' },
    finance:  { manual: 'New transaction', sub: 'Expense or income' },
    goals:    { manual: 'New goal', sub: 'Habit / target' },
    diary:    { manual: 'Write entry', sub: 'Memory + mood' },
    movies:   { manual: 'Add a movie', sub: 'Search TMDb' },
  };
  const cfg = labelByProject[projectId] || { manual: 'Add entry', sub: 'Manual form' };
  return (
    <div onClick={onClose} style={{
      position: 'absolute', inset: 0,
      background: 'rgba(0,0,0,0.45)',
      display: 'flex', alignItems: 'flex-end',
      animation: 'fadeIn .15s ease',
    }}>
      <div onClick={(e)=>e.stopPropagation()} style={{
        width: '100%', background: 'var(--bg)',
        borderTopLeftRadius: 22, borderTopRightRadius: 22,
        padding: '12px 16px calc(20px + var(--safe-bottom))',
        boxShadow: 'var(--shadow-lg)',
        animation: 'slideUp .22s cubic-bezier(.2,.7,.2,1)',
      }}>
        <div style={{ width: 36, height: 4, borderRadius: 4, background: 'var(--border-2)', margin: '4px auto 14px' }}/>
        <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 14 }}>What would you like to add?</div>
        <button onClick={onPickManual} style={addOptStyle('var(--accent)', 'var(--on-accent)')}>
          <div style={{
            width: 40, height: 40, borderRadius: 12,
            background: 'rgba(255,255,255,0.12)', color: 'var(--on-accent)',
            display: 'grid', placeItems: 'center', flexShrink: 0,
          }}>
            <PlusIcon size={20}/>
          </div>
          <div style={{ flex: 1, textAlign: 'left' }}>
            <div style={{ fontSize: 14, fontWeight: 700 }}>{cfg.manual}</div>
            <div style={{ fontSize: 12, opacity: 0.85, marginTop: 2 }}>{cfg.sub}</div>
          </div>
          <ChevronRight size={16}/>
        </button>
        {projectId === 'finance' && onPickTransfer && (
          <button onClick={onPickTransfer} style={addOptStyle('var(--surface)', 'var(--text)')}>
            <div style={{
              width: 40, height: 40, borderRadius: 12,
              background: 'var(--c-fat-soft)', color: 'var(--c-fat)',
              border: '1px solid rgba(87,132,216,0.25)',
              display: 'grid', placeItems: 'center', fontSize: 20, flexShrink: 0,
            }}>↔</div>
            <div style={{ flex: 1, textAlign: 'left' }}>
              <div style={{ fontSize: 14, fontWeight: 700 }}>Transfer</div>
              <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 2 }}>Move money between accounts</div>
            </div>
            <ChevronRight size={16}/>
          </button>
        )}
        <button onClick={onPickPaste} style={addOptStyle('var(--surface)', 'var(--text)')}>
          <div style={{
            width: 40, height: 40, borderRadius: 12,
            background: 'var(--surface-2)', color: 'var(--text)',
            border: '1px solid var(--border)',
            display: 'grid', placeItems: 'center', flexShrink: 0,
          }}>
            <ListIcon size={20}/>
          </div>
          <div style={{ flex: 1, textAlign: 'left' }}>
            <div style={{ fontSize: 14, fontWeight: 700 }}>Paste &amp; Parse</div>
            <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 2 }}>Drop a chunk of text — we'll parse it</div>
          </div>
          <ChevronRight size={16}/>
        </button>
        <button onClick={onClose} style={{
          width: '100%', marginTop: 8, padding: '12px',
          background: 'transparent', border: '1px solid var(--border-2)',
          borderRadius: 14, color: 'var(--muted)',
          fontSize: 13, fontWeight: 500, font: 'inherit', cursor: 'pointer',
        }}>Cancel</button>
      </div>
    </div>
  );
}
function addOptStyle(bg, color) {
  return {
    width: '100%', marginBottom: 10,
    display: 'flex', alignItems: 'center', gap: 12,
    padding: '12px 14px', borderRadius: 16,
    background: bg, color, border: '1px solid var(--border)',
    cursor: 'pointer', font: 'inherit',
    boxShadow: 'var(--shadow-sm)',
  };
}

// ─────────────────────────────────────────────────────────────
// Calorie Add Sheet
// ─────────────────────────────────────────────────────────────
function CalorieAddSheet({ defaultDate, onClose, onSave }) {
  const [date, setDate]       = React.useState(defaultDate || NUTRI_TODAY);
  const [meal, setMeal]       = React.useState('breakfast');
  const [name, setName]       = React.useState('');
  const [calories, setCal]    = React.useState('');
  const [protein, setProt]    = React.useState('');
  const [carbs, setCarb]      = React.useState('');
  const [fat, setFat]         = React.useState('');
  const [time, setTime]       = React.useState(new Date().toTimeString().slice(0,5));

  const [showLibrary, setShowLibrary] = React.useState(false);
  const [libQuery, setLibQuery]       = React.useState('');
  const filtered = NUTRI_FOODS.filter(f => !libQuery || f.name.toLowerCase().includes(libQuery.toLowerCase()));

  function applyFood(f) {
    setName(f.name);
    setCal(String(f.cal));
    setProt(String(f.p || 0));
    setCarb(String(f.c || 0));
    setFat(String(f.f || 0));
    setShowLibrary(false);
  }

  function submit() {
    if (!name.trim() && !calories) return;
    onSave({
      date, meal, name: name.trim(),
      emoji: NUTRI_MEAL_ICON[meal] || '🍽️',
      calories, protein_g: protein, carbs_g: carbs, fat_g: fat, time,
    });
  }

  return (
    <SheetShell title="Log a meal" onClose={onClose} onSave={submit} canSave={!!(name.trim() || calories)}>
      <Segment options={['breakfast','lunch','dinner','snack']} value={meal} onChange={setMeal}/>
      <div style={{ marginTop: 14, padding: 12, background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 14, display: 'flex', flexDirection: 'column', gap: 10 }}>
        <FormRow label="Date">
          <input type="date" value={date} onChange={e=>setDate(e.target.value)} style={inputStyle}/>
        </FormRow>
        <FormRow label="Time">
          <input type="time" value={time} onChange={e=>setTime(e.target.value)} style={inputStyle}/>
        </FormRow>
        <FormRow label="Meal name">
          <input value={name} onChange={e=>setName(e.target.value)} placeholder="e.g. Grilled chicken bowl" style={inputStyle}/>
        </FormRow>
        <button onClick={()=>setShowLibrary(v=>!v)} style={{
          alignSelf: 'flex-start', padding: '6px 12px', borderRadius: 999,
          background: 'var(--surface-2)', border: '1px solid var(--border-2)',
          fontSize: 12, fontWeight: 500, cursor: 'pointer', font: 'inherit', color: 'var(--text)',
        }}>{showLibrary ? 'Close library' : 'Pick from food library…'}</button>
        {showLibrary && (
          <div style={{
            border: '1px solid var(--border)', borderRadius: 12,
            background: 'var(--surface-2)',
            maxHeight: 220, overflow: 'auto',
            padding: 8,
          }}>
            <input value={libQuery} onChange={e=>setLibQuery(e.target.value)} placeholder="Search foods…"
              style={{ ...inputStyle, marginBottom: 8 }}/>
            {filtered.map(f => (
              <button key={f.id} onClick={()=>applyFood(f)} style={{
                width: '100%', textAlign: 'left',
                display: 'flex', alignItems: 'center', gap: 10,
                padding: '8px 10px', borderRadius: 10,
                background: 'var(--surface)', border: '1px solid var(--border)', marginBottom: 6,
                font: 'inherit', cursor: 'pointer', color: 'var(--text)',
              }}>
                <span style={{ fontSize: 22 }}>{f.emoji}</span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>{f.name}</div>
                  <div style={{ fontSize: 11, color: 'var(--muted)' }}>{f.cal} cal · P {f.p}g · C {f.c}g · F {f.f}g · {f.qty}</div>
                </div>
              </button>
            ))}
          </div>
        )}
      </div>
      <div style={{ marginTop: 14, padding: 12, background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 14, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
        <NumberField label="Calories" value={calories} onChange={setCal} suffix="kcal"/>
        <NumberField label="Protein" value={protein} onChange={setProt} suffix="g"/>
        <NumberField label="Carbs"   value={carbs}   onChange={setCarb} suffix="g"/>
        <NumberField label="Fat"     value={fat}     onChange={setFat}  suffix="g"/>
      </div>
    </SheetShell>
  );
}

// ─────────────────────────────────────────────────────────────
// Goal Add Sheet
// ─────────────────────────────────────────────────────────────
function GoalAddSheet({ onClose, onSave }) {
  const [title, setTitle]   = React.useState('');
  const [emoji, setEmoji]   = React.useState('🎯');
  const [tpd, setTpd]       = React.useState('1');
  const [unit, setUnit]     = React.useState('session');
  const [weekdays, setWeekdays] = React.useState([0,1,2,3,4,5,6]);
  const [repeatType, setRepeatType]         = React.useState('weekly');
  const [repeatInterval, setRepeatInterval] = React.useState('7');
  const [color, setColor]   = React.useState('#1F3D2E');
  const [startDate, setStartDate] = React.useState(NUTRI_TODAY);
  const [durNum, setDurNum] = React.useState('30');
  const [durUnit, setDurUnit] = React.useState('days');

  const EMOJIS = ['🎯','💧','📚','🏋️','🧘','🚶','📵','✍️','🌅','😴','🥗','🚴'];
  const COLORS = ['#1F3D2E','#5784D8','#E89B3C','#4FA862','#A99CE0','#C25A4E','#7A766C'];
  const DAYS = [
    { i: 0, l: 'Sun' }, { i: 1, l: 'Mon' }, { i: 2, l: 'Tue' }, { i: 3, l: 'Wed' },
    { i: 4, l: 'Thu' }, { i: 5, l: 'Fri' }, { i: 6, l: 'Sat' },
  ];

  function toggleDay(i) {
    setWeekdays(prev => prev.includes(i) ? prev.filter(x => x !== i) : [...prev, i].sort());
  }

  // Compute end date from start + duration
  function calcEndDate(start, num, unit) {
    if (!num || +num <= 0) return '';
    const d = new Date(start + 'T00:00:00');
    const n = +num;
    if (unit === 'weeks')  d.setDate(d.getDate() + n * 7);
    else if (unit === 'months') d.setMonth(d.getMonth() + n);
    else d.setDate(d.getDate() + n);
    return isoOf(d);
  }
  function toDays(num, unit) {
    const n = +num || 0;
    if (unit === 'weeks')  return n * 7;
    if (unit === 'months') return Math.round(n * 30.44);
    return n;
  }

  const endDate = calcEndDate(startDate, durNum, durUnit);

  return (
    <SheetShell title="New goal" onClose={onClose}
      onSave={() => onSave({
        title, emoji, timesPerDay: tpd, unit,
        weekdays: repeatType === 'weekly' ? weekdays : [0,1,2,3,4,5,6],
        repeatType,
        repeatInterval: repeatType === 'interval' ? (+repeatInterval || 7) : null,
        color, startDate, endDate: endDate || null,
        durationDays: toDays(durNum, durUnit),
      })}
      canSave={!!title.trim()}>
      <div style={{ padding: 12, background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
        <FormRow label="Title">
          <input value={title} onChange={e=>setTitle(e.target.value)} placeholder="e.g. Drink water" style={inputStyle}/>
        </FormRow>
        <FormRow label="Icon">
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
            {EMOJIS.map(e => (
              <button key={e} onClick={()=>setEmoji(e)} style={{
                width: 38, height: 38, borderRadius: 10,
                background: emoji === e ? 'var(--text)' : 'var(--surface-2)',
                border: '1px solid var(--border)',
                fontSize: 18, cursor: 'pointer', font: 'inherit',
              }}>{e}</button>
            ))}
          </div>
        </FormRow>
        <FormRow label="Color">
          <div style={{ display: 'flex', gap: 6 }}>
            {COLORS.map(c => (
              <button key={c} onClick={()=>setColor(c)} style={{
                width: 28, height: 28, borderRadius: '50%',
                background: c, border: color === c ? '2.5px solid var(--text)' : '2.5px solid var(--surface)',
                cursor: 'pointer',
              }} aria-label={c}/>
            ))}
          </div>
        </FormRow>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <NumberField label="Daily target" value={tpd} onChange={setTpd}/>
          <FormRow label="Unit">
            <input value={unit} onChange={e=>setUnit(e.target.value)} placeholder="e.g. min, glasses" style={inputStyle}/>
          </FormRow>
        </div>
        {/* ── Schedule section ── */}
        <div style={{ borderTop: '1px solid var(--border)', paddingTop: 12, display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Schedule</div>
          <Segment options={['weekly','interval']} value={repeatType} onChange={v => setRepeatType(v)}/>
          {repeatType === 'weekly' ? (
            <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
              {DAYS.map(d => {
                const on = weekdays.includes(d.i);
                return (
                  <button key={d.i} onClick={()=>toggleDay(d.i)} style={{
                    padding: '6px 10px', borderRadius: 999,
                    background: on ? 'var(--text)' : 'var(--surface-2)',
                    color: on ? 'var(--bg)' : 'var(--text)',
                    border: on ? '1px solid var(--text)' : '1px solid var(--border-2)',
                    font: 'inherit', fontSize: 11, fontWeight: 600, cursor: 'pointer',
                  }}>{d.l}</button>
                );
              })}
            </div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <span style={{ fontSize: 13, color: 'var(--muted)', whiteSpace: 'nowrap' }}>Every</span>
                <input
                  type="number" inputMode="numeric" min="1" max="365"
                  value={repeatInterval}
                  onChange={e => setRepeatInterval(e.target.value)}
                  style={{ ...inputStyle, width: 72, textAlign: 'center' }}
                />
                <span style={{ fontSize: 13, color: 'var(--muted)', whiteSpace: 'nowrap' }}>days</span>
              </div>
              {(+repeatInterval || 0) > 0 && (
                <div style={{
                  fontSize: 12.5, color: 'var(--muted)', background: 'var(--surface-2)',
                  borderRadius: 10, padding: '8px 12px', border: '1px solid var(--border)',
                }}>
                  🔁 Repeats every <strong>{repeatInterval} days</strong> from the start date
                </div>
              )}
            </div>
          )}
        </div>

        {/* ── Duration section ── */}
        <div style={{ borderTop: '1px solid var(--border)', paddingTop: 12, display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Duration</div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
            <FormRow label="Start date">
              <input type="date" value={startDate} onChange={e=>setStartDate(e.target.value)} style={inputStyle}/>
            </FormRow>
            <FormRow label="Duration">
              <div style={{ display: 'flex', gap: 4 }}>
                <input type="number" inputMode="numeric" value={durNum} onChange={e=>setDurNum(e.target.value)}
                  placeholder="30" style={{ ...inputStyle, width: '50%' }}/>
                <select value={durUnit} onChange={e=>setDurUnit(e.target.value)} style={{ ...inputStyle, width: '50%', padding: '8px 6px' }}>
                  <option value="days">Days</option>
                  <option value="weeks">Weeks</option>
                  <option value="months">Months</option>
                </select>
              </div>
            </FormRow>
          </div>
          {endDate && (
            <div style={{
              fontSize: 12.5, color: 'var(--muted)',
              background: 'var(--surface-2)', borderRadius: 10,
              padding: '8px 12px', border: '1px solid var(--border)',
            }}>
              📅 Ends on <strong>{prettyDate(endDate)}</strong>
              {' '}·{' '}
              {toDays(durNum, durUnit)} days total
            </div>
          )}
        </div>
      </div>
    </SheetShell>
  );
}

// ─────────────────────────────────────────────────────────────
// Shared sheet helpers
// ─────────────────────────────────────────────────────────────
function SheetShell({ title, children, onClose, onSave, canSave = true }) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', flexDirection: 'column',
      background: 'var(--bg)',
      animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)',
      paddingTop: 'var(--safe-top)',
    }}>
      <div style={{
        padding: '12px 16px 10px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        borderBottom: '1px solid var(--border)', flexShrink: 0,
      }}>
        <button onClick={onClose} style={topIcon}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>{title}</div>
        {onSave ? (
          <button onClick={onSave} disabled={!canSave} style={{
            background: canSave ? 'var(--accent)' : 'var(--surface-2)',
            color: canSave ? 'var(--on-accent)' : 'var(--muted)',
            border: '1px solid ' + (canSave ? 'var(--accent)' : 'var(--border-2)'),
            borderRadius: 999, padding: '6px 14px',
            fontSize: 13, fontWeight: 600, cursor: canSave ? 'pointer' : 'default',
            font: 'inherit',
          }}>Save</button>
        ) : <div style={{ width: 36 }}/>}
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 80px' }}>{children}</div>
    </div>
  );
}
function FormRow({ label, children }) {
  return (
    <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
      <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>{label}</span>
      {children}
    </label>
  );
}
function NumberField({ label, value, onChange, suffix }) {
  return (
    <FormRow label={label}>
      <div style={{
        display: 'flex', alignItems: 'center', gap: 6,
        background: 'var(--surface-2)', border: '1px solid var(--border)',
        borderRadius: 10, padding: '8px 12px',
      }}>
        <input type="number" inputMode="decimal" placeholder="0" value={value} onChange={e=>onChange(e.target.value)} style={{
          flex: 1, minWidth: 0, border: 0, background: 'transparent', outline: 'none',
          font: 'inherit', fontSize: 14, color: 'var(--text)',
        }}/>
        {suffix && <span style={{ fontSize: 11, color: 'var(--muted)' }}>{suffix}</span>}
      </div>
    </FormRow>
  );
}
function Segment({ options, value, onChange }) {
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: `repeat(${options.length},1fr)`, gap: 4,
      background: 'var(--surface-2)', borderRadius: 12, padding: 4, border: '1px solid var(--border)',
    }}>
      {options.map(o => (
        <button key={o} onClick={()=>onChange(o)} style={{
          padding: '8px 0', borderRadius: 9, border: 0,
          background: value === o ? 'var(--surface)' : 'transparent',
          color: value === o ? 'var(--text)' : 'var(--muted)',
          fontSize: 12, fontWeight: 600, cursor: 'pointer', font: 'inherit',
          boxShadow: value === o ? 'var(--shadow-sm)' : 'none',
          textTransform: 'capitalize',
        }}>{o}</button>
      ))}
    </div>
  );
}

const topIcon = {
  width: 36, height: 36, borderRadius: '50%',
  background: 'var(--surface)', border: '1px solid var(--border-2)',
  color: 'var(--text)', display: 'grid', placeItems: 'center', cursor: 'pointer',
  font: 'inherit',
};
const inputStyle = {
  width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)',
  borderRadius: 10, padding: '8px 12px', outline: 'none',
  font: 'inherit', fontSize: 14, color: 'var(--text)',
};

const ListIcon   = ({size}) => <Icon size={size}><path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/></Icon>;
const WalletIcon = ({size}) => <Icon size={size}><rect x="2" y="5" width="20" height="14" rx="2"/><path d="M16 13h.01M2 10h20"/></Icon>;
const TagIcon    = ({size}) => <Icon size={size}><path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"/><line x1="7" y1="7" x2="7.01" y2="7"/></Icon>;
const SparklesIcon = ({size}) => <Icon size={size}><path d="M12 3l1.8 4.7L18 9.5l-4.2 1.8L12 16l-1.8-4.7L6 9.5l4.2-1.8z"/><path d="M19 14l.8 1.7L21 17l-1.2 1.3L19 20l-.8-1.7L17 17l1.2-1.3z"/></Icon>;
const RecipeIcon = ({size}) => <Icon size={size}><path d="M4 19V5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v14M4 19h16M8 7h8M8 11h5"/></Icon>;
const HeartNavIcon = ({size}) => <Icon size={size}><path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.6l-1-1a5.5 5.5 0 1 0-7.8 7.8l1 1L12 21l7.8-7.6 1-1a5.5 5.5 0 0 0 0-7.8z"/></Icon>;
const EyeNavIcon = ({size}) => <Icon size={size}><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></Icon>;
const BookmarkNavIcon = ({size}) => <Icon size={size}><path d="M6 3h12a1 1 0 0 1 1 1v17l-7-4-7 4V4a1 1 0 0 1 1-1z"/></Icon>;
const ScaleIcon = ({size}) => <Icon size={size}><path d="M12 3a2 2 0 0 0-2 2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-4a2 2 0 0 0-2-2z"/><path d="M9 5h6"/><path d="M12 9v3l2 1"/></Icon>;

const nutriAppStyle = document.createElement('style');
nutriAppStyle.textContent = `
  @keyframes sheetIn { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
  @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
  @keyframes fadeIn  { from { opacity: 0; } to { opacity: 1; } }
  @keyframes toastIn { from { transform: translate(-50%, 20px); opacity: 0; } to { transform: translate(-50%, 0); opacity: 1; } }
  @keyframes pulse   { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(0.96); opacity: 0.85; } }
  .nutri-scroll-x::-webkit-scrollbar { display: none; }
  body, html { overscroll-behavior: contain; touch-action: manipulation; }
  input, textarea { font-family: inherit !important; }
`;
document.head.appendChild(nutriAppStyle);

window.AddActionSheet = AddActionSheet;
window.CalorieAddSheet = CalorieAddSheet;
window.GoalAddSheet = GoalAddSheet;

// ── Local notification scheduler (driven by NutriShell's per-minute effect) ──
function _notifFiredGet() { try { return JSON.parse(localStorage.getItem('nutri_notif_fired') || '{}') || {}; } catch (_) { return {}; } }
function _notifFiredSet(o) { try { localStorage.setItem('nutri_notif_fired', JSON.stringify(o)); } catch (_) {} }
function _notifParseHM(s) { const m = /^(\d{1,2}):(\d{2})$/.exec(s || ''); return m ? (+m[1]) * 60 + (+m[2]) : null; }
function _notifTodayISO() { const d = new Date(); return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); }
function nutriOnThisDayCount(diary) {
  const now = new Date(), mm = now.getMonth() + 1, dd = now.getDate(), yr = now.getFullYear();
  const pad = n => String(n).padStart(2, '0');
  let count = 0;
  (diary || []).forEach(e => {
    const dk = e.dateKey || (typeof e.day === 'number' ? `${e.year}-${pad(e.month)}-${pad(e.day)}` : (e.date || ''));
    const p = (dk || '').split('-'); if (p.length < 3) return;
    if (+p[1] === mm && +p[2] === dd && +p[0] < yr) count++;
  });
  return count;
}
function nutriGoalsUnfinishedToday(goals) {
  const todayISO = _notifTodayISO(), weekday = new Date().getDay();
  let n = 0;
  (goals || []).forEach(g => {
    const wds = Array.isArray(g.weekdays) ? g.weekdays : [0, 1, 2, 3, 4, 5, 6];
    let scheduled = wds.indexOf(weekday) >= 0;
    if (g.repeatType === 'interval' && +g.repeatInterval > 0 && g.startDate) {
      const diff = Math.floor((new Date(todayISO + 'T00:00:00') - new Date(g.startDate + 'T00:00:00')) / 86400000);
      scheduled = diff >= 0 && diff % (+g.repeatInterval) === 0;
    }
    if (g.startDate && todayISO < g.startDate) scheduled = false;
    if (g.endDate && todayISO > g.endDate) scheduled = false;
    if (!scheduled) return;
    const tpd = +g.timesPerDay || +g.target || 1;
    const arr = (g.log && g.log[todayISO]) || [];
    const done = arr.filter(s => s === 'done').length;
    const skipped = arr.length > 0 && arr.every(s => s === 'skip');
    if (!skipped && done < tpd) n++;
  });
  return n;
}
function _fireNutriNotification(title, body, nav) {
  try {
    if (!('Notification' in window) || Notification.permission !== 'granted') return false;
    const n = new Notification(title, { body: body, icon: './icon.png', tag: 'nutri-' + ((nav && nav.project) || 'x') });
    n.onclick = () => { try { window.focus(); } catch (_) {} window.dispatchEvent(new CustomEvent('nutri:navigate', { detail: nav })); try { n.close(); } catch (_) {} };
    return true;
  } catch (_) { return false; }
}
function runNutriNotificationCheck(store) {
  if (!('Notification' in window) || Notification.permission !== 'granted') return;
  if (!store || !store.notificationSettings) return;
  const ns = store.notificationSettings();
  const today = _notifTodayISO();
  const nowMin = new Date().getHours() * 60 + new Date().getMinutes();
  const fired = _notifFiredGet();
  const dt = _notifParseHM(ns.diary.time);
  if (ns.diary.enabled && dt != null && nowMin >= dt && fired.diary !== today) {
    const count = nutriOnThisDayCount(store.diary ? store.diary() : []);
    if (count > 0) _fireNutriNotification('On this day 📅', 'You have ' + count + ' memor' + (count === 1 ? 'y' : 'ies') + ' from past years.', { project: 'diary', tab: 'home' });
    else if (ns.diary.sendEmpty) _fireNutriNotification('On this day 📅', 'No past memories today — write a new one?', { project: 'diary', tab: 'home' });
    fired.diary = today; _notifFiredSet(fired);
  }
  const gt = _notifParseHM(ns.goals.time);
  if (ns.goals.enabled && gt != null && nowMin >= gt && fired.goals !== today) {
    const left = nutriGoalsUnfinishedToday(store.goals ? store.goals() : []);
    if (left > 0) _fireNutriNotification('Goals reminder 🎯', 'You still have ' + left + ' goal' + (left === 1 ? '' : 's') + ' unfinished today.', { project: 'goals', tab: 'review' });
    else if (ns.goals.sendDone) _fireNutriNotification('Goals complete 🎉', 'All your goals are done today. Nice work!', { project: 'goals', tab: 'home' });
    fired.goals = today; _notifFiredSet(fired);
  }
}
window.runNutriNotificationCheck = runNutriNotificationCheck;

const nutriRoot = ReactDOM.createRoot(document.getElementById('root'));
nutriRoot.render(<NutriApp />);
