<script lang="ts">
  import { onMount } from 'svelte';

  type MealType = 'breakfast' | 'lunch' | 'dinner' | 'snack';
  type FitnessTab = 'log' | 'foods' | 'templates';

  interface FoodEntry {
    id: string;
    name: string;
    serving: string;
    calories: number;
    protein: number;
    carbs: number;
    fat: number;
    method: string;
    image?: string;
    note?: string;
    meal: MealType;
    rawQty: number;
    rawUnit: string;
  }

  interface FoodItem {
    id: string;
    name: string;
    info: string;
    calories: number;
    favorite?: boolean;
  }

  interface Template {
    id: string;
    name: string;
    calories: number;
    items: number;
    meal: string;
  }

  const now = new Date();
  const todayStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;

  let activeTab = $state<FitnessTab>('log');
  let selectedDate = $state(todayStr);
  let expandedMeals = $state<Set<string>>(new Set(['breakfast', 'lunch', 'dinner', 'snack']));
  let foodSearch = $state('');
  let highlightTemplate = $state('');
  let fabOpen = $state(false);
  let loading = $state(true);
  let foodsLoading = $state(false);
  let templatesLoading = $state(false);

  let entries = $state<FoodEntry[]>([]);
  let goal = $state({ calories: 2000, protein: 150, carbs: 200, fat: 65 });
  let foods = $state<FoodItem[]>([]);
  let templates = $state<Template[]>([]);

  // Map backend entry to UI entry
  function mapEntry(e: any): FoodEntry {
    return {
      id: e.id,
      name: e.snapshot_food_name || 'Unknown',
      serving: e.serving_description || e.snapshot_serving_label || `${e.quantity || 1} ${e.unit || 'serving'}`,
      calories: Math.round(e.snapshot_calories || 0),
      protein: Math.round(e.snapshot_protein || 0),
      carbs: Math.round(e.snapshot_carbs || 0),
      fat: Math.round(e.snapshot_fat || 0),
      method: e.entry_method || 'manual',
      image: e.food_image_path,
      note: e.note,
      meal: (e.meal_type || 'snack') as MealType,
      rawQty: e.quantity || 1,
      rawUnit: e.unit || 'serving',
    };
  }

  // Map backend food to UI food (handles both /foods and /foods/recent shapes)
  function mapFood(f: any): FoodItem {
    const id = f.id || f.food_id || '';
    const name = f.name || f.snapshot_food_name || 'Unknown';
    const brand = f.brand ? `${f.brand} · ` : '';
    const unit = f.base_unit === '100g' ? '100g' : (f.servings?.[0]?.label || f.base_unit || 'serving');
    return {
      id,
      name,
      info: `${brand}${unit}`,
      calories: Math.round(f.calories_per_base || 0),
      favorite: f.is_favorite || false,
    };
  }

  // Map backend template to UI template
  function mapTemplate(t: any): Template {
    const items = t.items || [];
    const totalCal = items.reduce((s: number, i: any) => s + (i.snapshot_calories || 0) * (i.quantity || 1), 0);
    return {
      id: t.id,
      name: t.name,
      calories: Math.round(totalCal),
      items: items.length,
      meal: t.meal_type || 'snack',
    };
  }

  async function loadDayData() {
    loading = true;
    try {
      const [entriesRes, goalsRes] = await Promise.all([
        fetch(`/api/fitness/entries?date=${selectedDate}`, { credentials: 'include' }),
        fetch(`/api/fitness/goals/for-date?date=${selectedDate}`, { credentials: 'include' }),
      ]);
      if (entriesRes.ok) {
        const raw = await entriesRes.json();
        entries = (Array.isArray(raw) ? raw : []).map(mapEntry);
      } else {
        entries = [];
      }
      if (goalsRes.ok) {
        const g = await goalsRes.json();
        goal = { calories: g.calories || 2000, protein: g.protein || 150, carbs: g.carbs || 200, fat: g.fat || 65 };
      }
    } catch { /* silent */ }
    loading = false;
  }

  async function loadFoods(query?: string) {
    foodsLoading = true;
    try {
      const url = query
        ? `/api/fitness/foods/search?q=${encodeURIComponent(query)}&limit=30`
        : `/api/fitness/foods?limit=100`;
      const res = await fetch(url, { credentials: 'include' });
      if (res.ok) {
        const raw = await res.json();
        foods = (Array.isArray(raw) ? raw : []).map(mapFood);
      }
    } catch { /* silent */ }
    foodsLoading = false;
  }

  async function loadTemplates() {
    templatesLoading = true;
    try {
      const res = await fetch('/api/fitness/templates', { credentials: 'include' });
      if (res.ok) {
        const raw = await res.json();
        templates = (Array.isArray(raw) ? raw : []).map(mapTemplate);
      }
    } catch { /* silent */ }
    templatesLoading = false;
  }

  let expandedEntry = $state<string | null>(null);

  async function deleteEntry(id: string) {
    expandedEntry = null;
    entries = entries.filter(e => e.id !== id);
    try {
      await fetch(`/api/fitness/entries/${id}`, { method: 'DELETE', credentials: 'include' });
    } catch {
      await loadDayData();
    }
  }

  let editQtyValue = $state('');

  function toggleEntry(id: string) {
    if (expandedEntry === id) {
      expandedEntry = null;
    } else {
      expandedEntry = id;
      const entry = entries.find(e => e.id === id);
      editQtyValue = entry ? String(entry.rawQty) : '1';
    }
  }

  async function updateEntryQty(id: string) {
    const newQty = parseFloat(editQtyValue);
    if (!newQty || newQty <= 0) return;
    expandedEntry = null;
    try {
      const res = await fetch(`/api/fitness/entries/${id}`, {
        method: 'PATCH',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ quantity: newQty }),
      });
      if (res.ok) {
        await loadDayData();
      }
    } catch { /* silent */ }
  }

  async function logTemplate(tpl: Template) {
    try {
      const res = await fetch(`/api/fitness/templates/${tpl.id}/log`, {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ meal_type: tpl.meal, entry_date: selectedDate }),
      });
      if (res.ok) {
        activeTab = 'log';
        await loadDayData();
      }
    } catch { /* silent */ }
  }

  // ── AI Food Resolve (multi-item) ──
  interface ResolvedItem {
    result: any;
    name: string;
    qty: number;
    unit: string;
    calories: number;
    protein: number;
    carbs: number;
    fat: number;
  }

  let resolveInput = $state('');
  let resolveMeal = $state<MealType>('snack');
  let resolving = $state(false);
  let resolvedItems = $state<ResolvedItem[]>([]);
  let resolveError = $state('');
  let confirmLogging = $state(false);

  function guessCurrentMeal(): MealType {
    const h = new Date().getHours();
    if (h >= 6 && h < 11) return 'breakfast';
    if (h >= 11 && h < 15) return 'lunch';
    if (h >= 17 && h < 21) return 'dinner';
    return 'snack';
  }

  async function splitInputItems(input: string): Promise<string[]> {
    // Use AI to split multi-item input into individual food items
    try {
      const res = await fetch('/api/fitness/foods/split', {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ phrase: input }),
      });
      if (res.ok) {
        const data = await res.json();
        if (data.items?.length > 0) return data.items;
      }
    } catch { /* fallback below */ }
    // Fallback: comma split
    return input.split(/,/).map(s => s.trim()).filter(s => s.length > 0);
  }

  function resolveToItem(r: any): ResolvedItem {
    const qty = r.parsed?.quantity || 1;
    const food = r.matched_food;
    let cal = 0, pro = 0, carb = 0, f = 0;
    if (food) {
      cal = (food.calories_per_base || 0) * qty;
      pro = (food.protein_per_base || 0) * qty;
      carb = (food.carbs_per_base || 0) * qty;
      f = (food.fat_per_base || 0) * qty;
    } else if (r.ai_estimate) {
      cal = (r.ai_estimate.calories_per_base || 0) * qty;
      pro = (r.ai_estimate.protein_per_base || 0) * qty;
      carb = (r.ai_estimate.carbs_per_base || 0) * qty;
      f = (r.ai_estimate.fat_per_base || 0) * qty;
    } else if (r.resolution_type === 'quick_add') {
      cal = r.parsed?.quantity || 0;
    }
    const name = r.snapshot_name_override || food?.name || r.ai_estimate?.food_name || r.raw_text || 'Unknown';
    return {
      result: r,
      name,
      qty: Math.round(qty * 10) / 10,
      unit: r.parsed?.unit || 'serving',
      calories: Math.round(cal),
      protein: Math.round(pro),
      carbs: Math.round(carb),
      fat: Math.round(f),
    };
  }

  async function submitResolve() {
    const phrase = resolveInput.trim();
    if (!phrase) return;
    resolving = true;
    resolveError = '';
    resolvedItems = [];
    resolveMeal = guessCurrentMeal();

    const parts = await splitInputItems(phrase);

    try {
      const results = await Promise.all(parts.map(async (part) => {
        const res = await fetch('/api/fitness/foods/resolve', {
          method: 'POST',
          credentials: 'include',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ raw_phrase: part, meal_type: resolveMeal, entry_date: selectedDate }),
        });
        if (!res.ok) {
          if (res.status === 401) throw new Error('Not connected — connect fitness in Settings');
          throw new Error(`Failed to resolve "${part}" (${res.status})`);
        }
        return res.json();
      }));
      resolvedItems = results.map(resolveToItem);
    } catch (e: any) {
      resolveError = e?.message || 'Network error';
    }
    resolving = false;
  }

  function updateItemQty(index: number, newQty: number) {
    if (newQty <= 0) return;
    const item = resolvedItems[index];
    const r = item.result;
    const food = r.matched_food;
    const base = food || r.ai_estimate;
    if (!base) return;
    resolvedItems[index] = {
      ...item,
      qty: Math.round(newQty * 10) / 10,
      calories: Math.round((base.calories_per_base || 0) * newQty),
      protein: Math.round((base.protein_per_base || 0) * newQty),
      carbs: Math.round((base.carbs_per_base || 0) * newQty),
      fat: Math.round((base.fat_per_base || 0) * newQty),
    };
  }

  function removeItem(index: number) {
    resolvedItems = resolvedItems.filter((_, i) => i !== index);
    if (resolvedItems.length === 0) cancelResolve();
  }

  const resolveTotalCal = $derived(resolvedItems.reduce((s, i) => s + i.calories, 0));

  async function confirmResolve() {
    if (resolvedItems.length === 0) return;
    confirmLogging = true;
    try {
      for (const item of resolvedItems) {
        const r = item.result;
        let body: any;
        if (r.resolution_type === 'quick_add' && !r.matched_food) {
          body = {
            entry_type: 'quick_add',
            snapshot_food_name: r.raw_text || 'Quick add',
            snapshot_calories: item.calories,
            meal_type: resolveMeal,
            entry_date: selectedDate,
            source: 'web',
            raw_text: r.raw_text,
          };
        } else if (r.matched_food) {
          body = {
            food_id: r.matched_food.id,
            quantity: item.qty,
            unit: item.unit,
            meal_type: resolveMeal,
            entry_date: selectedDate,
            entry_method: r.resolution_type === 'ai_estimated' ? 'ai_label' : 'search',
            source: 'web',
            raw_text: r.raw_text,
            confidence_score: r.confidence,
            note: r.note || undefined,
            snapshot_food_name_override: r.snapshot_name_override || undefined,
          };
        }
        if (body) {
          await fetch('/api/fitness/entries', {
            method: 'POST',
            credentials: 'include',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(body),
          });
        }
      }
      resolvedItems = [];
      resolveInput = '';
      await loadDayData();
    } catch { /* silent */ }
    confirmLogging = false;
  }

  function cancelResolve() {
    resolvedItems = [];
    resolveError = '';
  }

  // ── Food Edit/Delete ──
  let editingFood = $state<any>(null);
  let editFoodName = $state('');
  let editFoodCal = $state('');
  let editFoodProtein = $state('');
  let editFoodCarbs = $state('');
  let editFoodFat = $state('');
  let editFoodUnit = $state('');
  let savingFood = $state(false);

  function openFoodEdit(food: FoodItem) {
    editingFood = food;
    editFoodName = food.name;
    editFoodCal = String(food.calories);
    editFoodProtein = '';
    editFoodCarbs = '';
    editFoodFat = '';
    editFoodUnit = food.info.split('·').pop()?.trim() || 'serving';
    // Fetch full food data for macros
    fetch(`/api/fitness/foods/${food.id}`, { credentials: 'include' })
      .then(r => r.ok ? r.json() : null)
      .then(data => {
        if (data) {
          editFoodProtein = String(Math.round(data.protein_per_base || 0));
          editFoodCarbs = String(Math.round(data.carbs_per_base || 0));
          editFoodFat = String(Math.round(data.fat_per_base || 0));
          editFoodUnit = data.base_unit || 'serving';
        }
      })
      .catch(() => {});
  }

  function closeFoodEdit() {
    editingFood = null;
  }

  async function saveFoodEdit() {
    if (!editingFood) return;
    savingFood = true;
    try {
      await fetch(`/api/fitness/foods/${editingFood.id}`, {
        method: 'PATCH',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name: editFoodName.trim(),
          calories_per_base: parseFloat(editFoodCal) || 0,
          protein_per_base: parseFloat(editFoodProtein) || 0,
          carbs_per_base: parseFloat(editFoodCarbs) || 0,
          fat_per_base: parseFloat(editFoodFat) || 0,
        }),
      });
      editingFood = null;
      await loadFoods(foodSearch || undefined);
    } catch { /* silent */ }
    savingFood = false;
  }

  async function deleteFood() {
    if (!editingFood) return;
    savingFood = true;
    try {
      await fetch(`/api/fitness/foods/${editingFood.id}`, {
        method: 'DELETE',
        credentials: 'include',
      });
      editingFood = null;
      await loadFoods(foodSearch || undefined);
    } catch { /* silent */ }
    savingFood = false;
  }

  // Reload day data when date changes
  $effect(() => {
    selectedDate; // track
    loadDayData();
  });

  // Reload foods when search changes (debounced)
  let foodSearchTimeout: ReturnType<typeof setTimeout>;
  $effect(() => {
    const q = foodSearch;
    clearTimeout(foodSearchTimeout);
    foodSearchTimeout = setTimeout(() => loadFoods(q || undefined), q ? 300 : 0);
  });

  onMount(() => {
    loadTemplates();
  });

  function jumpToTemplates() {
    activeTab = 'templates';
    if (rankedTemplates.length > 0) {
      highlightTemplate = rankedTemplates[0].name;
      setTimeout(() => { highlightTemplate = ''; }, 2000);
    }
  }

  const filteredFoods = $derived(foods);

  const totals = $derived({
    calories: entries.reduce((s, e) => s + e.calories, 0),
    protein: entries.reduce((s, e) => s + e.protein, 0),
    carbs: entries.reduce((s, e) => s + e.carbs, 0),
    fat: entries.reduce((s, e) => s + e.fat, 0),
    count: entries.length
  });

  const caloriesRemaining = $derived(goal.calories > 0 ? Math.max(goal.calories - totals.calories, 0) : 0);
  const caloriesPercent = $derived(goal.calories > 0 ? Math.round((totals.calories / goal.calories) * 100) : 0);
  const isToday = $derived(selectedDate === todayStr);

  function entriesByMeal(meal: MealType) { return entries.filter(e => e.meal === meal); }
  function mealCalories(meal: MealType) { return entriesByMeal(meal).reduce((s, e) => s + e.calories, 0); }
  function mealProtein(meal: MealType) { return entriesByMeal(meal).reduce((s, e) => s + e.protein, 0); }

  function toggleMeal(meal: string) {
    const next = new Set(expandedMeals);
    if (next.has(meal)) next.delete(meal); else next.add(meal);
    expandedMeals = next;
  }

  function shiftDate(days: number) {
    const d = new Date(selectedDate + 'T00:00:00');
    d.setDate(d.getDate() + days);
    selectedDate = d.toISOString().split('T')[0];
  }

  function formatDate(date: string) {
    const d = new Date(date + 'T00:00:00');
    return d.toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' });
  }

  // Action-oriented coaching based on progress
  function coachMessage(remaining: number, percent: number): string {
    if (percent >= 100) return 'Done for the day — you hit your goal';
    if (percent >= 90) return 'Almost done — keep it light';
    if (percent >= 75) return 'Finish strong — ' + remaining.toLocaleString() + ' cal left';
    if (percent >= 50) return 'You\'ve got room for a solid meal';
    if (percent >= 25) return 'On track — plan your next meal';
    if (percent > 0) return 'Just getting started — ' + remaining.toLocaleString() + ' cal to work with';
    return 'Start logging to see your progress';
  }

  // Coaching focus — what the user needs most right now
  type CoachFocus = 'protein' | 'low_carb' | 'low_fat' | 'light' | 'balanced';

  function getCoachFocus(t: typeof totals, g: typeof goal, calLeft: number): CoachFocus {
    const proteinPct = t.protein / g.protein;
    const carbsPct = t.carbs / g.carbs;
    const fatPct = t.fat / g.fat;
    if (calLeft < 300) return 'light';
    if (proteinPct < 0.7) return 'protein';
    if (carbsPct > 0.8 && proteinPct < 0.8) return 'low_carb';
    if (fatPct > 0.8) return 'low_fat';
    return 'balanced';
  }

  const coachFocus = $derived(getCoachFocus(totals, goal, caloriesRemaining));

  // "Best next move" — realistic meal suggestions
  function bestNextMove(t: typeof totals, g: typeof goal, calRemaining: number): string {
    if (calRemaining <= 0) return '';

    const proteinPct = t.protein / g.protein;
    const carbsPct = t.carbs / g.carbs;
    const fatPct = t.fat / g.fat;
    const proteinLeft = Math.max(g.protein - t.protein, 0);

    // Protein-focused — use realistic meal combos
    if (proteinPct < 0.4 && proteinLeft > 40) {
      const lo = Math.round(proteinLeft * 0.3);
      const hi = Math.round(proteinLeft * 0.45);
      return `Best next: ~${lo}–${hi}g protein (chicken + rice, 3 eggs, or a shake)`;
    }
    if (proteinPct < 0.6 && proteinLeft > 25) {
      const lo = Math.round(proteinLeft * 0.35);
      const hi = Math.round(proteinLeft * 0.5);
      return `Best next: ~${lo}–${hi}g protein (chicken, Greek yogurt, or protein shake)`;
    }
    if (proteinPct < 0.8 && proteinLeft > 10) {
      return `Best next: ~${Math.round(proteinLeft * 0.6)}–${proteinLeft}g protein (eggs, yogurt, or tuna)`;
    }

    // Carbs running high + still need protein
    if (carbsPct > 0.8 && proteinPct < 0.75) {
      return 'Best next: skip the carbs — chicken salad, eggs, or a protein shake';
    }

    // Fat running high
    if (fatPct > 0.85) {
      return 'Best next: lean protein + rice or sweet potato — skip the oils';
    }
    if (fatPct > 0.7 && carbsPct < 0.5) {
      return 'Best next: oatmeal, rice bowl, or pasta with grilled chicken';
    }

    // Low calories remaining
    if (calRemaining < 200) {
      return 'Best next: something light — yogurt, fruit, or veggies';
    }
    if (calRemaining < 400) {
      return 'Best next: small meal — salad with chicken, or a shake with fruit';
    }

    // Everything balanced
    return 'Best next: balanced plate — protein, carbs, and veggies';
  }

  // Macro guidance — considers both target gap and calories budget
  function macroInstruction(macro: 'protein' | 'carbs' | 'fat', current: number, target: number, calLeft: number): string {
    const pct = Math.round((current / target) * 100);
    const left = Math.max(target - current, 0);
    if (pct >= 100) return 'On target';

    const budgetTight = calLeft < 400;

    if (macro === 'protein') {
      if (pct < 50 && !budgetTight) return 'Prioritize protein';
      if (pct < 50 && budgetTight) return 'Add lean protein';
      if (pct < 80) return 'Add protein';
      return 'Almost there';
    }
    if (macro === 'carbs') {
      if (pct >= 85) return 'Ease off carbs';
      if (pct >= 70) return 'Watch carbs';
      return 'Carbs on track';
    }
    // fat
    if (pct >= 85) return 'Keep fats minimal';
    if (pct >= 70) return 'Keep fats light';
    return 'Fats on track';
  }

  function macroLeft(current: number, target: number): string {
    const left = Math.max(target - current, 0);
    if (left === 0) return '';
    return `${left}g left`;
  }

  // Meal weight label — contextual per meal type
  function mealWeight(mealCal: number, dailyGoal: number, meal: MealType): string {
    if (mealCal === 0) return '';
    const pct = Math.round((mealCal / dailyGoal) * 100);

    if (meal === 'snack') {
      // Snacks: bias toward Light/Snack
      if (pct >= 25) return 'Heavy';
      if (pct >= 12) return 'Moderate';
      return 'Light';
    }
    if (meal === 'breakfast') {
      // Breakfast: cap at Moderate
      if (pct >= 30) return 'Moderate';
      if (pct >= 12) return 'Light';
      return 'Snack';
    }
    // Lunch / Dinner: normal scale
    if (pct >= 35) return 'Heavy';
    if (pct >= 20) return 'Moderate';
    if (pct >= 8) return 'Light';
    return 'Snack';
  }

  // Template scoring — decisive ranking aligned with coaching focus
  function templateScore(tpl: Template, t: typeof totals, g: typeof goal, calLeft: number, focus: CoachFocus): number {
    let score = 0;
    const hour = new Date().getHours();
    const isProteinRich = /protein|chicken|egg|salmon/i.test(tpl.name);
    const isCarbHeavy = /oatmeal|rice|pasta/i.test(tpl.name);

    // ── Calorie fit (highest weight) ──
    if (calLeft > 0) {
      if (tpl.calories <= calLeft) {
        const ratio = tpl.calories / calLeft;
        // Perfect fit: within ±15%
        if (ratio >= 0.85 && ratio <= 1.0) score += 50;
        // Good fit: within ±30%
        else if (ratio >= 0.7) score += 35;
        // Usable: under budget but not close
        else if (ratio >= 0.4) score += 15;
      } else {
        // Over remaining — penalize harder
        const overBy = (tpl.calories - calLeft) / calLeft;
        score -= overBy > 0.3 ? 30 : 15;
      }
    }

    // ── Coaching alignment (reinforce "Best next" direction) ──
    if (focus === 'protein' && isProteinRich) score += 35;
    if (focus === 'protein' && !isProteinRich) score -= 5;
    if (focus === 'low_carb' && isCarbHeavy) score -= 20;
    if (focus === 'low_carb' && isProteinRich) score += 20;
    if (focus === 'low_fat' && isProteinRich) score += 15;
    if (focus === 'light' && tpl.calories < 350) score += 20;
    if (focus === 'light' && tpl.calories > 500) score -= 20;

    // ── Time-of-day ──
    if (tpl.meal === 'breakfast' && hour >= 6 && hour < 11) score += 15;
    if (tpl.meal === 'lunch' && hour >= 11 && hour < 15) score += 15;
    if (tpl.meal === 'dinner' && hour >= 17 && hour < 21) score += 15;
    if (tpl.meal === 'snack' && hour >= 14 && hour < 17) score += 10;

    // ── Macro penalties ──
    const carbsPct = t.carbs / g.carbs;
    const fatPct = t.fat / g.fat;
    if (carbsPct > 0.85 && isCarbHeavy) score -= 15;
    if (fatPct > 0.85) score -= 5;

    return score;
  }

  // Sorted templates by score (descending)
  const rankedTemplates = $derived(
    [...templates]
      .map(tpl => ({ tpl, score: templateScore(tpl, totals, goal, caloriesRemaining, coachFocus) }))
      .sort((a, b) => b.score - a.score)
      .map(item => item.tpl)
  );

  // Template hints — selective, max 3 total, top-ranked only
  function templateHints(ranked: Template[], t: typeof totals, g: typeof goal, calLeft: number): Map<string, string> {
    const hints = new Map<string, string>();
    const hour = new Date().getHours();
    const proteinPct = t.protein / g.protein;
    const proteinPhrases = ['High protein option', 'Great for protein', 'Helps close your protein gap'];
    let proteinIdx = 0;
    const MAX_HINTS = 3;

    // Only consider top-ranked templates for hints
    const candidates = ranked.slice(0, Math.min(ranked.length, 5));

    for (const tpl of candidates) {
      if (hints.size >= MAX_HINTS) break;

      // Calorie fit — tight ±15%
      if (calLeft > 0 && tpl.calories <= calLeft) {
        const ratio = tpl.calories / calLeft;
        if (ratio >= 0.85 && ratio <= 1.0) {
          hints.set(tpl.name, 'Fits your remaining calories');
          continue;
        }
      }

      // Protein hint — varied phrasing, max 2
      const isProteinRich = /protein|chicken|egg|salmon/i.test(tpl.name);
      if (isProteinRich && proteinPct < 0.7 && proteinIdx < 2) {
        hints.set(tpl.name, proteinPhrases[proteinIdx]);
        proteinIdx++;
        continue;
      }

      // Time-of-day — max 1
      if (!Array.from(hints.values()).some(h => h.startsWith('Good '))) {
        if (tpl.meal === 'breakfast' && hour >= 6 && hour < 11) { hints.set(tpl.name, 'Good for this morning'); continue; }
        if (tpl.meal === 'lunch' && hour >= 11 && hour < 15) { hints.set(tpl.name, 'Good for lunch right now'); continue; }
        if (tpl.meal === 'dinner' && hour >= 17 && hour < 21) { hints.set(tpl.name, 'Good dinner choice'); continue; }
        if (tpl.meal === 'snack' && hour >= 14 && hour < 17) { hints.set(tpl.name, 'Good afternoon pick-me-up'); continue; }
      }
    }

    return hints;
  }

  const templateHintMap = $derived(templateHints(rankedTemplates, totals, goal, caloriesRemaining));

  const mealTypes: MealType[] = ['breakfast', 'lunch', 'dinner', 'snack'];
</script>

<div class="page">
  <div class="app-surface">
    <!-- Header -->
    <div class="page-header">
      <div class="page-title">FITNESS</div>
      <div class="page-date">{#if isToday}Today{:else}{formatDate(selectedDate)}{/if}</div>
    </div>

    <!-- Top tabs (subtle) -->
    <div class="top-tabs">
      <button class="top-tab" class:active={activeTab === 'log'} onclick={() => activeTab = 'log'}>Food Log</button>
      <button class="top-tab" class:active={activeTab === 'foods'} onclick={() => activeTab = 'foods'}>Food Library</button>
      <button class="top-tab" class:active={activeTab === 'templates'} onclick={() => activeTab = 'templates'}>Quick Meals</button>
    </div>

    <!-- ═══════════════════════════════ -->
    <!-- TAB: Food Log                   -->
    <!-- ═══════════════════════════════ -->
    {#if activeTab === 'log'}

      <!-- Date nav -->
      <div class="date-nav">
        <button class="btn-icon" onclick={() => shiftDate(-1)} aria-label="Previous day">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
        </button>
        <input type="date" class="date-input" bind:value={selectedDate} />
        <button class="btn-icon" onclick={() => shiftDate(1)} aria-label="Next day">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
        </button>
        {#if !isToday}
          <button class="today-btn" onclick={() => selectedDate = todayStr}>Today</button>
        {/if}
      </div>

      <!-- Calorie hero -->
      <div class="calorie-card">
        <div class="calorie-eaten">
          <span class="calorie-number">{totals.calories.toLocaleString()}</span>
          <span class="calorie-of">/ {goal.calories.toLocaleString()}</span>
        </div>
        <div class="progress-bar">
          <div class="progress-fill" style="width:{Math.min(caloriesPercent, 100)}%"></div>
        </div>
        <div class="calorie-coach">{coachMessage(caloriesRemaining, caloriesPercent)}</div>
        {#if caloriesRemaining > 0}
          {@const hint = bestNextMove(totals, goal, caloriesRemaining)}
          {#if hint}
            <button class="calorie-next" onclick={jumpToTemplates}>{hint} <span class="next-arrow">→</span></button>
          {/if}
        {/if}
      </div>

      <!-- AI food input -->
      <div class="resolve-bar">
        <input
          type="text"
          class="resolve-input"
          placeholder="Describe your meal... (e.g. 2 eggs and toast)"
          bind:value={resolveInput}
          onkeydown={(e) => { if (e.key === 'Enter') submitResolve(); }}
          disabled={resolving}
        />
        <button class="resolve-submit" onclick={submitResolve} disabled={resolving || !resolveInput.trim()}>
          {#if resolving}
            <span class="resolve-spinner"></span>
          {:else}
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
          {/if}
        </button>
      </div>
      {#if resolveError}
        <div class="resolve-error">{resolveError}</div>
      {/if}

      <!-- Macros — inline row, not cards -->
      <div class="macro-row">
        <div class="macro-item">
          <div class="macro-bar-wrap">
            <div class="macro-bar-fill protein" style="width:{Math.min(totals.protein / goal.protein * 100, 100)}%"></div>
          </div>
          <div class="macro-label">
            <span class="macro-current">{totals.protein}g</span>
            <span class="macro-name">Protein</span>
          </div>
          <div class="macro-guidance">
            <div class="macro-instruction">{macroInstruction('protein', totals.protein, goal.protein, caloriesRemaining)}</div>
            <div class="macro-left">{macroLeft(totals.protein, goal.protein)}</div>
          </div>
        </div>
        <div class="macro-item">
          <div class="macro-bar-wrap">
            <div class="macro-bar-fill carbs" style="width:{Math.min(totals.carbs / goal.carbs * 100, 100)}%"></div>
          </div>
          <div class="macro-label">
            <span class="macro-current">{totals.carbs}g</span>
            <span class="macro-name">Carbs</span>
          </div>
          <div class="macro-guidance">
            <div class="macro-instruction">{macroInstruction('carbs', totals.carbs, goal.carbs, caloriesRemaining)}</div>
            <div class="macro-left">{macroLeft(totals.carbs, goal.carbs)}</div>
          </div>
        </div>
        <div class="macro-item">
          <div class="macro-bar-wrap">
            <div class="macro-bar-fill fat" style="width:{Math.min(totals.fat / goal.fat * 100, 100)}%"></div>
          </div>
          <div class="macro-label">
            <span class="macro-current">{totals.fat}g</span>
            <span class="macro-name">Fat</span>
          </div>
          <div class="macro-guidance">
            <div class="macro-instruction">{macroInstruction('fat', totals.fat, goal.fat, caloriesRemaining)}</div>
            <div class="macro-left">{macroLeft(totals.fat, goal.fat)}</div>
          </div>
        </div>
      </div>

      <!-- Meals label -->
      <div class="meals-divider">
        <span class="meals-label">Meals</span>
        <span class="meals-count">{totals.count} entries</span>
      </div>

      <!-- Meal sections -->
      {#each mealTypes as meal, i}
        {@const mealEntries = entriesByMeal(meal)}
        {@const mCal = mealCalories(meal)}
        {@const mPro = mealProtein(meal)}
        {@const expanded = expandedMeals.has(meal)}
        {@const weight = mealWeight(mCal, goal.calories, meal)}
        {@const mealPct = mCal > 0 ? Math.round((mCal / goal.calories) * 100) : 0}

        <div class="meal-section">
          <button class="meal-header" onclick={() => toggleMeal(meal)}>
            <div class="meal-number" class:has-entries={mealEntries.length > 0}>{i + 1}</div>
            <div class="meal-info">
              <div class="meal-top">
                <span class="meal-name">{meal}</span>
                {#if weight}
                  <span class="meal-weight {weight.toLowerCase()}">{weight}</span>
                {/if}
              </div>
              {#if mCal > 0}
                <span class="meal-stats">{Math.round(mCal)} cal · {mealPct}% of daily · {Math.round(mPro)}g protein</span>
              {:else}
                <span class="meal-stats empty">No entries yet</span>
              {/if}
            </div>
            <svg class="meal-chevron" class:expanded viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
          </button>

          {#if expanded}
            <div class="meal-entries">
              {#if mealEntries.length > 0}
                {#each mealEntries as entry}
                  <div class="entry-wrap">
                    <!-- svelte-ignore a11y_no_static_element_interactions -->
                    <div class="entry-row" onclick={() => toggleEntry(entry.id)}>
                      <div class="entry-info">
                        <div class="entry-name">
                          {entry.name}
                          {#if entry.method === 'ai_plate' || entry.method === 'ai_label'}
                            <span class="entry-badge ai">AI</span>
                          {/if}
                          {#if entry.method === 'quick_add'}
                            <span class="entry-badge quick">quick</span>
                          {/if}
                        </div>
                        <div class="entry-serving">{entry.serving}</div>
                        {#if entry.note}
                          <div class="entry-note">{entry.note}</div>
                        {/if}
                      </div>
                      <div class="entry-cals">
                        <span class="entry-cal-value">{entry.calories}</span>
                        <span class="entry-cal-label">cal</span>
                      </div>
                    </div>
                    {#if expandedEntry === entry.id}
                      <div class="entry-actions">
                        <div class="entry-qty-control">
                          <input
                            type="number"
                            class="entry-qty-input"
                            bind:value={editQtyValue}
                            onkeydown={(e) => { if (e.key === 'Enter') updateEntryQty(entry.id); }}
                            step="0.5"
                            min="0.1"
                          />
                          <span class="entry-qty-unit">{entry.rawUnit}</span>
                          <button class="entry-qty-save" onclick={() => updateEntryQty(entry.id)}>Save</button>
                        </div>
                        <button class="entry-delete-btn" onclick={() => deleteEntry(entry.id)}>
                          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
                          Delete
                        </button>
                      </div>
                    {/if}
                  </div>
                {/each}
              {/if}

              <button class="add-food-btn" onclick={() => { resolveMeal = meal; const input = document.querySelector('.resolve-input') as HTMLInputElement; if (input) { input.focus(); input.placeholder = `Add to ${meal}...`; } }}>
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                Add to {meal}
              </button>
            </div>
          {/if}
        </div>
      {/each}

    <!-- ═══════════════════════════════ -->
    <!-- TAB: Foods (neutral utility)    -->
    <!-- ═══════════════════════════════ -->
    {:else if activeTab === 'foods'}

      <div class="foods-nudge">
        Looking for a quick option? <button class="nudge-link" onclick={() => activeTab = 'templates'}>Try Quick Meals →</button>
      </div>

      <div class="list-header">
        <div class="search-wrap">
          <svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
          <input type="text" class="search-input" placeholder="Search food library..." bind:value={foodSearch} />
          {#if foodSearch}
            <button class="search-clear" onclick={() => foodSearch = ''}>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
            </button>
          {/if}
        </div>
      </div>

      <div class="list-card">
        {#each filteredFoods as food (food.name)}
          <!-- svelte-ignore a11y_no_static_element_interactions -->
          <div class="list-row" onclick={() => openFoodEdit(food)}>
            <div class="list-row-info">
              <div class="list-row-name">
                {food.name}
                {#if food.favorite}
                  <svg class="fav-icon" viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
                {/if}
              </div>
              <div class="list-row-meta">{food.info}</div>
            </div>
            <div class="list-row-right">
              <span class="list-row-value">{food.calories} cal</span>
              <svg class="list-row-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
            </div>
          </div>
        {/each}
        {#if filteredFoods.length === 0}
          <div class="empty-state">No foods found for "{foodSearch}"</div>
        {/if}
      </div>

    <!-- ═══════════════════════════════ -->
    <!-- TAB: Templates                  -->
    <!-- ═══════════════════════════════ -->
    {:else if activeTab === 'templates'}

      <div class="list-header">
        <div class="list-count">{#if templatesLoading}Loading...{:else if templates.length === 0}No quick meals yet{:else}{templates.length} go-to meals · ranked for you{/if}</div>
      </div>

      <div class="list-card">
        {#each rankedTemplates as tpl}
          {@const hint = templateHintMap.get(tpl.name) || ''}
          <div class="list-row" class:highlighted={highlightTemplate === tpl.name}>
            <div class="template-badge">{tpl.meal.charAt(0).toUpperCase()}</div>
            <div class="list-row-info">
              <div class="list-row-name">{tpl.name}</div>
              <div class="list-row-meta"><span class="template-meal-label">{tpl.meal}</span> · {tpl.calories} cal</div>
              <div class="list-row-sub">{tpl.items} items</div>
              {#if hint}
                <div class="template-hint">{hint}</div>
              {/if}
            </div>
            <div class="list-row-right">
              <button class="log-btn" onclick={() => logTemplate(tpl)}>Log meal</button>
              <svg class="list-row-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
            </div>
          </div>
        {/each}
      </div>

    {/if}
  </div>
</div>

<!-- FAB — always visible -->
<button class="fab" class:open={fabOpen} aria-label="Add" onclick={() => fabOpen = !fabOpen}>
  <svg class="fab-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
</button>

<!-- FAB bottom sheet -->
{#if fabOpen}
  <!-- svelte-ignore a11y_no_static_element_interactions -->
  <div class="fab-overlay" onclick={() => fabOpen = false}></div>
  <div class="fab-sheet">
    <div class="fab-sheet-handle"></div>
    <button class="fab-action" onclick={() => { fabOpen = false; activeTab = 'log'; }}>
      <div class="fab-action-icon accent">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
      </div>
      <div class="fab-action-text">
        <div class="fab-action-name">Log food</div>
        <div class="fab-action-desc">Add an entry to your daily log</div>
      </div>
    </button>
    <button class="fab-action" onclick={() => { fabOpen = false; activeTab = 'foods'; foodSearch = ''; }}>
      <div class="fab-action-icon">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
      </div>
      <div class="fab-action-text">
        <div class="fab-action-name">Search food</div>
        <div class="fab-action-desc">Find a food in your library</div>
      </div>
    </button>
    <button class="fab-action" onclick={() => { fabOpen = false; activeTab = 'foods'; }}>
      <div class="fab-action-icon">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2v6m0 0l3-3m-3 3L9 5"/><rect x="3" y="10" width="18" height="12" rx="2"/></svg>
      </div>
      <div class="fab-action-text">
        <div class="fab-action-name">Create food</div>
        <div class="fab-action-desc">Add a new food to your library</div>
      </div>
    </button>
    <button class="fab-action" onclick={() => { fabOpen = false; activeTab = 'templates'; }}>
      <div class="fab-action-icon">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M9 21V9"/></svg>
      </div>
      <div class="fab-action-text">
        <div class="fab-action-name">Create quick meal</div>
        <div class="fab-action-desc">Save a reusable meal preset</div>
      </div>
    </button>
  </div>
{/if}

<!-- Food edit modal -->
{#if editingFood}
  <!-- svelte-ignore a11y_no_static_element_interactions -->
  <div class="resolve-overlay" onclick={closeFoodEdit}>
    <!-- svelte-ignore a11y_no_static_element_interactions -->
    <div class="resolve-modal" onclick={(e) => e.stopPropagation()}>
      <div class="resolve-modal-header">
        <div class="resolve-modal-title">Edit Food</div>
        <button class="resolve-modal-close" onclick={closeFoodEdit}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
        </button>
      </div>
      <div class="resolve-modal-body">
        <div class="food-edit-field">
          <label class="food-edit-label">Name</label>
          <input class="food-edit-input" type="text" bind:value={editFoodName} />
        </div>
        <div class="food-edit-row">
          <div class="food-edit-field">
            <label class="food-edit-label">Calories</label>
            <input class="food-edit-input" type="number" bind:value={editFoodCal} />
          </div>
          <div class="food-edit-field">
            <label class="food-edit-label">Protein (g)</label>
            <input class="food-edit-input" type="number" bind:value={editFoodProtein} />
          </div>
        </div>
        <div class="food-edit-row">
          <div class="food-edit-field">
            <label class="food-edit-label">Carbs (g)</label>
            <input class="food-edit-input" type="number" bind:value={editFoodCarbs} />
          </div>
          <div class="food-edit-field">
            <label class="food-edit-label">Fat (g)</label>
            <input class="food-edit-input" type="number" bind:value={editFoodFat} />
          </div>
        </div>
        <div class="food-edit-unit">Per 1 {editFoodUnit}</div>
      </div>
      <div class="resolve-modal-footer">
        <button class="entry-delete-btn" onclick={deleteFood} disabled={savingFood}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
          Delete
        </button>
        <div class="resolve-footer-actions">
          <button class="btn-secondary" onclick={closeFoodEdit}>Cancel</button>
          <button class="btn-primary" onclick={saveFoodEdit} disabled={savingFood}>
            {savingFood ? 'Saving...' : 'Save'}
          </button>
        </div>
      </div>
    </div>
  </div>
{/if}

<!-- Resolve confirmation modal -->
{#if resolvedItems.length > 0}
  <!-- svelte-ignore a11y_no_static_element_interactions -->
  <div class="resolve-overlay" onclick={cancelResolve}>
    <!-- svelte-ignore a11y_no_static_element_interactions -->
    <div class="resolve-modal" onclick={(e) => e.stopPropagation()}>
      <div class="resolve-modal-header">
        <div class="resolve-modal-title">
          {resolvedItems.length === 1 ? 'Confirm entry' : `Confirm ${resolvedItems.length} items`}
        </div>
        <button class="resolve-modal-close" onclick={cancelResolve}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
        </button>
      </div>

      <div class="resolve-modal-body">
        {#each resolvedItems as item, idx}
          <div class="resolve-item" class:resolve-item-border={idx > 0}>
            <div class="resolve-item-top">
              <div class="resolve-item-info">
                <div class="resolve-food-name">{item.name}</div>
                <div class="resolve-food-detail">{item.calories} cal · {item.protein}g P · {item.carbs}g C · {item.fat}g F</div>
              </div>
              {#if resolvedItems.length > 1}
                <button class="resolve-item-remove" onclick={() => removeItem(idx)} aria-label="Remove">
                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
                </button>
              {/if}
            </div>
            <div class="resolve-qty-row">
              <button class="resolve-qty-btn" onclick={() => updateItemQty(idx, item.qty - 0.5)} disabled={item.qty <= 0.5}>−</button>
              <span class="resolve-qty-value">{item.qty} {item.unit}</span>
              <button class="resolve-qty-btn" onclick={() => updateItemQty(idx, item.qty + 0.5)}>+</button>
            </div>
          </div>
        {/each}

        <div class="resolve-meal-row">
          <span class="resolve-meal-label">Meal</span>
          <select class="resolve-meal-select" bind:value={resolveMeal}>
            <option value="breakfast">Breakfast</option>
            <option value="lunch">Lunch</option>
            <option value="dinner">Dinner</option>
            <option value="snack">Snack</option>
          </select>
        </div>

        {#if resolvedItems.some(i => i.result.resolution_type === 'ai_estimated')}
          <div class="resolve-note">Some items estimated by AI — values are approximate</div>
        {/if}
      </div>

      <div class="resolve-modal-footer">
        <div class="resolve-total">{resolveTotalCal} cal total</div>
        <div class="resolve-footer-actions">
          <button class="btn-secondary" onclick={cancelResolve}>Cancel</button>
          <button class="btn-primary" onclick={confirmResolve} disabled={confirmLogging}>
            {confirmLogging ? 'Logging...' : resolvedItems.length === 1 ? 'Log entry' : `Log ${resolvedItems.length} entries`}
          </button>
        </div>
      </div>
    </div>
  </div>
{/if}

<style>
  /* ── Top tabs (subtle segmented control) ── */
  .top-tabs {
    display: flex; gap: var(--sp-0.5); margin-bottom: var(--sp-5);
    border-bottom: 1px solid var(--border); padding-bottom: 0;
  }
  .top-tab {
    flex: 1; padding: 10px 8px 12px; font-size: var(--text-sm); font-weight: 500;
    color: var(--text-3); background: none; border: none; border-bottom: 2px solid transparent;
    cursor: pointer; font-family: var(--font); transition: all var(--transition);
    text-align: center; margin-bottom: -1px;
  }
  .top-tab:hover { color: var(--text-2); }
  .top-tab.active { color: var(--text-1); border-bottom-color: var(--accent); font-weight: 600; }

  /* ── Header ── */
  .page-date { font-size: var(--text-2xl); font-weight: 300; color: var(--text-1); line-height: 1.2; }

  /* ── Date nav ── */
  .date-nav { display: flex; align-items: center; gap: var(--sp-1.5); margin-bottom: var(--sp-5); }
  .btn-icon { width: 34px; height: 34px; border-radius: var(--radius-md); background: var(--card); border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-3); transition: all var(--transition); flex-shrink: 0; }
  .btn-icon:hover { color: var(--text-1); background: var(--card-hover); }
  .btn-icon svg { width: 16px; height: 16px; }
  .date-input { padding: 6px 10px; border-radius: var(--radius-md); border: 1px solid var(--border); background: var(--card); color: var(--text-1); font-size: var(--text-sm); font-family: var(--font); }
  .today-btn { padding: 6px 12px; border-radius: var(--radius-md); background: var(--accent-bg); color: var(--accent); border: none; font-size: var(--text-sm); font-weight: 600; cursor: pointer; font-family: var(--font); transition: all var(--transition); }
  .today-btn:hover { opacity: 0.8; }

  /* ── Calorie hero ── */
  .calorie-card {
    background: var(--card); border-radius: var(--radius); padding: var(--sp-6);
    border: 1px solid var(--border); box-shadow: var(--card-shadow);
    margin-bottom: var(--sp-4);
  }
  .calorie-eaten { display: flex; align-items: baseline; gap: var(--sp-1.5); margin-bottom: 14px; }
  .calorie-number { font-size: var(--text-3xl); font-weight: 300; font-family: var(--mono); color: var(--text-1); line-height: 1; letter-spacing: -0.02em; }
  .calorie-of { font-size: var(--text-md); color: var(--text-3); font-family: var(--mono); }
  .progress-bar { height: 8px; background: var(--border); border-radius: 4px; overflow: hidden; margin-bottom: var(--sp-3); }
  .progress-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.6s ease; }
  .calorie-coach { font-size: var(--text-md); font-weight: 500; color: var(--text-2); line-height: 1.4; }
  .calorie-next {
    display: inline-flex; align-items: center; gap: var(--sp-1);
    font-size: var(--text-sm); color: var(--accent); margin-top: var(--sp-1.5); font-weight: 500; opacity: 0.85;
    background: none; border: none; padding: 0; cursor: pointer;
    font-family: var(--font); text-align: left; transition: opacity var(--transition);
  }
  .calorie-next:hover { opacity: 1; }
  .next-arrow { font-size: var(--text-sm); opacity: 0.6; transition: transform var(--transition); }
  .calorie-next:hover .next-arrow { transform: translateX(2px); opacity: 1; }

  /* ── Macro row (compact, not cards) ── */
  .macro-row {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3);
    margin-bottom: var(--section-gap);
  }
  .macro-item {
    background: var(--card); border-radius: 10px; padding: 14px;
    border: 1px solid var(--border);
  }
  .macro-bar-wrap { height: 4px; background: var(--border); border-radius: 2px; overflow: hidden; margin-bottom: 10px; }
  .macro-bar-fill { height: 100%; border-radius: 2px; transition: width 0.6s ease; }
  .macro-bar-fill.protein { background: #8B5CF6; }
  .macro-bar-fill.carbs { background: #F59E0B; }
  .macro-bar-fill.fat { background: #3B82F6; }
  .macro-label { display: flex; align-items: baseline; gap: 5px; margin-bottom: 2px; }
  .macro-current { font-size: var(--text-md); font-weight: 500; font-family: var(--mono); color: var(--text-1); letter-spacing: -0.01em; }
  .macro-name { font-size: var(--text-sm); color: var(--text-3); }
  .macro-guidance { margin-top: var(--sp-2); border-top: 1px solid var(--border); padding-top: var(--sp-2); }
  .macro-instruction { font-size: var(--text-sm); font-weight: 500; color: var(--text-2); line-height: 1.3; }
  .macro-left { font-size: var(--text-sm); color: var(--text-3); margin-top: 1px; font-family: var(--mono); }

  /* ── Meals divider ── */
  .meals-divider {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: var(--sp-2); padding-bottom: var(--sp-2);
  }
  .meals-label { font-size: var(--text-sm); font-weight: 600; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.06em; }
  .meals-count { font-size: var(--text-sm); color: var(--text-4); }

  /* ── Meal sections ── */
  .meal-section { margin-bottom: var(--sp-1); }
  .meal-header {
    display: flex; align-items: center; gap: var(--sp-3); width: 100%;
    padding: var(--sp-3) 0; background: none; border: none; cursor: pointer;
    font-family: var(--font); color: inherit; text-align: left;
  }
  .meal-number {
    width: 32px; height: 32px; border-radius: 50%;
    background: var(--card-hover); display: flex; align-items: center; justify-content: center;
    font-size: var(--text-sm); font-weight: 600; color: var(--text-4); flex-shrink: 0;
    transition: all var(--transition);
  }
  .meal-number.has-entries { background: var(--accent-bg); color: var(--accent); }
  .meal-info { flex: 1; min-width: 0; }
  .meal-top { display: flex; align-items: center; gap: var(--sp-2); }
  .meal-name { font-size: var(--text-md); font-weight: 600; color: var(--text-1); text-transform: capitalize; }
  .meal-weight { font-size: var(--text-xs); font-weight: 600; padding: 1px 7px; border-radius: var(--radius-xs); text-transform: uppercase; letter-spacing: 0.03em; }
  .meal-weight.heavy { background: rgba(239,68,68,0.1); color: #DC2626; }
  .meal-weight.moderate { background: rgba(245,158,11,0.1); color: #B45309; }
  .meal-weight.light { background: rgba(34,197,94,0.1); color: #15803D; }
  .meal-weight.snack { background: var(--card-hover); color: var(--text-3); }
  .meal-stats { font-size: var(--text-sm); color: var(--text-3); margin-top: 1px; }
  .meal-stats.empty { color: var(--text-4); font-style: italic; }
  .meal-chevron { width: 16px; height: 16px; color: var(--text-4); transition: transform var(--transition); flex-shrink: 0; }
  .meal-chevron.expanded { transform: rotate(180deg); }

  .meal-entries { padding-left: var(--sp-4); margin-left: var(--sp-4); border-left: 2px solid var(--border); padding-bottom: var(--sp-1.5); }

  .entry-row {
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
    padding: 11px 14px; border-radius: var(--radius-md); background: var(--card);
    border: 1px solid var(--border); margin-bottom: var(--sp-1); transition: all var(--transition);
  }
  .entry-row:hover { background: var(--card-hover); }

  .entry-info { flex: 1; min-width: 0; }
  .entry-name { font-size: var(--text-base); font-weight: 500; color: var(--text-1); display: flex; align-items: center; gap: var(--sp-1.5); flex-wrap: wrap; }
  .entry-badge { font-size: var(--text-xs); font-weight: 600; padding: 1px 6px; border-radius: var(--radius-xs); text-transform: uppercase; letter-spacing: 0.03em; }
  .entry-badge.ai { background: rgba(59,130,246,0.1); color: #3B82F6; }
  .entry-badge.quick { background: var(--card-hover); color: var(--text-4); }
  .entry-serving { font-size: var(--text-sm); color: var(--text-3); margin-top: 1px; }
  .entry-note { font-size: var(--text-sm); color: var(--text-3); font-style: italic; margin-top: 1px; }

  .entry-wrap { margin-bottom: var(--sp-1); }
  .entry-row { cursor: pointer; }
  .entry-actions {
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--sp-2) var(--sp-3); background: var(--surface-secondary);
    border: 1px solid var(--border); border-top: none;
    border-radius: 0 0 var(--radius-md) var(--radius-md);
  }
  .entry-wrap:has(.entry-actions) .entry-row {
    border-radius: var(--radius-md) var(--radius-md) 0 0;
  }
  .entry-qty-control { display: flex; align-items: center; gap: var(--sp-2); }
  .entry-qty-input {
    width: 64px; padding: var(--sp-1.5) var(--sp-2); border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--card); color: var(--text-1);
    font-size: var(--text-md); font-family: var(--mono); text-align: center;
  }
  .entry-qty-input:focus { outline: none; border-color: var(--accent); }
  .entry-qty-unit { font-size: var(--text-sm); color: var(--text-3); }
  .entry-qty-save {
    padding: var(--sp-2) var(--sp-4); border-radius: var(--radius-md);
    background: var(--accent); color: white; border: none;
    font-size: var(--text-sm); font-weight: 600; cursor: pointer; font-family: var(--font);
    transition: opacity var(--transition); white-space: nowrap;
  }
  .entry-qty-save:hover { opacity: 0.9; }
  .entry-delete-btn {
    display: flex; align-items: center; gap: var(--sp-1.5); padding: var(--sp-2) var(--sp-4);
    border-radius: var(--radius-md); background: none; border: 1px solid var(--error);
    color: var(--error); font-size: var(--text-sm); font-weight: 500; cursor: pointer;
    font-family: var(--font); transition: all var(--transition); white-space: nowrap;
  }
  .entry-delete-btn:hover { background: var(--error-dim); }
  .entry-delete-btn svg { width: 14px; height: 14px; }

  .entry-cals { display: flex; align-items: baseline; gap: 3px; flex-shrink: 0; }
  .entry-cal-value { font-size: var(--text-md); font-weight: 600; font-family: var(--mono); color: var(--text-1); }
  .entry-cal-label { font-size: var(--text-xs); color: var(--text-3); }

  .add-food-btn {
    display: flex; align-items: center; gap: var(--sp-1.5);
    padding: 10px 14px; margin-top: var(--sp-1); background: none; border: 1px dashed var(--border);
    border-radius: var(--radius-md); width: 100%;
    font-size: var(--text-sm); font-weight: 500; color: var(--accent); cursor: pointer;
    font-family: var(--font); transition: all var(--transition);
  }
  .add-food-btn:hover { background: var(--accent-bg); border-color: var(--accent); }
  .add-food-btn svg { width: 15px; height: 15px; }

  /* ── Foods nudge ── */
  .foods-nudge { font-size: var(--text-sm); color: var(--text-3); margin-bottom: 14px; }
  .nudge-link { background: none; border: none; color: var(--accent); font-size: var(--text-sm); font-weight: 500; cursor: pointer; font-family: var(--font); padding: 0; transition: opacity var(--transition); }
  .nudge-link:hover { opacity: 0.7; }

  /* ── Shared list styles (Foods + Templates) ── */
  .list-header { display: flex; gap: 10px; align-items: center; margin-bottom: var(--sp-3); }
  .list-count { flex: 1; font-size: var(--text-sm); color: var(--text-3); }
  .search-wrap { flex: 1; position: relative; }
  .search-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: var(--text-4); pointer-events: none; }
  .search-input { width: 100%; padding: 9px 36px 9px 36px; border-radius: var(--radius-md); border: 1px solid var(--border); background: var(--card); color: var(--text-1); font-size: var(--text-sm); font-family: var(--font); transition: all var(--transition); }
  .search-input::placeholder { color: var(--text-4); }
  .search-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
  .search-clear { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; color: var(--text-4); padding: 4px; }
  .search-clear svg { width: 14px; height: 14px; }
  .btn-primary-sm { padding: 8px 14px; border-radius: var(--radius-md); background: var(--accent); color: white; border: none; font-size: var(--text-sm); font-weight: 600; cursor: pointer; font-family: var(--font); transition: opacity var(--transition); white-space: nowrap; flex-shrink: 0; }
  .btn-primary-sm:hover { opacity: 0.9; }

  .list-card {
    background: var(--card); border-radius: var(--radius); border: 1px solid var(--border);
    box-shadow: var(--card-shadow-sm); overflow: hidden;
  }
  .list-row {
    display: flex; align-items: center; gap: 12px;
    padding: 13px 16px; border-bottom: 1px solid var(--border); cursor: pointer;
    transition: background var(--transition);
  }
  .list-row:last-child { border-bottom: none; }
  .list-row:hover { background: var(--card-hover); }
  .list-row:nth-child(even) { background: color-mix(in srgb, var(--surface) 68%, var(--card)); }
  .list-row:nth-child(even):hover { background: var(--card-hover); }
  .list-row-info { flex: 1; min-width: 0; }
  .list-row-name { font-size: var(--text-base); font-weight: 500; color: var(--text-1); display: flex; align-items: center; gap: var(--sp-1.5); }
  .list-row-meta { font-size: var(--text-sm); color: var(--text-3); margin-top: 1px; }
  .list-row-right { display: flex; align-items: center; gap: var(--sp-2); flex-shrink: 0; }
  .list-row-value { font-size: var(--text-sm); font-family: var(--mono); color: var(--text-2); }
  .list-row-chevron { width: 14px; height: 14px; color: var(--text-4); opacity: 0.5; }
  .list-row:hover .list-row-chevron { opacity: 1; }
  .list-row.highlighted { background: var(--accent-bg); border-bottom-color: color-mix(in srgb, var(--accent) 12%, transparent); animation: highlight-pulse 2s ease-out; }
  @keyframes highlight-pulse { 0% { box-shadow: 0 0 0 3px rgba(79,70,229,0.15); } 100% { box-shadow: none; } }
  .fav-icon { width: 13px; height: 13px; color: #F59E0B; flex-shrink: 0; }
  .empty-state { padding: var(--sp-8); text-align: center; color: var(--text-3); font-size: var(--text-base); }

  .list-row-sub { font-size: var(--text-xs); color: var(--text-4); margin-top: 1px; }
  .template-meal-label { text-transform: capitalize; }
  .template-hint { font-size: var(--text-xs); color: var(--accent); margin-top: 3px; font-weight: 500; opacity: 0.75; }

  /* Template-specific */
  .template-badge {
    width: 32px; height: 32px; border-radius: var(--radius-md);
    background: var(--accent-bg); color: var(--accent);
    display: flex; align-items: center; justify-content: center;
    font-size: var(--text-sm); font-weight: 700; flex-shrink: 0;
  }
  .log-btn {
    padding: 5px 12px; border-radius: var(--radius-sm); background: var(--accent-bg);
    color: var(--accent); border: none; font-size: var(--text-sm); font-weight: 600;
    cursor: pointer; font-family: var(--font); transition: all var(--transition);
  }
  .log-btn:hover { background: var(--accent); color: white; }

  /* ── FAB ── */
  .fab {
    position: fixed; bottom: 24px; right: 24px; z-index: 50;
    width: 56px; height: 56px; border-radius: 50%;
    background: var(--accent); color: white; border: none;
    box-shadow: 0 8px 24px rgba(79,70,229,0.3); cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: all var(--transition);
  }
  .fab:hover { transform: scale(1.06); box-shadow: 0 12px 32px rgba(79,70,229,0.4); }
  .fab-icon { width: 24px; height: 24px; transition: transform 200ms ease; }
  .fab.open .fab-icon { transform: rotate(45deg); }

  /* ── FAB bottom sheet ── */
  .fab-overlay { position: fixed; inset: 0; background: var(--overlay); z-index: 45; animation: fadeIn 150ms ease; }
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
  .fab-sheet {
    position: fixed; bottom: 0; left: 0; right: 0; z-index: 48;
    background: var(--surface); border-top-left-radius: 20px; border-top-right-radius: 20px;
    padding: 12px 16px 28px; box-shadow: 0 -8px 32px rgba(0,0,0,0.12);
    animation: slideUp 200ms ease;
  }
  @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
  .fab-sheet-handle { width: 36px; height: 4px; border-radius: 2px; background: var(--border-strong); margin: 0 auto var(--sp-4); }

  .fab-action {
    display: flex; align-items: center; gap: 14px; width: 100%;
    padding: 14px 12px; border-radius: var(--radius); background: none; border: none;
    cursor: pointer; font-family: var(--font); text-align: left;
    transition: background var(--transition);
  }
  .fab-action:hover { background: var(--card-hover); }

  .fab-action-icon {
    width: 40px; height: 40px; border-radius: 10px;
    background: var(--card); border: 1px solid var(--border);
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0; color: var(--text-3);
  }
  .fab-action-icon.accent { background: var(--accent); border-color: var(--accent); color: white; }
  .fab-action-icon svg { width: 18px; height: 18px; }

  .fab-action-text { flex: 1; min-width: 0; }
  .fab-action-name { font-size: var(--text-md); font-weight: 500; color: var(--text-1); }
  .fab-action-desc { font-size: var(--text-sm); color: var(--text-3); margin-top: 1px; }

  /* ── AI Resolve bar ── */
  .resolve-bar {
    display: flex; gap: var(--sp-2); margin-bottom: var(--sp-4);
  }
  .resolve-input {
    flex: 1; padding: 10px 14px; border-radius: var(--radius); border: 1.5px solid var(--border);
    background: var(--card); color: var(--text-1); font-size: var(--text-md); font-family: var(--font);
    transition: all var(--transition);
  }
  .resolve-input::placeholder { color: var(--text-4); }
  .resolve-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
  .resolve-submit {
    width: 42px; height: 42px; border-radius: var(--radius); background: var(--accent); color: white;
    border: none; display: flex; align-items: center; justify-content: center; cursor: pointer;
    transition: opacity var(--transition); flex-shrink: 0;
  }
  .resolve-submit:disabled { opacity: 0.4; cursor: default; }
  .resolve-submit svg { width: 18px; height: 18px; }
  .resolve-spinner { width: 16px; height: 16px; border: 2px solid rgba(255,255,255,0.3); border-top-color: white; border-radius: 50%; animation: spin 0.6s linear infinite; }
  @keyframes spin { to { transform: rotate(360deg); } }
  .resolve-error { font-size: var(--text-sm); color: var(--error); margin-top: calc(-1 * var(--sp-3)); margin-bottom: var(--sp-3); }

  /* ── Resolve modal ── */
  .resolve-overlay {
    position: fixed; inset: 0; background: var(--overlay); z-index: 70;
    display: flex; align-items: center; justify-content: center; animation: resolveFade 150ms ease;
  }
  @keyframes resolveFade { from { opacity: 0; } to { opacity: 1; } }
  .resolve-modal {
    width: 420px; max-width: 92vw; background: var(--surface); border-radius: var(--radius);
    box-shadow: var(--shadow-xl); animation: resolveSlide 200ms ease;
  }
  @keyframes resolveSlide { from { transform: translateY(12px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
  .resolve-modal-header {
    display: flex; align-items: center; justify-content: space-between; padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border);
  }
  .resolve-modal-title { font-size: var(--text-md); font-weight: 600; color: var(--text-1); }
  .resolve-modal-close { background: none; border: none; cursor: pointer; color: var(--text-3); padding: var(--sp-1); border-radius: var(--radius-sm); }
  .resolve-modal-close:hover { color: var(--text-1); background: var(--card-hover); }
  .resolve-modal-close svg { width: 18px; height: 18px; }
  .resolve-modal-body { padding: var(--sp-5); }
  .resolve-item { padding-bottom: var(--sp-3); }
  .resolve-item-border { padding-top: var(--sp-3); border-top: 1px solid var(--border); }
  .resolve-item-top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-2); }
  .resolve-item-info { flex: 1; min-width: 0; }
  .resolve-item-remove { background: none; border: none; cursor: pointer; color: var(--text-4); padding: var(--sp-1); border-radius: var(--radius-xs); flex-shrink: 0; }
  .resolve-item-remove:hover { color: var(--error); background: var(--error-dim); }
  .resolve-item-remove svg { width: 14px; height: 14px; }
  .resolve-food-name { font-size: var(--text-base); font-weight: 600; color: var(--text-1); line-height: 1.3; }
  .resolve-food-detail { font-size: var(--text-sm); color: var(--text-3); margin-top: 2px; font-family: var(--mono); }
  .resolve-qty-row {
    display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-2);
  }
  .resolve-qty-btn {
    width: 28px; height: 28px; border-radius: var(--radius-sm); border: 1px solid var(--border);
    background: var(--card); color: var(--text-2); font-size: var(--text-md); font-weight: 600;
    display: flex; align-items: center; justify-content: center; cursor: pointer;
    transition: all var(--transition);
  }
  .resolve-qty-btn:hover { background: var(--card-hover); color: var(--text-1); }
  .resolve-qty-btn:disabled { opacity: 0.3; cursor: default; }
  .resolve-qty-value { font-size: var(--text-sm); font-family: var(--mono); color: var(--text-2); min-width: 60px; text-align: center; }
  .resolve-meal-row {
    display: flex; align-items: center; justify-content: space-between; margin-top: var(--sp-4);
    padding-top: var(--sp-3); border-top: 1px solid var(--border);
  }
  .resolve-meal-label { font-size: var(--text-sm); color: var(--text-3); }
  .resolve-meal-select {
    padding: var(--sp-1.5) var(--sp-3); border-radius: var(--radius-md); border: 1px solid var(--border);
    background: var(--card); color: var(--text-1); font-size: var(--text-sm); font-family: var(--font); cursor: pointer;
  }
  .resolve-note { font-size: var(--text-sm); color: var(--text-3); font-style: italic; margin-top: var(--sp-3); }
  /* ── Food edit modal ── */
  .food-edit-field { display: flex; flex-direction: column; gap: var(--sp-1); }
  .food-edit-label { font-size: var(--text-sm); font-weight: 500; color: var(--text-3); }
  .food-edit-input {
    padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-md); border: 1px solid var(--border);
    background: var(--surface-secondary); color: var(--text-1); font-size: var(--text-md); font-family: var(--font);
  }
  .food-edit-input:focus { outline: none; border-color: var(--accent); }
  .food-edit-input[type="number"] { font-family: var(--mono); }
  .food-edit-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); margin-top: var(--sp-3); }
  .food-edit-field:first-child:not(:last-child) { margin-bottom: 0; }
  .food-edit-unit { font-size: var(--text-sm); color: var(--text-4); margin-top: var(--sp-3); font-style: italic; }

  .resolve-modal-footer {
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--sp-3) var(--sp-5); border-top: 1px solid var(--border);
  }
  .resolve-total { font-size: var(--text-sm); font-weight: 600; font-family: var(--mono); color: var(--text-1); }
  .resolve-footer-actions { display: flex; gap: var(--sp-2); }

  /* ── Mobile ── */
  @media (max-width: 768px) {
    .page-date { font-size: var(--text-xl); }
    .calorie-number { font-size: var(--text-3xl); }
    .calorie-card { padding: 20px 16px; }
    .macro-row { gap: var(--sp-2); }
    .macro-item { padding: var(--sp-3); }
    .macro-current { font-size: var(--text-base); }
    .meal-entries { padding-left: var(--sp-3); margin-left: var(--sp-3); }
    .entry-row { padding: 10px 12px; }
    .top-tab { font-size: var(--text-sm); }
    .list-header { flex-direction: column; }
    .fab { bottom: 80px; right: 16px; width: 52px; height: 52px; }
    .fab svg { width: 22px; height: 22px; }
  }
</style>
