// Pau Fit — Tab screens (Home, Retos, Recetas, Comunidad, Perfil)
// SWEAT-inspired rewrite — photo-forward, bold sans, energetic

const { useState, useRef, useEffect } = React;

function timeShort() {
  const d = new Date();
  return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
}

// ═════════════════════════════════════════════════════════════
// HOME — SWEAT-style hero + program tiles + everything else
// ═════════════════════════════════════════════════════════════
function HomeScreen({ theme, isPremium, nav }) {
  const T = theme;
  // Comidas REALES del día desde Supabase meal_logs (no mock)
  const [meals, setMeals] = useState([]);
  // Bump para re-render cuando cache cambia (después de completar día, unirse a reto, etc)
  const [, setCacheBump] = useState(0);

  useEffect(() => {
    const handler = () => setCacheBump(x => x + 1);
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);

  // Cargar comidas reales del día
  const [, setMealsBump] = useState(0);
  useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) return;
    async function loadMeals() {
      const { data: { user } } = await sb.auth.getUser();
      if (!user) return;
      const today = new Date();
      today.setHours(0, 0, 0, 0);
      const { data } = await sb.from('meal_logs')
        .select('id, meal_name, meal_type, logged_at, photo_url')
        .eq('user_id', user.id)
        .gte('logged_at', today.toISOString())
        .order('logged_at', { ascending: true });
      const mapped = (data || []).map(m => ({
        id: m.id,
        meal: m.meal_type || 'comida',
        text: m.meal_name,
        time: new Date(m.logged_at).toLocaleTimeString('es', { hour: '2-digit', minute: '2-digit' }),
        photo: !!m.photo_url,
      }));
      setMeals(mapped);
    }
    loadMeals();
    // Recargar cuando se añade/quita comida en otra parte de la app
    const handler = () => loadMeals();
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);

  async function deleteMeal(id) {
    if (!id) return;
    if (!window.confirm('¿Borrar esta comida del registro?')) return;
    const sb = window.__PAU_SUPABASE;
    if (!sb) return;
    try {
      const { data: { user } } = await sb.auth.getUser();
      if (!user) { nav.toast('Inicia sesión', { icon: '🔒' }); return; }
      const { error } = await sb.from('meal_logs')
        .delete()
        .eq('id', id)
        .eq('user_id', user.id); // doble seguridad + ayuda a RLS
      if (error) {
        console.warn('[delete-meal]', error);
        console.warn('[delete-meal]', error.message); nav.toast('No se pudo borrar', { icon: '⚠️' });
        return;
      }
      setMeals(arr => arr.filter(m => m.id !== id));
      nav.toast('Borrada', { icon: '🗑️' });
      window.dispatchEvent(new CustomEvent('pau:cache-updated'));
    } catch (e) {
      console.warn('[delete-meal] exception', e);
      nav.toast('No se pudo borrar', { icon: '⚠️' });
    }
  }

  // Editar comida registrada — abre AddMealModal con prefill editId
  function editMeal(m) {
    if (!m?.id) return;
    if (nav?.openModal && window.AddMealModal) {
      nav.openModal(React.createElement(window.AddMealModal, {
        open: true, T, accent: T.accent,
        prefill: {
          editId: m.id,
          name: m.text || m.meal_name || '',
          mealType: m.meal || m.meal_type || 'snack',
          imageUrl: m.photoUrl || null,
        },
        onClose: () => nav.closeModal(),
        onSave: () => {
          nav.closeModal();
          nav.toast('Comida actualizada 💗', { icon: '✓' });
          window.dispatchEvent(new CustomEvent('pau:cache-updated'));
        },
      }));
    }
  }

  // ── DATOS REALES desde Supabase (cargados en app.jsx en window.__pauCache) ──
  const cache = (typeof window !== 'undefined' && window.__pauCache) || {};
  const profile = cache.profile || {};
  const stats = cache.stats || { totalDays: 0, retosCompleted: 0, streak: 0 };
  const points = profile.points || 0;

  // Nombre real (no STATS.name mock)
  const fullName = profile.full_name || profile.username || '';
  const firstName = fullName ? fullName.split(' ')[0] : '';
  const initials = fullName
    ? fullName.split(' ').map(s => s[0]).slice(0,2).join('').toUpperCase()
    : (profile.email ? profile.email[0].toUpperCase() : '?');

  // Reto activo real (del cache de Supabase)
  const activeUserReto = cache.activeReto;
  const active = activeUserReto ? window.pauRetoById(activeUserReto.reto_id) : null;
  const completedCount = activeUserReto ? (activeUserReto.completed_days || []).length : 0;
  const todayProgress = active ? completedCount / active.days : 0;

  function openAddMeal() {
    nav.openModal(<AddMealModal
      open T={T} accent={T.accent}
      onClose={() => nav.closeModal()}
      onSave={(m) => {
        // Refrescar comidas reales tras guardar
        setMeals(arr => [...arr, { meal: m.meal, text: m.text, time: m.time || timeShort(), photo: m.photo }]);
        nav.toast('Comida anotada 💗', { icon: '🍓' });
      }}
    />);
  }

  return (
    <div style={{ paddingBottom: 120 }}>
      {/* ─── TOP BAR estilo Silbe: puntos | logo centrado | avatar ─── */}
      {/* TopBar Home: ⭐ puntos izq · logo centro · avatar der · sticky con glass al scroll */}
      <PauTopBar T={T} nav={nav} variant="home"/>

      {/* ─── SALUDO cercano con emoji rotativo del día (sin piropos) ─── */}
      <div style={{ padding: '20px 20px 0' }}>
        <div className="pf-display" style={{
          fontSize: 38, fontWeight: 800, color: T.text, lineHeight: 0.95, letterSpacing: -1.4,
        }}>
          {(() => {
            const EMOJIS = ['💕','💖','💗','💞','✨','💓','💘','🩷'];
            const d = new Date();
            const seed = d.getFullYear() * 366 + d.getMonth() * 31 + d.getDate();
            const e = EMOJIS[seed % EMOJIS.length];
            return firstName
              ? `Hola, ${firstName} ${e}`
              : `Hola ${e}`;
          })()}
        </div>
      </div>

      {/* ─── CALENDARIO SEMANAL pills estilo Silbe ─── */}
      <SilbeWeekPills T={T}/>

      {/* HERO — programa activo manda; si no, reto activo; si no, descubrir */}
      {cache.activePrograma
        ? <ProgramaTodayHero T={T} nav={nav}/>
        : active
          ? <TodayHero active={active} userReto={activeUserReto} T={T} progress={todayProgress} nav={nav}/>
          : <DiscoverHero T={T} isPremium={isPremium} nav={nav}/>
      }

      {/* Acceso rápido — SVG en el cromo (DISEÑO.md §4), cards limpias */}
      <div style={{ margin: '32px 20px 0', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10 }}>
        {[
          { icon: () => Icon.camera(T.text, 22), label: 'Progreso', go: () => nav.go('progreso') },
          { icon: () => Icon.calendarDays(T.text, 22), label: 'Calendario', go: () => nav.go('calendario', active?.id) },
          { icon: () => Icon.trophy(T.text, 22), label: 'Logros', go: () => nav.go('logros') },
        ].map((q, i) => (
          <button key={i} onClick={q.go} style={{
            padding: '20px 10px', border: 'none', borderRadius: 20, cursor: 'pointer',
            background: T.card, color: T.text,
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8,
            fontFamily: 'inherit',
          }}>
            {q.icon()}
            <span className="pf-eyebrow" style={{ color: T.text, fontSize: 9 }}>{q.label}</span>
          </button>
        ))}
      </div>

      {/* Reto del mes — solo cuando NO hay reto ni programa activo:
          con algo en curso, el héroe ya cumple ese rol (evita doble héroe) */}
      {!active && !cache.activePrograma && <RetoDelMesBanner T={T} nav={nav} activeRetoId={active?.id}/>}

      {/* Restaurar racha — solo si racha === 0 y hay reto activo */}
      {active && (stats.streak === 0) && <RestaurarRachaBanner T={T} nav={nav}/>}

      {/* Recetas */}
      <SectionHeader title="Comer hoy" action="Ver recetas" onAction={() => nav.tab('recetas')} T={T}/>
      <RecetaFeatured T={T} nav={nav} meals={meals} onAddMeal={openAddMeal} onDeleteMeal={deleteMeal} onEditMeal={editMeal}/>

      {/* Premium — fila discreta (el paywall ya vende al abrirse; DISEÑO.md: 1 CTA primario por pantalla) */}
      {!isPremium && (
        <div style={{ margin: '32px 20px 0' }}>
          <button onClick={() => nav.go('paywall')} style={{
            width: '100%', padding: '18px 20px', cursor: 'pointer',
            border: T.border, borderRadius: 20,
            background: T.card, color: T.text, textAlign: 'left',
            display: 'flex', alignItems: 'center', gap: 12, fontFamily: 'inherit',
          }}>
            <span style={{ color: T.accent, fontSize: 12 }}>●</span>
            <span style={{ flex: 1, fontSize: 13.5, fontWeight: 700, letterSpacing: -0.2 }}>
              Premium — 7 días gratis
            </span>
            {Icon.chevR(T.muted, 9)}
          </button>
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// TodayHero — full-bleed photo, big workout, CTA
// Si ya completó el día de hoy → mensaje "Día X completado · nos vemos mañana"
// ─────────────────────────────────────────────────────────────
function TodayHero({ active, userReto, T, progress, nav }) {
  // Datos REALES del progreso del usuario (no del mock RETOS)
  const currentDay = (userReto && userReto.current_day) || 1;
  const completedDays = (userReto && userReto.completed_days) || [];
  const lastDoneDay = completedDays.length > 0 ? Math.max(...completedDays) : 0;

  // ── LÓGICA DESBLOQUEO A LAS 12am LOCAL ──
  // localStorage guarda la fecha del último día completado: YYYY-MM-DD
  // Si esa fecha === hoy → ya hiciste tu día, vuelve mañana
  // Si esa fecha < hoy → puedes hacer el siguiente día
  const todayStr = new Date().toLocaleDateString('en-CA'); // 2026-05-29
  let lastDoneLocalDate = null;
  try { lastDoneLocalDate = localStorage.getItem(`paufit-last-done-${active.id}`); } catch {}
  const doneToday = lastDoneLocalDate === todayStr;

  const retoCompleted = completedDays.length >= active.days;
  const todayCompleted = doneToday || retoCompleted;
  const nextDay = lastDoneDay + 1;
  const isLastDay = currentDay >= active.days;

  // Sin vídeos inventados: mostramos datos REALES del reto
  const totalMin = active.minPerDay || 20;
  const usePhoto = !!active.image;

  return (
    <div onClick={() => nav.go('reto', active.id)} style={{
      margin: '16px 20px 0', cursor: 'pointer',
      borderRadius: 28, position: 'relative', overflow: 'hidden',
      aspectRatio: '3 / 4',
      background: usePhoto ? '#000' : `linear-gradient(160deg, ${T.accent}, ${T.deep})`,
      boxShadow: `0 16px 36px rgba(0,0,0,0.15), 0 6px 16px rgba(0,0,0,0.08)`,
    }}>
      {usePhoto && (
        <img loading="lazy" src={active.image} alt={active.title}
          style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}/>
      )}

      {/* Top gradient overlay for chrome */}
      <div style={{
        position: 'absolute', inset: 0,
        background: 'linear-gradient(180deg, rgba(0,0,0,0.4) 0%, transparent 30%, transparent 55%, rgba(0,0,0,0.85) 100%)',
      }}/>

      {/* Top row */}
      <div style={{
        position: 'absolute', top: 14, left: 14, right: 14,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        {/* Racha REAL en días — arriba a la izquierda con explicación clara */}
        <div style={{
          padding: '6px 12px', borderRadius: 999,
          background: 'rgba(0,0,0,0.55)', backdropFilter: 'blur(10px)',
          color: '#fff', fontSize: 11, fontWeight: 700,
          display: 'inline-flex', alignItems: 'center', gap: 6,
        }}>
          <span style={{ fontSize: 13 }}>🔥</span>
          {(window.__pauCache?.stats?.streak || 0)} {(window.__pauCache?.stats?.streak === 1) ? 'día' : 'días'} de racha
        </div>

        <div style={{
          padding: '5px 10px', borderRadius: 6,
          background: 'rgba(255,255,255,0.95)', backdropFilter: 'blur(10px)',
          color: '#0E0A0C', fontSize: 9, fontWeight: 700, letterSpacing: 1.2,
        }}>
          {retoCompleted ? `RETO COMPLETADO 💗` : (todayCompleted ? `DÍA ${lastDoneDay} ✓` : `HOY · DÍA ${currentDay}/${active.days}`)}
        </div>
      </div>

      {/* Bottom — workout info */}
      <div style={{
        position: 'absolute', bottom: 16, left: 18, right: 18,
        color: '#fff',
      }}>
        <div className="pf-eyebrow" style={{ color: 'rgba(255,255,255,0.85)' }}>
          {active.title}
        </div>

        {/* ── CASO 1: reto COMPLETO ── */}
        {retoCompleted ? (
          <>
            <div className="pf-display" style={{
              fontSize: 26, fontWeight: 500, color: '#fff', marginTop: 4,
              letterSpacing: -0.5, lineHeight: 1.1,
              textShadow: '0 2px 12px rgba(0,0,0,0.4)',
            }}>
              ¡Reto completado! 💗
            </div>
            <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.9)', marginTop: 6 }}>
              Has terminado los {active.days} días.
            </div>
          </>
        ) : todayCompleted ? (
          /* ── CASO 2: día de HOY ya completado ── */
          <>
            <div className="pf-display" style={{
              fontSize: 24, fontWeight: 500, color: '#fff', marginTop: 4,
              letterSpacing: -0.5, lineHeight: 1.15,
              textShadow: '0 2px 12px rgba(0,0,0,0.4)',
            }}>
              Día {lastDoneDay} completado ✓
            </div>
            <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.9)', marginTop: 6 }}>
              {isLastDay ? '¡Mañana terminas el reto! 💗' : `Mañana toca el día ${nextDay} 💗`}
            </div>
            {!isLastDay && (
              <button onClick={(e) => { e.stopPropagation(); nav.go('reto', active.id); }} style={{
                marginTop: 14, width: '100%', padding: '14px',
                border: '1.5px solid rgba(255,255,255,0.45)', borderRadius: 999, cursor: 'pointer',
                background: 'rgba(255,255,255,0.12)', color: '#fff', backdropFilter: 'blur(8px)',
                fontSize: 13, fontWeight: 600, letterSpacing: 0.2,
              }}>Adelantar día {nextDay} →</button>
            )}
          </>
        ) : (
          /* ── CASO 3: día pendiente — entreno normal ── */
          <>
            <div className="pf-display" style={{
              fontSize: 30, fontWeight: 500, color: '#fff', marginTop: 4,
              letterSpacing: -0.6, lineHeight: 1.05,
              textShadow: '0 2px 12px rgba(0,0,0,0.4)',
            }}>
              Tu entreno del día {currentDay}
            </div>

            <div style={{
              marginTop: 10, display: 'flex', alignItems: 'center', gap: 14,
              fontSize: 11, fontWeight: 500, color: 'rgba(255,255,255,0.95)',
              whiteSpace: 'nowrap',
            }}>
              <span style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
                {Icon.clock({ c: '#fff', size: 13 })} ~{totalMin} min
              </span>
              <span style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
                {Icon.fire('#fff', 13)} {active.intensity}
              </span>
            </div>

            <button onClick={(e) => { e.stopPropagation(); nav.go('reto', active.id); }} style={{
              marginTop: 14, width: '100%', padding: '14px',
              border: 'none', borderRadius: 999, cursor: 'pointer',
              background: '#fff', color: '#0E0A0C',
              fontSize: 13, fontWeight: 600, letterSpacing: 0.2,
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
            }}>
              {Icon.playFilled({ c: '#0E0A0C', size: 11 })} Empezar entreno
            </button>
          </>
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// DiscoverHero — when user hasn't joined any reto yet
// ─────────────────────────────────────────────────────────────
function DiscoverHero({ T, isPremium, nav }) {
  const featured = window.pauRetos()[0];
  return (
    <div onClick={() => nav.go('reto', featured.id)} style={{
      margin: '16px 20px 0', cursor: 'pointer',
      borderRadius: 28, position: 'relative', overflow: 'hidden',
      aspectRatio: '3 / 4',
      background: 'rgba(0,0,0,0.04)',
      boxShadow: '0 16px 36px rgba(0,0,0,0.15)',
    }}>
      {featured.image && (
        <img loading="lazy" src={featured.image} alt={featured.title}
          style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}/>
      )}
      <div style={{
        position: 'absolute', inset: 0,
        background: 'linear-gradient(180deg, transparent 35%, rgba(0,0,0,0.75) 100%)',
      }}/>

      <div style={{
        position: 'absolute', bottom: 18, left: 18, right: 18, color: '#fff',
      }}>
        <span className="pf-eyebrow" style={{ color: 'rgba(255,255,255,0.9)' }}>
          Empieza tu primer reto
        </span>
        <div className="pf-display-bold" style={{
          fontSize: 28, color: '#fff', marginTop: 8,
          letterSpacing: -0.8, lineHeight: 0.95, textTransform: 'none',
          textShadow: '0 2px 12px rgba(0,0,0,0.4)',
        }}>
          {featured.title}
        </div>
        <button onClick={(e) => { e.stopPropagation(); nav.go('reto', featured.id); }} style={{
          marginTop: 18, padding: '12px 18px',
          border: 'none', borderRadius: 999, cursor: 'pointer',
          background: '#fff', color: T.text,
          fontSize: 14, fontWeight: 700, fontFamily: 'inherit',
        }}>Empezar</button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// RetoDelMesBanner — destacado del mes (rota cada mes)
// Selecciona deterministicamente un reto basado en (año*12 + mes)
// Si la usuaria ya está en este reto, lo muestra como "Tu reto del mes"
// ─────────────────────────────────────────────────────────────
function RetoDelMesBanner({ T, nav, activeRetoId }) {
  if (!RETOS || RETOS.length === 0) return null;
  const now = new Date();
  const mesIdx = now.getFullYear() * 12 + now.getMonth();
  const _retosCat = window.pauRetos();
  const reto = _retosCat[mesIdx % _retosCat.length];
  const meses = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre'];
  const mesNombre = meses[now.getMonth()];
  const yaEnReto = activeRetoId === reto.id;
  return (
    <div style={{ margin: '32px 20px 0' }}>
      <div className="pf-display-bold" style={{
        fontSize: 18, color: T.text, marginBottom: 14, lineHeight: 1,
      }}>
        Reto de {mesNombre}
      </div>
      <div onClick={() => nav.go('reto', reto.id)} style={{
        position: 'relative', cursor: 'pointer',
        borderRadius: 24, overflow: 'hidden',
        aspectRatio: '16 / 10',
        background: 'rgba(0,0,0,0.04)',
      }}>
        {reto.image
          ? <img loading="lazy" src={reto.image} alt={reto.title}
              style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}/>
          : <div style={{ position: 'absolute', inset: 0, background: `linear-gradient(135deg, ${reto.color}, ${reto.bg})` }}/>
        }
        <div style={{
          position: 'absolute', inset: 0,
          background: 'linear-gradient(180deg, transparent 40%, rgba(0,0,0,0.7) 100%)',
        }}/>
        {yaEnReto && (
          <div style={{
            position: 'absolute', top: 12, left: 12,
            padding: '5px 10px', borderRadius: 999,
            background: '#fff', color: T.text,
            fontSize: 9, fontWeight: 800, letterSpacing: 0.8,
          }}>EN CURSO ✓</div>
        )}
        <div style={{
          position: 'absolute', bottom: 14, left: 14, right: 14, color: '#fff',
        }}>
          <div className="pf-display-bold" style={{
            fontSize: 22, color: '#fff', letterSpacing: -0.6, lineHeight: 0.95,
            textTransform: 'none', textShadow: '0 2px 10px rgba(0,0,0,0.4)',
          }}>{reto.title}</div>
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// RestaurarRachaBanner — aparece si la racha rompió
// 3 restauraciones gratis al mes (estilo Snapchat/TikTok)
// Si gastas las 3, te dice cuándo se renuevan
// ─────────────────────────────────────────────────────────────
function RestaurarRachaBanner({ T, nav }) {
  const cache = (typeof window !== 'undefined' && window.__pauCache) || {};
  const MAX_PER_MONTH = 3;
  const RESTORE_WINDOW_HOURS = 72; // ventana Snapchat: 72h después de romperse

  const now = new Date();
  const mesKey = `paufit-restores-${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
  let usadas = 0;
  try {
    const stored = JSON.parse(localStorage.getItem(mesKey) || '[]');
    if (Array.isArray(stored)) usadas = stored.length;
  } catch {}
  const restantes = Math.max(0, MAX_PER_MONTH - usadas);

  // Detectar la última day_completion y calcular cuánto queda de ventana
  const [windowState, setWindowState] = useState(null);
  // null = loading, { hoursLeft, lastDay, lastAt } o { expired: true }
  useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    const userReto = cache.activeReto;
    if (!sb || !userReto) return;
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) return;
        const { data: last } = await sb.from('day_completions')
          .select('day_number, completed_at')
          .eq('user_id', user.id).eq('reto_id', userReto.reto_id)
          .order('completed_at', { ascending: false })
          .limit(1).maybeSingle();
        if (!last) { setWindowState({ expired: true }); return; }
        const lastAt = new Date(last.completed_at);
        const hoursSince = (Date.now() - lastAt.getTime()) / 3600000;
        const hoursLeft = Math.max(0, RESTORE_WINDOW_HOURS - hoursSince);
        if (hoursLeft <= 0) { setWindowState({ expired: true }); return; }
        setWindowState({ hoursLeft, lastDay: last.day_number, lastAt });
      } catch {}
    })();
  }, []);

  // Si todavía está cargando, no mostramos nada (evita flash)
  if (windowState === null) return null;
  // Si pasó la ventana de 48h, no se puede restaurar — ocultar banner
  if (windowState.expired) return null;

  async function restaurar() {
    if (restantes <= 0) {
      const next = new Date(now.getFullYear(), now.getMonth() + 1, 1);
      const daysToWait = Math.ceil((next - now) / 86400000);
      nav.toast(`Se renuevan en ${daysToWait} día${daysToWait === 1 ? '' : 's'} (1 de cada mes)`, { icon: '🕐' });
      return;
    }
    const nextDayNum = (windowState.lastDay || 0) + 1;
    if (!window.confirm(`¿Restaurar tu racha?\nContinuarás con el día ${nextDayNum} HOY.\nTe quedan ${restantes} de ${MAX_PER_MONTH} restauraciones este mes.`)) return;
    try {
      const sb = window.__PAU_SUPABASE;
      const userReto = cache.activeReto;
      if (!sb || !userReto) { nav.toast('Sin reto activo', { icon: '⚠️' }); return; }
      const { data: { user } } = await sb.auth.getUser();
      if (!user) { nav.toast('Inicia sesión', { icon: '🔒' }); return; }
      // Crear day_completion para HOY con day_number = lastDay + 1 (estilo Snapchat)
      // Esto extiende la racha sin "rellenar" el hueco que se perdió.
      await sb.from('day_completions').insert({
        user_id: user.id,
        user_reto_id: userReto.id,
        reto_id: userReto.reto_id,
        day_number: nextDayNum,
        completed_at: new Date().toISOString(),
        source: 'streak-restore',
      });
      try {
        const stored = JSON.parse(localStorage.getItem(mesKey) || '[]');
        stored.push(new Date().toISOString());
        localStorage.setItem(mesKey, JSON.stringify(stored));
      } catch {}
      if (window.PauUserRetos?.getStats) {
        const s = await window.PauUserRetos.getStats();
        if (s) { window.__pauCache.stats = s; }
      }
      window.dispatchEvent(new CustomEvent('pau:cache-updated'));
      const newRestantes = restantes - 1;
      nav.toast(`Racha restaurada · día ${nextDayNum} hecho 🔥`, { icon: '✓' });
    } catch (e) {
      nav.toast('Error: ' + (e?.message || e), { icon: '⚠️' });
    }
  }

  // Texto del countdown: horas restantes
  const hoursLeftRound = Math.ceil(windowState.hoursLeft);
  const subtitle = restantes > 0
    ? `Te quedan ${hoursLeftRound}h · ${restantes} de ${MAX_PER_MONTH} disponibles`
    : 'Se renuevan el día 1 del próximo mes';

  return (
    <div style={{ margin: '20px 20px 0' }}>
      <button onClick={restaurar} disabled={restantes <= 0} style={{
        width: '100%', padding: '16px 18px',
        border: `1.5px solid ${restantes > 0 ? T.accent + '40' : T.muted + '30'}`, borderRadius: 20,
        cursor: restantes > 0 ? 'pointer' : 'default',
        background: T.dark ? 'rgba(217,71,126,0.10)' : `${T.accent}10`,
        color: T.text, textAlign: 'left',
        display: 'flex', alignItems: 'center', gap: 14, fontFamily: 'inherit',
        opacity: restantes > 0 ? 1 : 0.6,
      }}>
        <div style={{
          width: 44, height: 44, borderRadius: 14,
          background: T.accent + '20',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 22,
        }}>🔥</div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13, fontWeight: 700, color: T.text }}>
            {restantes > 0 ? `Sigue con día ${(windowState.lastDay || 0) + 1}` : 'Sin restauraciones este mes'}
          </div>
          <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>{subtitle}</div>
        </div>
        {restantes > 0 && (
          <span style={{
            padding: '6px 12px', borderRadius: 999,
            background: T.accent, color: '#fff',
            fontSize: 11, fontWeight: 700,
          }}>Restaurar</span>
        )}
      </button>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// WeekStrip — current week with completed days
// ─────────────────────────────────────────────────────────────
function WeekStrip({ T, nav }) {
  if (!WEEK_PLAN || WEEK_PLAN.length === 0) return null;
  return (
    <div style={{
      margin: '16px 20px 0', padding: '14px 12px',
      background: T.card, borderRadius: 20, border: T.border,
    }}>
      <div style={{
        display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
        padding: '0 4px 10px',
      }}>
        <span className="pf-eyebrow" style={{ color: T.muted }}>Semana 3</span>
        <span style={{ fontSize: 11, color: T.text, fontWeight: 700 }}>
          5/7 <span style={{ color: T.muted, fontWeight: 500 }}>· racha {STATS.streak}🔥</span>
        </span>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 6 }}>
        {WEEK_PLAN.map((d, i) => {
          const isToday = d.status === 'today';
          const isDone = d.status === 'done';
          return (
            <button key={i} onClick={() => nav.toast(`${d.title} · ${d.minutes} min`, { icon: d.emoji })} style={{
              padding: '10px 0 8px', cursor: 'pointer',
              border: isToday ? `1.5px solid ${T.accent}` : 'none',
              background: isToday ? T.accent : (isDone ? T.text : T.subtle),
              color: isToday || isDone ? '#fff' : T.text,
              borderRadius: 14,
              display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
              position: 'relative',
            }}>
              <span style={{ fontSize: 9, fontWeight: 700, opacity: 0.8, letterSpacing: 0.4 }}>{d.day}</span>
              <span style={{ fontSize: 14, fontWeight: 800, letterSpacing: -0.3 }}>{d.date}</span>
              {isDone && (
                <span style={{
                  width: 4, height: 4, borderRadius: 999, background: T.accent,
                  marginTop: 1,
                }}/>
              )}
            </button>
          );
        })}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// QuickStats — Apple Health rings + steps + sleep
// ─────────────────────────────────────────────────────────────
function QuickStats({ T, nav }) {
  if (!HEALTH || !HEALTH.connected) return null;
  const h = HEALTH;
  return (
    <div onClick={() => nav.go('health')} style={{
      margin: '12px 18px 0', padding: '16px 20px',
      background: T.card, borderRadius: 20, border: T.border,
      cursor: 'pointer',
      display: 'flex', alignItems: 'center', gap: 14,
    }}>
      <ThreeRings
        move={h.activeKcal/h.activeKcalGoal}
        exercise={h.exerciseMin/h.exerciseGoal}
        stand={h.steps/h.stepsGoal}
        size={56}/>
      <div style={{ flex: 1, minWidth: 0 }}>
        <span className="pf-eyebrow" style={{ color: T.muted }}>Apple Health</span>
        <div style={{ fontSize: 13, fontWeight: 700, color: T.text, marginTop: 3, letterSpacing: -0.2 }}>
          {h.steps.toLocaleString('es-ES')} pasos · {h.activeKcal} kcal
        </div>
        <div style={{ fontSize: 11, color: T.muted, marginTop: 1, fontWeight: 500 }}>
          {h.exerciseMin}/{h.exerciseGoal} min ejercicio · {h.heartAvg} bpm
        </div>
      </div>
      {Icon.chevR(T.muted, 9)}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// ProgramsRail — horizontal scroll of program tiles (16:9 photos)
// ─────────────────────────────────────────────────────────────
function ProgramsRail({ T, isPremium, nav }) {
  return (
    <div style={{
      display: 'flex', gap: 12, padding: '4px 22px 4px',
      overflowX: 'auto',     }} className="hide-scroll">
      {RETOS.map(r => {
        const locked = r.tag === 'PRO' && !isPremium;
        const joined = nav.joinedRetos.has(r.id);
        return (
          <div key={r.id} onClick={() => nav.go(locked ? 'paywall' : 'reto', r.id)} style={{
            flexShrink: 0, width: 260, cursor: 'pointer',             borderRadius: 20, overflow: 'hidden',
            background: T.card, border: T.border,
            position: 'relative',
          }}>
            <div style={{
              position: 'relative', aspectRatio: '3 / 4',
              background: 'rgba(0,0,0,0.04)',
            }}>
              {r.image
                ? <img loading="lazy" src={r.image} alt={r.title} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}/>
                : <div style={{ position: 'absolute', inset: 0, background: `linear-gradient(135deg, ${r.color}, ${r.bg})` }}/>
              }
              {/* subtle bottom shadow only when no image */}
              {!r.image && <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, transparent 50%, rgba(0,0,0,0.5) 100%)' }}/>}
              {/* Tag */}
              <div style={{
                position: 'absolute', top: 10, left: 10,
                padding: '4px 8px', borderRadius: 5,
                background: r.tag === 'GRATIS' ? 'rgba(255,255,255,0.95)' : 'rgba(14,10,12,0.85)',
                backdropFilter: 'blur(8px)',
                color: r.tag === 'GRATIS' ? '#0E0A0C' : '#fff',
                fontSize: 9, fontWeight: 700, letterSpacing: 1,
              }}>{r.tag}</div>
              {joined && (
                <div style={{
                  position: 'absolute', top: 10, right: 10,
                  padding: '4px 8px', borderRadius: 999,
                  background: 'rgba(255,255,255,0.95)', color: T.deep,
                  fontSize: 9, fontWeight: 700, letterSpacing: 0.8,
                  display: 'flex', alignItems: 'center', gap: 4,
                }}>✓ Unida</div>
              )}
              {/* Lock */}
              {locked && (
                <div style={{
                  position: 'absolute', top: 10, right: 10,
                  width: 28, height: 28, borderRadius: 999,
                  background: 'rgba(14,10,12,0.7)', backdropFilter: 'blur(10px)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>{Icon.lock('#fff', 12)}</div>
              )}
            </div>
            <div style={{ padding: '12px 14px' }}>
              <div style={{ fontSize: 14, fontWeight: 600, color: T.text, letterSpacing: -0.2, lineHeight: 1.2 }}>{r.title}</div>
              <div style={{
                marginTop: 5, display: 'flex', alignItems: 'center', gap: 8,
                fontSize: 11, color: T.muted, fontWeight: 500, whiteSpace: 'nowrap',
              }}>
                <span>{r.days} días</span>
                <span style={{ width: 3, height: 3, borderRadius: 999, background: T.muted, flexShrink: 0 }}/>
                <span>{r.minPerDay} min/día</span>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// LiveRail — live workouts
// ─────────────────────────────────────────────────────────────
function LiveRail({ T, isPremium, nav }) {
  if (!LIVE_WORKOUTS || LIVE_WORKOUTS.length === 0) return null;
  return (
    <div style={{
      display: 'flex', gap: 12, padding: '4px 22px 4px',
      overflowX: 'auto',     }} className="hide-scroll">
      {LIVE_WORKOUTS.map(l => {
        const locked = !isPremium;
        return (
          <div key={l.id} onClick={() => nav.go(locked ? 'paywall' : 'live', l.id)} style={{
            flexShrink: 0, width: 200, cursor: 'pointer',             borderRadius: 20, overflow: 'hidden',
            border: T.border, background: T.card,
          }}>
            <div style={{
              position: 'relative', aspectRatio: '16 / 11',
              background: `linear-gradient(135deg, ${l.color}, ${l.color}90)`,
            }}>
              <image-slot
                id={`live-${l.id}`}
                placeholder={l.title}
                shape="rect"
                style={{ position: 'absolute', inset: 0 }}
              />
              <div style={{
                position: 'absolute', inset: 0,
                background: 'linear-gradient(180deg, transparent 50%, rgba(0,0,0,0.6) 100%)',
              }}/>
              {/* Live badge */}
              <div style={{
                position: 'absolute', top: 10, left: 10,
                padding: '4px 8px', borderRadius: 5,
                background: l.live ? '#E63149' : '#0E0A0C',
                color: '#fff', fontSize: 9, fontWeight: 800, letterSpacing: 1,
                display: 'flex', alignItems: 'center', gap: 5,
              }}>
                {l.live && <span style={{
                  width: 5, height: 5, borderRadius: 999, background: '#fff',
                  animation: 'live-pulse 1.5s ease-in-out infinite',
                }}/>}
                {l.tag}
              </div>
              {/* Lock */}
              {locked && (
                <div style={{
                  position: 'absolute', inset: 0,
                  background: 'rgba(0,0,0,0.35)',
                  backdropFilter: 'blur(6px)',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>
                  <div style={{
                    width: 36, height: 36, borderRadius: 999, background: 'rgba(255,255,255,0.9)',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                  }}>{Icon.lock('#0E0A0C', 14)}</div>
                </div>
              )}
            </div>
            <div style={{ padding: '12px 14px' }}>
              <div style={{ fontSize: 13, fontWeight: 700, color: T.text, letterSpacing: -0.2, lineHeight: 1.2 }}>{l.title}</div>
              <div style={{ fontSize: 11, color: T.muted, marginTop: 4, fontWeight: 500 }}>{l.when} · {l.duration} min</div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// SpotifyCard — full bleed green card
// ─────────────────────────────────────────────────────────────
function SpotifyCard({ T, nav }) {
  const [playing, setPlaying] = useState(true);
  const s = SPOTIFY.current;
  if (!s) return null;
  return (
    <div onClick={() => nav.go('spotify')} style={{
      margin: '4px 18px 0', padding: '16px 20px', cursor: 'pointer',
      borderRadius: 20, position: 'relative', overflow: 'hidden',
      background: 'linear-gradient(135deg, #1DB954 0%, #0F6B30 100%)',
    }}>
      <div style={{
        position: 'absolute', top: -20, right: -10, width: 130, height: 130,
        borderRadius: '50%', background: 'radial-gradient(circle, rgba(255,255,255,0.18), transparent 70%)',
      }}/>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, position: 'relative' }}>
        <SpotifyGlyph size={22} c="#fff"/>
        <div style={{ flex: 1, minWidth: 0 }}>
          <span className="pf-eyebrow" style={{ color: 'rgba(255,255,255,0.8)' }}>Pau Cardio Mix</span>
          <div style={{ fontSize: 14, fontWeight: 700, color: '#fff', marginTop: 2 }}>
            {s.song} <span style={{ fontWeight: 400, opacity: 0.7 }}>· {s.artist}</span>
          </div>
        </div>
        <button onClick={(e) => { e.stopPropagation(); setPlaying(!playing); }} style={{
          width: 38, height: 38, borderRadius: 999, border: 'none',
          background: '#fff', cursor: 'pointer', flexShrink: 0,
          display: 'flex', alignItems: 'center', justifyContent: 'center', paddingLeft: playing ? 0 : 2,
        }}>
          {playing
            ? <svg width="13" height="13" viewBox="0 0 14 14"><rect x="3" y="2" width="3" height="10" rx="1" fill="#1DB954"/><rect x="8" y="2" width="3" height="10" rx="1" fill="#1DB954"/></svg>
            : <svg width="12" height="12" viewBox="0 0 13 13"><path d="M3 2l8 4.5-8 4.5V2z" fill="#1DB954"/></svg>
          }
        </button>
      </div>

      {/* Waveform */}
      <div style={{ marginTop: 12, display: 'flex', alignItems: 'flex-end', gap: 2, height: 14, position: 'relative' }}>
        {Array.from({ length: 50 }).map((_, i) => (
          <span key={i} style={{
            flex: 1, height: `${30 + Math.abs(Math.sin(i * 0.5 + 1)) * 70}%`,
            background: 'rgba(255,255,255,0.45)', borderRadius: 1,
            opacity: i < 18 ? 1 : 0.35,
          }}/>
        ))}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// RecetaFeatured — receta del día + diario de comidas
// ─────────────────────────────────────────────────────────────
function RecetaFeatured({ T, nav, meals, onAddMeal, onDeleteMeal, onEditMeal }) {
  const macrosPrefs = useShowMacros();
  // Si la usuaria tiene plan activo → mostrar sus comidas del día
  // Si no → receta del día rotando (cambia cada día)
  const [planMeals, setPlanMeals] = useState(null); // null = loading
  const [planTitle, setPlanTitle] = useState('');

  // Reload del plan también cuando cache se actualiza (post-IA generation, etc)
  const [reloadTick, setReloadTick] = useState(0);
  useEffect(() => {
    const handler = () => setReloadTick(x => x + 1);
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);
  useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setPlanMeals([]); return; }
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) { setPlanMeals([]); return; }
        const { data: enrollments } = await sb
          .from('user_planes')
          .select('plan_id, enrolled_at')
          .eq('user_id', user.id)
          .is('completed_at', null)
          .order('enrolled_at', { ascending: false })
          .limit(1);
        const enrollment = enrollments?.[0];
        if (!enrollment) { setPlanMeals([]); return; }
        const start = new Date(enrollment.enrolled_at);
        const today = new Date();
        const daysElapsed = Math.floor((today - start) / 86400000);
        const todayDay = Math.max(1, daysElapsed + 1);
        const [{ data: planRow }, { data: dayRow }] = await Promise.all([
          sb.from('planes').select('title').eq('id', enrollment.plan_id).maybeSingle(),
          sb.from('plan_days').select('meals').eq('plan_id', enrollment.plan_id).eq('day_number', todayDay).maybeSingle(),
        ]);
        setPlanTitle(planRow?.title || '');
        const meals = Array.isArray(dayRow?.meals) ? dayRow.meals : [];
        // Enriquecer cada meal con su image_url de cache (recipe_images)
        if (meals.length > 0) {
          const keys = meals.map(m => {
            const n = (m?.name || '').toLowerCase()
              .normalize('NFD').replace(/[̀-ͯ]/g, '')
              .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80);
            return n;
          }).filter(Boolean);
          if (keys.length > 0) {
            const { data: images } = await sb.from('recipe_images')
              .select('recipe_key, image_url')
              .in('recipe_key', keys);
            const byKey = {};
            (images || []).forEach(i => { byKey[i.recipe_key] = i.image_url; });
            meals.forEach(m => {
              const k = (m?.name || '').toLowerCase()
                .normalize('NFD').replace(/[̀-ͯ]/g, '')
                .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80);
              if (byKey[k]) m.image_url = byKey[k];
            });
          }
        }
        setPlanMeals(meals);
      } catch (e) {
        console.warn('[home-plan]', e?.message);
        setPlanMeals([]);
      }
    })();
  }, [reloadTick]);

  // Receta del día rotativa (cambia cada día determinísticamente)
  function pickRecipeOfDay() {
    const pool = (window.__pauCache && window.__pauCache.recetas) || [];
    if (pool.length === 0) return null;
    const today = new Date();
    const seed = today.getFullYear() * 366 + today.getMonth() * 31 + today.getDate();
    return pool[seed % pool.length];
  }
  const featured = pickRecipeOfDay();
  const hasPlanMeals = Array.isArray(planMeals) && planMeals.length > 0;

  // ¿Esta comida del plan ya está registrada hoy? → se marca como hecha ✓
  // Matchea por tipo (desayuno/almuerzo/cena/snack) o por nombre parecido.
  const norm = (s) => String(s || '').toLowerCase()
    .normalize('NFD').replace(/[̀-ͯ]/g, '').trim();
  function isMealLogged(m, fallbackLabel) {
    const type = norm(m.mealType || fallbackLabel);
    const name = norm(m.name);
    return (meals || []).some(l => {
      const lType = norm(l.meal);
      const lName = norm(l.text);
      if (name && lName && (lName.includes(name.slice(0, 14)) || name.includes(lName.slice(0, 14)))) return true;
      return !!type && lType === type;
    });
  }

  return (
    <div style={{ margin: '0 20px' }}>
      {/* CASO 1: plan activo — fila COMPACTA (el grid completo vive en el
          tab Comidas; en Home solo la siguiente comida + progreso del día) */}
      {hasPlanMeals && (() => {
        const withState = planMeals.slice(0, 4).map((m, i) => {
          const rawLabel = m.mealType || ['Desayuno','Almuerzo','Snack','Cena'][i] || 'Almuerzo';
          return { m, rawLabel, done: isMealLogged(m, rawLabel) };
        });
        const doneCount = withState.filter(x => x.done).length;
        const next = withState.find(x => !x.done);
        const allDone = !next;
        return (
          <button onClick={() => nav.tab('recetas')} style={{
            width: '100%', padding: '14px 16px', cursor: 'pointer',
            border: T.border, borderRadius: 20, background: T.card,
            display: 'flex', alignItems: 'center', gap: 12, textAlign: 'left',
            fontFamily: 'inherit',
          }}>
            <div style={{
              width: 52, height: 52, borderRadius: 14, flexShrink: 0, overflow: 'hidden',
              background: allDone ? `${T.accent}18` : `linear-gradient(135deg, ${T.accent}15, ${T.accent}05)`,
              display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 24,
            }}>
              {allDone
                ? '🎉'
                : (next.m.image_url
                  ? <img src={next.m.image_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
                  : (next.m.emoji || '🍽️'))}
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 9.5, fontWeight: 700, color: allDone ? T.accent : T.muted, letterSpacing: 0.5,  }}>
                {allDone ? 'Comidas de hoy' : `Siguiente · ${next.rawLabel}`}
              </div>
              <div style={{
                fontSize: 13.5, fontWeight: 700, color: T.text, marginTop: 2, letterSpacing: -0.2,
                overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
              }}>
                {allDone ? '¡Todas hechas! 💗' : (next.m.name || 'Comida')}
              </div>
              <div style={{ fontSize: 10.5, color: T.muted, marginTop: 2, fontWeight: 600 }}>
                ✓ {doneCount} de {withState.length} hechas
              </div>
            </div>
            {Icon.chevR(T.muted, 9)}
          </button>
        );
      })()}

      {/* CASO 2: sin plan activo — receta del día rotativa */}
      {!hasPlanMeals && featured && (
        <div style={{
          padding: 0, background: T.card, borderRadius: 20,
          border: T.border, overflow: 'hidden',
        }}>
          <div onClick={() => nav.go('receta', featured.id)} style={{ position: 'relative', aspectRatio: '3 / 4', cursor: 'pointer' }}>
            <RecipeImage receta={featured} T={T} style={{ position: 'absolute', inset: 0 }}/>
            <div style={{
              position: 'absolute', inset: 0,
              background: 'linear-gradient(0deg, rgba(0,0,0,0.6) 0%, transparent 50%)',
            }}/>
            <div style={{ position: 'absolute', bottom: 12, left: 14, right: 14, color: '#fff' }}>
              <span className="pf-eyebrow" style={{ color: 'rgba(255,255,255,0.85)' }}>Receta del día · {featured.category}</span>
              <div className="pf-display" style={{ fontSize: 22, fontWeight: 800, marginTop: 3, letterSpacing: -0.5, lineHeight: 1.1 }}>
                {featured.title}
              </div>
              <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.85)', marginTop: 4, fontWeight: 600 }}>
                {featured.kcal} kcal · {featured.minutes} min
              </div>
            </div>
          </div>
          <button onClick={() => {
            if (nav.openModal) nav.openModal(<AIPlanWizard T={T} nav={nav} onClose={() => nav.closeModal()}/>);
          }} style={{
            width: '100%', padding: '12px 16px', textAlign: 'center',
            fontSize: 11.5, color: T.accent, fontWeight: 700,
            border: 'none', background: 'transparent', cursor: 'pointer',
            fontFamily: 'inherit',
          }}>
            ✨ Crea tu plan personalizado con IA →
          </button>
        </div>
      )}

      {/* Diario movido al tab Comidas (RegistroHoy) — Home queda ligera */}
    </div>
  );
}

function SectionHeader({ title, action, onAction, T, children }) {
  // Anna-style: titular grande bold uppercase, sin texto extra
  return (
    <div style={{
      display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between',
      padding: '32px 20px 14px',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <span className="pf-display-bold" style={{
          fontSize: 18, color: T.text, lineHeight: 1,
        }}>{title}</span>
        {children}
      </div>
      {action && (
        <button onClick={onAction} style={{
          border: 'none', background: 'transparent', cursor: 'pointer',
          fontSize: 11, fontWeight: 700, color: T.accent, padding: 0,
          letterSpacing: 0.3,
        }}>{action} ›</button>
      )}
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// SilbeWeekPills — calendario semanal de pills estilo Silbe
// ═════════════════════════════════════════════════════════════
function SilbeWeekPills({ T }) {
  const [doneSet, setDoneSet] = React.useState(new Set());

  // Cargar día_completions reales de esta semana
  React.useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) return;
    (async () => {
      const { data: { user } } = await sb.auth.getUser();
      if (!user) return;
      // Lunes de esta semana
      const now = new Date();
      const dow = (now.getDay() + 6) % 7;
      const monday = new Date(now); monday.setDate(now.getDate() - dow);
      monday.setHours(0, 0, 0, 0);
      const { data } = await sb
        .from('day_completions')
        .select('completed_at')
        .eq('user_id', user.id)
        .gte('completed_at', monday.toISOString())
        .limit(20);
      const ds = new Set();
      (data || []).forEach(r => {
        const d = new Date(r.completed_at);
        ds.add(d.toLocaleDateString('en-CA'));
      });
      setDoneSet(ds);
    })();
    // Re-fetch cuando se completa un día
    const handler = () => {
      const sb2 = window.__PAU_SUPABASE;
      if (!sb2) return;
      (async () => {
        const { data: { user } } = await sb2.auth.getUser();
        if (!user) return;
        const now = new Date();
        const dow = (now.getDay() + 6) % 7;
        const monday = new Date(now); monday.setDate(now.getDate() - dow);
        monday.setHours(0, 0, 0, 0);
        const { data } = await sb2
          .from('day_completions')
          .select('completed_at')
          .eq('user_id', user.id)
          .gte('completed_at', monday.toISOString())
          .limit(20);
        const ds = new Set();
        (data || []).forEach(r => {
          const d = new Date(r.completed_at);
          ds.add(d.toLocaleDateString('en-CA'));
        });
        setDoneSet(ds);
      })();
    };
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);

  const today = new Date();
  const todayIdx = (today.getDay() + 6) % 7; // Lun=0
  const monday = new Date(today); monday.setDate(today.getDate() - todayIdx);
  const days = ['L','M','X','J','V','S','D'].map((letter, i) => {
    const d = new Date(monday); d.setDate(monday.getDate() + i);
    const ds = d.toLocaleDateString('en-CA');
    return {
      letter,
      num: d.getDate(),
      isToday: i === todayIdx,
      isDone: doneSet.has(ds),
      isPast: i < todayIdx,
    };
  });
  return (
    <div style={{
      margin: '16px 20px 0', padding: '8px 6px',
      background: T.card, borderRadius: 20, border: T.border,
      display: 'flex', justifyContent: 'space-between', gap: 2,
    }}>
      {days.map((d, i) => (
        <div key={i} style={{
          flex: 1, padding: '8px 2px',
          display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
          borderRadius: 14,
          background: d.isToday ? T.accent : (d.isDone ? `${T.accent}18` : 'transparent'),
          transition: 'background 200ms ease',
          position: 'relative',
        }}>
          <span style={{
            fontSize: 9, fontWeight: 600,
            color: d.isToday ? '#fff' : T.muted,
            letterSpacing: 0.3,
          }}>{d.letter}</span>
          <span style={{
            fontSize: 15, fontWeight: 500,
            color: d.isToday ? '#fff' : T.text,
            letterSpacing: -0.3, lineHeight: 1,
          }}>{d.num}</span>
          {/* Dot de completado debajo del número */}
          <span style={{
            width: 5, height: 5, borderRadius: 999,
            marginTop: 2,
            background: d.isDone
              ? (d.isToday ? '#fff' : T.accent)
              : 'transparent',
          }}/>
        </div>
      ))}
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// RETOS — programas + rutinas
// ═════════════════════════════════════════════════════════════
function RetosScreen({ theme, isPremium, nav }) {
  const T = theme;
  const [sub, setSub] = useState('retos');
  const [searchQ, setSearchQ] = useState('');
  const [showSearch, setShowSearch] = useState(false);

  return (
    <div style={{ paddingBottom: 120 }}>
      <PauTopBar T={T} nav={nav} showSearch showBookmark
        onSearch={() => setShowSearch(s => !s)}
        onBookmark={() => nav.go('guardados')}
      />
      {showSearch && (
        <div style={{ padding: '12px 20px 0' }}>
          <input
            autoFocus
            value={searchQ}
            onChange={(e) => setSearchQ(e.target.value)}
            placeholder="Buscar rutina o reto…"
            style={{
              width: '100%', padding: '14px 16px',
              border: T.border, borderRadius: 999, background: T.card,
              fontSize: 14, color: T.text, outline: 'none',
            }}
          />
        </div>
      )}
      <div style={{ padding: '14px 20px 0' }}>
        <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95 }}>
          Rutinas
        </div>
        {!isPremium && (
          <div style={{
            marginTop: 12, padding: '10px 14px',
            background: T.subtle, borderRadius: 14,
            display: 'flex', alignItems: 'center', gap: 10,
          }}>
            <span style={{ fontSize: 16 }}>👀</span>
            <span style={{ fontSize: 11.5, color: T.text, fontWeight: 600, lineHeight: 1.35, flex: 1 }}>
              Vista previa: 3 gratis · resto bloqueado.
              <button onClick={() => nav.go('paywall')} style={{
                marginLeft: 4, border: 'none', background: 'transparent', cursor: 'pointer',
                color: T.accent, fontSize: 11.5, fontWeight: 800, padding: 0,
                textDecoration: 'underline',
              }}>Desbloquear</button>
            </span>
          </div>
        )}
      </div>

      {/* Sub tabs */}
      <div style={{
        margin: '18px 20px 0', padding: 4,
        background: T.subtle, borderRadius: 14, display: 'flex', position: 'relative',
      }}>
        <div style={{
          position: 'absolute', top: 4, bottom: 4,
          left: sub === 'retos' ? 4 : 'calc(50% + 0px)',
          width: 'calc(50% - 4px)',
          background: T.text, borderRadius: 9,
          transition: 'left 240ms cubic-bezier(.2,.8,.2,1)',
        }}/>
        {[
          { id: 'retos', label: 'Programas' },
          { id: 'rutinas', label: 'Rutinas' },
        ].map(s => (
          <button key={s.id} onClick={() => setSub(s.id)} style={{
            flex: 1, padding: '9px 6px', border: 'none', background: 'transparent', cursor: 'pointer',
            position: 'relative', zIndex: 1,
            fontSize: 11.5, fontWeight: 800, letterSpacing: 0.3,
            color: sub === s.id ? T.card : T.muted,
          }}>{s.label}</button>
        ))}
      </div>

      {sub === 'retos' ? <RetosList T={T} isPremium={isPremium} nav={nav} searchQ={searchQ}/> : <RutinasList T={T} isPremium={isPremium} nav={nav} searchQ={searchQ}/>}
    </div>
  );
}

// RETOS LIST estilo Silbe: SECCIONES horizontales con carruseles
// Portadas 3:4 (vertical) — como tu captura "PIERNAS + GLÚTEOS"
function RetosList({ T, isPremium, nav, searchQ = '' }) {
  const q = (searchQ || '').toLowerCase().trim();
  const filteredAll = window.pauRetos().filter(r => {
    if (q && !(`${r.title} ${r.desc || ''} ${r.category || ''}`).toLowerCase().includes(q)) return false;
    return true;
  });

  // Agrupar por categoría
  const cats = [...new Set(filteredAll.map(r => r.category).filter(Boolean))];
  const featured = filteredAll.slice(0, 5);

  // Card de reto Anna-style: foto grande con título overlay, sin contenedor de texto
  function RetoCard({ r, width = 240, big = false }) {
    const locked = r.tag === 'PRO' && !isPremium;
    const joined = nav.joinedRetos.has(r.id);
    return (
      <div onClick={() => nav.go(locked ? 'paywall' : 'reto', r.id)} style={{
        flexShrink: 0, width, cursor: 'pointer',
        borderRadius: 22, overflow: 'hidden',
        position: 'relative', aspectRatio: '3 / 4',
        background: 'rgba(0,0,0,0.04)',
      }}>
        {r.image
          ? <img loading="lazy" src={r.image} alt={r.title} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}/>
          : <div style={{ position: 'absolute', inset: 0, background: `linear-gradient(135deg, ${r.color}, ${r.bg})` }}/>
        }
        <div style={{
          position: 'absolute', inset: 0,
          background: 'linear-gradient(180deg, transparent 40%, rgba(0,0,0,0.75) 100%)',
        }}/>
        {/* Bookmark */}
        <button onClick={(e) => { e.stopPropagation(); const k='paufit-favs-retos'; const cur=new Set(JSON.parse(localStorage.getItem(k)||'[]')); if(cur.has(r.id)){cur.delete(r.id);nav.toast('Quitado',{icon:'🤍'});}else{cur.add(r.id);nav.toast('Guardado 💗',{icon:'🔖'});} localStorage.setItem(k,JSON.stringify([...cur])); }} style={{
          position: 'absolute', top: 12, right: 12,
          width: 30, height: 30, borderRadius: 999, border: 'none',
          background: 'rgba(255,255,255,0.95)', cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <svg width="11" height="13" viewBox="0 0 16 18" fill="none">
            <path d="M2 2v14l6-4 6 4V2H2z" stroke={T.accent} strokeWidth="1.8" strokeLinejoin="round"/>
          </svg>
        </button>
        {/* Lock */}
        {locked && (
          <div style={{
            position: 'absolute', top: 12, left: 12,
            width: 30, height: 30, borderRadius: 999,
            background: 'rgba(14,10,12,0.7)', backdropFilter: 'blur(10px)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>{Icon.lock('#fff', 13)}</div>
        )}
        {/* Joined */}
        {joined && (
          <div style={{
            position: 'absolute', top: 12, left: 12,
            padding: '5px 10px', borderRadius: 999,
            background: '#fff', color: T.text,
            fontSize: 9, fontWeight: 800, letterSpacing: 0.6,
          }}>EN CURSO ✓</div>
        )}
        {/* Título sobre foto Anna style */}
        <div style={{
          position: 'absolute', bottom: 14, left: 14, right: 14, color: '#fff',
        }}>
          <div className="pf-display-bold" style={{
            fontSize: big ? 18 : 14, color: '#fff', lineHeight: 1, letterSpacing: -0.4,
            textTransform: 'none',
            textShadow: '0 2px 8px rgba(0,0,0,0.5)',
            display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
            overflow: 'hidden',
          }}>{r.title}</div>
          <div style={{
            marginTop: 6, fontSize: 9, fontWeight: 800, letterSpacing: 0.6,
            color: 'rgba(255,255,255,0.92)', textShadow: '0 1px 3px rgba(0,0,0,0.5)',
          }}>
            {r.days} DÍAS · {r.minPerDay} MIN
          </div>
        </div>
      </div>
    );
  }

  function CarouselSection({ title, items, big }) {
    if (!items.length) return null;
    return (
      <div style={{ marginTop: 28 }}>
        <div style={{ padding: '0 20px' }}>
          <div className="pf-display-bold" style={{ fontSize: 16, color: T.text, lineHeight: 1 }}>
            {title}
          </div>
        </div>
        <div style={{
          marginTop: 14,
          display: 'flex', gap: 12, overflowX: 'auto',
          padding: '0 20px 4px',
        }} className="hide-scroll">
          {items.map(r => <RetoCard key={r.id} r={r} width={big ? 260 : 180} big={big}/>)}
        </div>
      </div>
    );
  }

  if (q) {
    // Si hay búsqueda activa, grid 2x lineal
    return (
      <div style={{ padding: '18px 20px 0', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        {filteredAll.map(r => <RetoCard key={r.id} r={r} width="100%"/>)}
        {filteredAll.length === 0 && (
          <div style={{ gridColumn: '1 / -1' }}>
            <EmptyState emoji="🔍" T={T} title="Sin resultados" description={`No encontramos retos con "${searchQ}"`}/>
          </div>
        )}
      </div>
    );
  }

  return (
    <div style={{ paddingTop: 4 }}>
      {/* Programas estructurados por semanas (desde Supabase) */}
      <ProgramasSection T={T} isPremium={isPremium} nav={nav} searchQ={searchQ}/>
      <CarouselSection title="Para ti" items={featured} big/>
      {cats.map(cat => (
        <CarouselSection key={cat} title={cat} items={filteredAll.filter(r => r.category === cat)}/>
      ))}
    </div>
  );
}

function BigRetoCard({ reto, T, isPremium, onClick, joined }) {
  const r = reto;
  const locked = r.tag === 'PRO' && !isPremium;
  return (
    <div onClick={onClick} style={{
      cursor: 'pointer', borderRadius: 20,
      background: T.card, border: T.border, overflow: 'hidden',
    }}>
      <div style={{
        position: 'relative', aspectRatio: '3 / 4',
        background: 'rgba(0,0,0,0.04)',
      }}>
        {r.image
          ? <img loading="lazy" src={r.image} alt={r.title} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}/>
          : <div style={{ position: 'absolute', inset: 0, background: `linear-gradient(135deg, ${r.color}, ${r.bg})` }}/>
        }
        {/* Tag */}
        <div style={{
          position: 'absolute', top: 12, left: 12,
          padding: '5px 9px', borderRadius: 5,
          background: r.tag === 'GRATIS' ? 'rgba(255,255,255,0.95)' : 'rgba(14,10,12,0.85)',
          backdropFilter: 'blur(8px)',
          color: r.tag === 'GRATIS' ? '#0E0A0C' : '#fff',
          fontSize: 9, fontWeight: 700, letterSpacing: 1.2,
        }}>{r.tag}</div>
        {joined && (
          <div style={{
            position: 'absolute', top: 12, right: 12,
            padding: '5px 10px', borderRadius: 999,
            background: 'rgba(255,255,255,0.95)', color: T.deep,
            fontSize: 10, fontWeight: 700, letterSpacing: 0.6,
          }}>✓ UNIDA</div>
        )}
        {locked && !joined && (
          <div style={{
            position: 'absolute', top: 12, right: 12,
            width: 32, height: 32, borderRadius: 999,
            background: 'rgba(14,10,12,0.7)', backdropFilter: 'blur(10px)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>{Icon.lock('#fff', 14)}</div>
        )}
      </div>
      <div style={{ padding: '14px 20px' }}>
        <div className="pf-display" style={{ fontSize: 22, fontWeight: 500, color: T.text, letterSpacing: -0.4, lineHeight: 1.1 }}>
          {r.title}
        </div>
        <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.45, fontWeight: 500 }}>{r.desc}</div>
        <div style={{
          marginTop: 10, display: 'flex', alignItems: 'center', gap: 12,
          fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.4, 
          flexWrap: 'nowrap', whiteSpace: 'nowrap',
        }}>
          <span>{r.days} días</span>
          <span style={{ width: 3, height: 3, borderRadius: 999, background: T.muted, flexShrink: 0 }}/>
          <span>{r.minPerDay} min/día</span>
          <span style={{ width: 3, height: 3, borderRadius: 999, background: T.muted, flexShrink: 0 }}/>
          <span>{r.intensity}</span>
        </div>
      </div>
    </div>
  );
}

// RUTINAS Anna minimal: filtro tiempo + carruseles por músculo
function RutinasList({ T, isPremium, nav, searchQ = '' }) {
  const q = (searchQ || '').toLowerCase().trim();
  // Filtro de tiempo "solo tengo X min hoy" — idea seguidoras Pau
  const [timeFilter, setTimeFilter] = useState('todas');
  function passesTime(r) {
    if (timeFilter === 'todas') return true;
    const m = r.minutes || r.duration_min || 30;
    if (timeFilter === '<=10') return m <= 10;
    if (timeFilter === '<=20') return m <= 20;
    if (timeFilter === '<=30') return m <= 30;
    if (timeFilter === '>30') return m > 30;
    return true;
  }
  const filteredAll = (window.__pauCache?.rutinas || RUTINAS).filter(r => {
    if (q && !(`${r.title || r.name || ''} ${r.muscle || ''}`).toLowerCase().includes(q)) return false;
    if (!passesTime(r)) return false;
    return true;
  });
  const muscles = [...new Set(filteredAll.map(r => r.muscle).filter(Boolean))];
  const featured = filteredAll.slice(0, 6);
  // Pills de tiempo — siempre visibles arriba
  function TimePills() {
    const opts = [
      { v: 'todas', l: 'Todas' },
      { v: '<=10', l: '≤ 10 min' },
      { v: '<=20', l: '≤ 20 min' },
      { v: '<=30', l: '≤ 30 min' },
      { v: '>30', l: '+ 30 min' },
    ];
    return (
      <div style={{
        display: 'flex', gap: 8, padding: '12px 20px 4px', overflowX: 'auto',
      }} className="hide-scroll">
        {opts.map(o => (
          <Pill key={o.v} active={timeFilter === o.v} onClick={() => setTimeFilter(o.v)} dark={T.dark} color={T.accent}>
            {o.l}
          </Pill>
        ))}
      </div>
    );
  }

  function RutinaCard({ r, width = 240 }) {
    const locked = r.tag === 'PRO' && !isPremium;
    const isDemo = r.is_demo || r.tag === 'DEMO';
    const posterUrl = r.poster_path
      ? `${window.__PAU_SUPABASE_URL || ''}/storage/v1/object/public/rutinas-posters/${r.poster_path}`
      : null;
    return (
      <div onClick={() => nav.go(locked && !isDemo ? 'paywall' : 'rutina', r.id)} style={{
        flexShrink: 0, width, cursor: 'pointer',
        borderRadius: 22, overflow: 'hidden',
        position: 'relative', aspectRatio: '4 / 5',
        background: posterUrl ? `url(${posterUrl}) center/cover` : `linear-gradient(135deg, ${T.subtle}, ${T.accent}40)`,
      }}>
        <div style={{
          position: 'absolute', inset: 0,
          background: 'linear-gradient(180deg, transparent 45%, rgba(0,0,0,0.75) 100%)',
        }}/>
        {/* Bookmark */}
        <button onClick={(e) => { e.stopPropagation(); const k='paufit-favs-rutinas'; const cur=new Set(JSON.parse(localStorage.getItem(k)||'[]')); if(cur.has(r.id)){cur.delete(r.id);nav.toast('Quitado',{icon:'🤍'});}else{cur.add(r.id);nav.toast('Guardado 💗',{icon:'🔖'});} localStorage.setItem(k,JSON.stringify([...cur])); }} style={{
          position: 'absolute', top: 12, right: 12,
          width: 30, height: 30, borderRadius: 999, border: 'none',
          background: 'rgba(255,255,255,0.95)', cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <svg width="11" height="13" viewBox="0 0 16 18" fill="none">
            <path d="M2 2v14l6-4 6 4V2H2z" stroke={T.accent} strokeWidth="1.8" strokeLinejoin="round"/>
          </svg>
        </button>
        {locked && !isDemo && (
          <div style={{
            position: 'absolute', top: 12, left: 12,
            width: 30, height: 30, borderRadius: 999,
            background: 'rgba(14,10,12,0.7)', backdropFilter: 'blur(10px)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>{Icon.lock('#fff', 13)}</div>
        )}
        {/* Título sobre foto Anna style */}
        <div style={{
          position: 'absolute', bottom: 14, left: 14, right: 14, color: '#fff',
        }}>
          <div className="pf-display-bold" style={{
            fontSize: 13, color: '#fff', lineHeight: 1, letterSpacing: -0.3,
            textShadow: '0 2px 8px rgba(0,0,0,0.5)',
            display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
            overflow: 'hidden',
          }}>{r.title}</div>
          <div style={{
            marginTop: 6, fontSize: 9, fontWeight: 800, letterSpacing: 0.6,
            color: 'rgba(255,255,255,0.92)', textShadow: '0 1px 3px rgba(0,0,0,0.5)',
          }}>
            {r.minutes || r.duration_min || 30} MIN
          </div>
        </div>
      </div>
    );
  }

  function CarouselSection({ title, items }) {
    if (!items.length) return null;
    return (
      <div style={{ marginTop: 28 }}>
        <div style={{ padding: '0 20px' }}>
          <div className="pf-display-bold" style={{ fontSize: 16, color: T.text, lineHeight: 1 }}>
            {title}
          </div>
        </div>
        <div style={{
          marginTop: 14,
          display: 'flex', gap: 12, overflowX: 'auto',
          padding: '0 20px 4px',
        }} className="hide-scroll">
          {items.map(r => <RutinaCard key={r.id} r={r} width={200}/>)}
        </div>
      </div>
    );
  }

  if (q) {
    return (
      <div>
        <TimePills/>
        <div style={{ padding: '18px 20px 0', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
          {filteredAll.map(r => <RutinaCard key={r.id} r={r} width="100%"/>)}
          {filteredAll.length === 0 && (
            <div style={{ gridColumn: '1 / -1' }}>
              <EmptyState emoji="🔍" T={T} title="Sin resultados" description={`No encontramos rutinas con "${searchQ}"`}/>
            </div>
          )}
        </div>
      </div>
    );
  }

  return (
    <div style={{ paddingTop: 4 }}>
      <TimePills/>
      {filteredAll.length === 0 ? (
        <div style={{ padding: '40px 24px', textAlign: 'center', color: T.muted, fontSize: 13, lineHeight: 1.5 }}>
          No hay rutinas con ese tiempo. Prueba otra duración.
        </div>
      ) : (
        <>
          <CarouselSection title="Para ti" items={featured}/>
          {muscles.map(m => (
            <CarouselSection key={m} title={m.charAt(0).toUpperCase() + m.slice(1).toLowerCase()} items={filteredAll.filter(r => r.muscle === m)}/>
          ))}
        </>
      )}
    </div>
  );
}

function Stat({ label, T }) {
  return <span style={{ fontSize: 11, color: T.muted, fontWeight: 600 }}>{label}</span>;
}

// ═════════════════════════════════════════════════════════════
// COMIDAS (Planes + Recetas)
// ═════════════════════════════════════════════════════════════
// ─────────────────────────────────────────────────────────────
// NutriHoy — anillo de kcal + barras de macros de HOY (estilo Yazio/Cal AI).
// Objetivo = suma de las comidas del plan de hoy; sin plan → valores estándar.
// Consumido = meal_logs de hoy. Un solo tono de marca sobre pista neutra,
// números en tinta de texto (no en color de serie), barras finas redondeadas.
// ─────────────────────────────────────────────────────────────
function NutriHoy({ T }) {
  const [logged, setLogged] = useState(null);  // { kcal, p, c, g } consumido hoy
  const [target, setTarget] = useState(null);  // { kcal, p, c, g } objetivo de hoy
  const [reloadTick, setReloadTick] = useState(0);

  useEffect(() => {
    const handler = () => setReloadTick(x => x + 1);
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);

  useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) return;
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) return;
        // 1) Consumido hoy (meal_logs con macros)
        const dayStart = new Date(); dayStart.setHours(0, 0, 0, 0);
        const { data: logs } = await sb.from('meal_logs')
          .select('kcal, protein_g, carbs_g, fat_g')
          .eq('user_id', user.id)
          .gte('logged_at', dayStart.toISOString());
        const sum = { kcal: 0, p: 0, c: 0, g: 0 };
        (logs || []).forEach(l => {
          sum.kcal += l.kcal || 0; sum.p += l.protein_g || 0;
          sum.c += l.carbs_g || 0; sum.g += l.fat_g || 0;
        });
        setLogged(sum);
        // 2) Objetivo: comidas del plan de HOY sumadas (o kcal_per_day del plan)
        const { data: enrollments } = await sb.from('user_planes')
          .select('plan_id, enrolled_at').eq('user_id', user.id)
          .is('completed_at', null).order('enrolled_at', { ascending: false }).limit(1);
        const enroll = enrollments?.[0];
        let tgt = { kcal: 1800, p: 100, c: 180, g: 60 };
        if (enroll) {
          const daysElapsed = Math.floor((new Date() - new Date(enroll.enrolled_at)) / 86400000);
          const todayDay = Math.max(1, daysElapsed + 1);
          const [{ data: planRow }, { data: dayRow }] = await Promise.all([
            sb.from('planes').select('kcal_per_day').eq('id', enroll.plan_id).maybeSingle(),
            sb.from('plan_days').select('meals').eq('plan_id', enroll.plan_id).eq('day_number', todayDay).maybeSingle(),
          ]);
          const meals = Array.isArray(dayRow?.meals) ? dayRow.meals : [];
          if (meals.length > 0) {
            const s = { kcal: 0, p: 0, c: 0, g: 0 };
            meals.forEach(m => {
              const em = ensureMacros(m);
              s.kcal += em.kcal || 0; s.p += em.protein_g || 0;
              s.c += em.carbs_g || 0; s.g += em.fat_g || 0;
            });
            if (s.kcal > 400) tgt = s;
          } else if (planRow?.kcal_per_day) {
            tgt = { kcal: planRow.kcal_per_day, p: Math.round(planRow.kcal_per_day * 0.25 / 4), c: Math.round(planRow.kcal_per_day * 0.45 / 4), g: Math.round(planRow.kcal_per_day * 0.30 / 9) };
          }
        }
        setTarget(tgt);
      } catch (e) { console.warn('[nutri-hoy]', e?.message); }
    })();
  }, [reloadTick]);

  if (!logged || !target) return null;

  const remaining = Math.round(target.kcal - logged.kcal);
  const pct = Math.max(0, Math.min(1, logged.kcal / (target.kcal || 1)));
  const R = 40, CIRC = 2 * Math.PI * R;

  function MacroBar({ label, val, max }) {
    const p = Math.max(0, Math.min(1, val / (max || 1)));
    return (
      <div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
          <span style={{ fontSize: 10, fontWeight: 700, color: T.muted, letterSpacing: 0.4,  }}>{label}</span>
          <span style={{ fontSize: 10, fontWeight: 700, color: T.text }}>{Math.round(val)}<span style={{ color: T.muted, fontWeight: 600 }}> / {Math.round(max)} g</span></span>
        </div>
        <div style={{ marginTop: 4, height: 6, borderRadius: 999, background: T.subtle, overflow: 'hidden' }}>
          <div style={{ height: '100%', width: `${p * 100}%`, borderRadius: 999, background: T.accent, transition: 'width 500ms ease' }}/>
        </div>
      </div>
    );
  }

  return (
    <div style={{
      margin: '16px 20px 0', padding: '18px 18px',
      background: T.card, border: T.border, borderRadius: 20,
      display: 'flex', alignItems: 'center', gap: 18,
    }}>
      {/* Anillo kcal — lo primero que ves al abrir: cuánto te queda hoy */}
      <div style={{ position: 'relative', width: 104, height: 104, flexShrink: 0 }}>
        <svg width="104" height="104" viewBox="0 0 104 104">
          <circle cx="52" cy="52" r={R} fill="none" stroke={T.subtle} strokeWidth="9"/>
          <circle cx="52" cy="52" r={R} fill="none" stroke={T.accent} strokeWidth="9"
            strokeLinecap="round"
            strokeDasharray={`${CIRC * pct} ${CIRC}`}
            transform="rotate(-90 52 52)"
            style={{ transition: 'stroke-dasharray 600ms ease' }}/>
        </svg>
        <div style={{
          position: 'absolute', inset: 0,
          display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
        }}>
          <div style={{ fontSize: 20, fontWeight: 800, color: T.text, letterSpacing: -0.5, lineHeight: 1 }}>
            {Math.abs(remaining)}
          </div>
          <div style={{ fontSize: 8.5, fontWeight: 700, color: T.muted, letterSpacing: 0.4,  marginTop: 3 }}>
            {remaining >= 0 ? 'kcal te quedan' : 'kcal de más'}
          </div>
        </div>
      </div>
      {/* Macros del día — texto en tinta, identidad por etiqueta */}
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 9 }}>
        <MacroBar label="Proteína" val={logged.p} max={target.p}/>
        <MacroBar label="Carbs" val={logged.c} max={target.c}/>
        <MacroBar label="Grasas" val={logged.g} max={target.g}/>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// RegistroHoy — lo que YA registraste hoy, con opción de borrar.
// Pau: "que permita ver lo que ya has registrado por si quieres borrarlo"
// ─────────────────────────────────────────────────────────────
function RegistroHoy({ T, nav }) {
  const [logs, setLogs] = useState(null);
  const [reloadTick, setReloadTick] = useState(0);

  useEffect(() => {
    const handler = () => setReloadTick(x => x + 1);
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);

  useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) return;
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) return;
        const dayStart = new Date(); dayStart.setHours(0, 0, 0, 0);
        const { data } = await sb.from('meal_logs')
          .select('id, meal_name, meal_type, kcal, protein_g, carbs_g, fat_g, logged_at')
          .eq('user_id', user.id)
          .gte('logged_at', dayStart.toISOString())
          .order('logged_at', { ascending: true });
        setLogs(data || []);
      } catch (e) { console.warn('[registro-hoy]', e?.message); }
    })();
  }, [reloadTick]);

  async function borrar(log) {
    if (!window.confirm(`¿Borrar "${log.meal_name}" del registro?`)) return;
    const sb = window.__PAU_SUPABASE;
    try {
      const { data: { user } } = await sb.auth.getUser();
      if (!user) { nav.toast('Tu sesión expiró — vuelve a entrar', { icon: '🔒' }); return; }
      const { error } = await sb.from('meal_logs').delete().eq('id', log.id).eq('user_id', user.id);
      if (error) throw error;
      nav.toast('Borrada', { icon: '🗑️' });
      window.dispatchEvent(new CustomEvent('pau:cache-updated'));
    } catch (e) {
      console.warn('[registro-hoy]', e?.message); nav.toast('No se pudo borrar', { icon: '⚠️' });
    }
  }

  if (!logs || logs.length === 0) return null;

  const typeEmoji = { desayuno: '🌅', snack: '☕', almuerzo: '🥗', cena: '🌙' };

  return (
    <div style={{ margin: '12px 20px 0', padding: '14px 16px', background: T.card, border: T.border, borderRadius: 20 }}>
      <div style={{ fontSize: 10.5, fontWeight: 800, letterSpacing: 0.6, color: T.muted,  marginBottom: 8 }}>
        Registrado hoy · {logs.length}
      </div>
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        {logs.map((l, i) => (
          <div key={l.id} style={{
            display: 'flex', alignItems: 'center', gap: 10,
            padding: '9px 0',
            borderBottom: i < logs.length - 1 ? `0.5px solid ${T.muted}18` : 'none',
          }}>
            <span style={{ fontSize: 17 }}>{typeEmoji[l.meal_type] || '🍽️'}</span>
            <button onClick={() => {
              if (nav.openModal && window.AddMealModal) {
                nav.openModal(React.createElement(window.AddMealModal, {
                  open: true, T, accent: T.accent,
                  prefill: { editId: l.id, name: l.meal_name, mealType: l.meal_type, kcal: l.kcal, protein_g: l.protein_g, carbs_g: l.carbs_g, fat_g: l.fat_g },
                  onClose: () => nav.closeModal(),
                  onSave: () => { nav.closeModal(); nav.toast('Comida actualizada 💗', { icon: '✓' }); window.dispatchEvent(new CustomEvent('pau:cache-updated')); },
                }));
              }
            }} style={{ flex: 1, minWidth: 0, textAlign: 'left', border: 'none', background: 'transparent', padding: 0, cursor: 'pointer', fontFamily: 'inherit' }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, color: T.text, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {l.meal_name}
              </div>
              <div style={{ fontSize: 10, color: T.muted, marginTop: 1 }}>
                {new Date(l.logged_at).toLocaleTimeString('es', { hour: '2-digit', minute: '2-digit' })}{l.kcal ? ` · ${l.kcal} kcal` : ''}
              </div>
            </button>
            <button onClick={() => borrar(l)} aria-label="Borrar" style={{
              width: 30, height: 30, border: 'none', borderRadius: 999,
              background: 'transparent', cursor: 'pointer', fontSize: 14, color: T.muted,
            }}>🗑️</button>
          </div>
        ))}
      </div>
    </div>
  );
}

function RecetasScreen({ theme, isPremium, nav }) {
  const T = theme;
  const [sub, setSub] = useState('planes');
  // Si ya tiene plan activo, "Planes" sobra (decisión de Pau): solo Recetas
  const [hasActivePlan, setHasActivePlan] = useState(null);
  useEffect(() => {
    (async () => {
      try {
        const active = await (window.PauUserRetos?.getActivePlan?.() || Promise.resolve(null));
        setHasActivePlan(!!active);
        if (active) setSub('recetas');
      } catch { setHasActivePlan(false); }
    })();
  }, []);
  const [searchQ, setSearchQ] = useState('');
  const [showSearch, setShowSearch] = useState(false);
  // Re-render cuando se crea/actualiza el plan activo (post-IA)
  const [, setBump] = useState(0);
  useEffect(() => {
    const handler = () => setBump(x => x + 1);
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);

  return (
    <div style={{ paddingBottom: 120 }}>
      <PauTopBar T={T} nav={nav} showSearch showBookmark
        onSearch={() => setShowSearch(s => !s)}
        onBookmark={() => nav.go('guardados')}
      />
      {showSearch && (
        <div style={{ padding: '12px 20px 0' }}>
          <input
            autoFocus
            value={searchQ}
            onChange={(e) => setSearchQ(e.target.value)}
            placeholder="Buscar receta o plan…"
            style={{
              width: '100%', padding: '14px 16px',
              border: T.border, borderRadius: 999, background: T.card,
              fontSize: 14, color: T.text, outline: 'none',
            }}
          />
        </div>
      )}
      <div style={{ padding: '14px 20px 0' }}>
        <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95 }}>
          Comidas
        </div>
      </div>

      {/* 1️⃣ TU DÍA — anillo de kcal y macros (lo primero, estilo Yazio/Cal AI) */}
      <NutriHoy T={T}/>

      {/* Lo ya registrado hoy — visible y borrable */}
      <RegistroHoy T={T} nav={nav}/>

      {/* 2️⃣ ACCIONES RÁPIDAS — la foto es la estrella (estilo Cal AI) */}
      <div style={{ margin: '12px 20px 0', display: 'grid', gridTemplateColumns: '1.4fr 1fr 1fr', gap: 8 }}>
        {[
          { e: '📷', l: 'Foto', primary: true, go: () => nav.openModal(<AddMealModal open T={T} accent={T.accent}
              prefill={{ autoPhoto: true }}
              onClose={() => nav.closeModal()}
              onSave={() => { nav.closeModal(); nav.toast('Comida anotada 💗', { icon: '📸' }); window.dispatchEvent(new CustomEvent('pau:cache-updated')); }}/>) },
          { e: '＋', l: 'Anotar', go: () => nav.openModal(<AddMealModal open T={T} accent={T.accent}
              onClose={() => nav.closeModal()}
              onSave={() => { nav.closeModal(); nav.toast('Comida anotada 💗', { icon: '🍓' }); window.dispatchEvent(new CustomEvent('pau:cache-updated')); }}/>) },
          { e: '🛒', l: 'Compra', go: () => nav.go('lista-compra') },
          // 'Antojo dulce' retirado (2026-07-04, decisión de Pau): invitaba a
          // comer fuera del plan. El componente sigue en el código por si vuelve.
        ].map((a, i) => (
          <button key={i} onClick={a.go} style={{
            padding: '13px 6px', border: 'none', borderRadius: 20, cursor: 'pointer',
            background: a.primary ? T.accent : T.card,
            color: a.primary ? '#fff' : T.text,
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 5,
            fontFamily: 'inherit',
          }}>
            <span style={{ fontSize: 20 }}>{a.e}</span>
            <span style={{ fontSize: 9.5, fontWeight: 800, letterSpacing: 0.4,  }}>{a.l}</span>
          </button>
        ))}
      </div>

      {/* 3️⃣ TU PLAN de hoy (dashboard existente, con flechas por día) */}
      <ActivePlanDashboard T={T} nav={nav}/>

      {/* 4️⃣ EXPLORAR — el catálogo entero, ordenado y sin molestar */}
      <div style={{ padding: '28px 20px 0' }}>
        <div className="pf-display-bold" style={{ fontSize: 16, color: T.text, lineHeight: 1 }}>Explorar</div>
      </div>

      {hasActivePlan === false && (
      <div style={{
        margin: '12px 20px 0', padding: 4,
        background: T.subtle, borderRadius: 14, display: 'flex', position: 'relative',
      }}>
        <div style={{
          position: 'absolute', top: 4, bottom: 4,
          left: sub === 'planes' ? 4 : 'calc(50% + 0px)',
          width: 'calc(50% - 4px)',
          background: T.text, borderRadius: 11,
          transition: 'left 240ms cubic-bezier(.2,.8,.2,1)',
        }}/>
        {[
          { id: 'planes', label: 'Planes' },
          { id: 'recetas', label: 'Recetas' },
        ].map(s => (
          <button key={s.id} onClick={() => setSub(s.id)} style={{
            flex: 1, padding: '9px 6px', border: 'none', background: 'transparent', cursor: 'pointer',
            position: 'relative', zIndex: 1,
            fontSize: 11.5, fontWeight: 800, letterSpacing: 0.3,
            color: sub === s.id ? T.card : T.muted,
          }}>{s.label}</button>
        ))}
      </div>

      )}
      {hasActivePlan === false && sub === 'planes' && <PlanesList T={T} isPremium={isPremium} nav={nav} searchQ={searchQ}/>}
      {(hasActivePlan || sub === 'recetas') && <RecetasList T={T} nav={nav} searchQ={searchQ}/>}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// ActivePlanDashboard — vista estilo Grow with Anna del plan activo
// Hero: "Tu plan · día N" con flechas anterior/siguiente
// Grid: cards visuales por comida (con emoji grande de fondo)
// Click en una comida → ver receta completa
// ─────────────────────────────────────────────────────────────
function ActivePlanDashboard({ T, nav }) {
  const [plan, setPlan] = useState(null); // { planRow, days: [{day_number, meals}], currentDay }
  const [viewDay, setViewDay] = useState(1);
  const [showOpts, setShowOpts] = useState(false); // gestión mensual, colapsada
  const [loading, setLoading] = useState(true);
  // Recargar al recibir pau:cache-updated (tras generar plan / actualizar)
  const [reloadTick, setReloadTick] = useState(0);
  useEffect(() => {
    const handler = () => setReloadTick(x => x + 1);
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);

  useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setLoading(false); return; }
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) { setLoading(false); return; }
        // 1) Plan activo de la usuaria
        const { data: enrollments } = await sb
          .from('user_planes')
          .select('id, plan_id, current_day, enrolled_at')
          .eq('user_id', user.id)
          .is('completed_at', null)
          .order('enrolled_at', { ascending: false })
          .limit(1);
        const enrollment = enrollments?.[0];
        if (!enrollment) { setPlan(null); setLoading(false); return; }
        // 2) Datos del plan (puede ser catálogo o personal)
        const { data: planRow } = await sb
          .from('planes')
          .select('id, title, description, days, kcal_per_day, color, bg_color, sample_day, meals')
          .eq('id', enrollment.plan_id)
          .maybeSingle();
        // 3) Días del plan
        const { data: planDays } = await sb
          .from('plan_days')
          .select('day_number, title, meals')
          .eq('plan_id', enrollment.plan_id)
          .order('day_number', { ascending: true });

        // 4) Enriquecer cada meal con su image_url de cache (recipe_images)
        const allKeys = new Set();
        (planDays || []).forEach(d => {
          (d.meals || []).forEach(m => {
            const k = (m?.name || '').toLowerCase()
              .normalize('NFD').replace(/[̀-ͯ]/g, '')
              .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80);
            if (k) allKeys.add(k);
          });
        });
        if (allKeys.size > 0) {
          const { data: images } = await sb.from('recipe_images')
            .select('recipe_key, image_url')
            .in('recipe_key', [...allKeys]);
          const byKey = {};
          (images || []).forEach(i => { byKey[i.recipe_key] = i.image_url; });
          (planDays || []).forEach(d => {
            (d.meals || []).forEach(m => {
              const k = (m?.name || '').toLowerCase()
                .normalize('NFD').replace(/[̀-ͯ]/g, '')
                .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80);
              if (byKey[k]) m.image_url = byKey[k];
            });
          });
        }

        // ── Día actual SIEMPRE basado en fecha real (no en current_day estático)
        // Esto mantiene sincronizado home, comidas y calendario. Si han pasado
        // 3 días desde la inscripción, hoy = día 4 (independiente de qué hayas
        // completado). Las comidas avanzan con el calendario.
        const totalDays = planRow?.days || (planDays?.length || 7);
        const start = new Date(enrollment.enrolled_at);
        const today = new Date();
        const daysElapsed = Math.floor((today - start) / 86400000);
        const todayDay = Math.max(1, Math.min(totalDays, daysElapsed + 1));
        // Comidas YA registradas hoy → para marcar ✓ y no duplicar
        const dayStart = new Date(); dayStart.setHours(0, 0, 0, 0);
        const { data: todayLogs } = await sb.from('meal_logs')
          .select('meal_name, meal_type')
          .eq('user_id', user.id)
          .gte('logged_at', dayStart.toISOString());
        setPlan({
          enrollment, planRow,
          days: planDays || [],
          currentDay: todayDay,
          totalDays,
          todayLogs: todayLogs || [],
        });
        setViewDay(todayDay);
      } catch (e) {
        console.warn('[active-plan]', e?.message);
      }
      setLoading(false);
    })();
  }, [reloadTick]);

  // No mostrar nada si está cargando o no hay plan activo
  if (loading || !plan || !plan.days || plan.days.length === 0) return null;

  const dayData = plan.days.find(d => d.day_number === viewDay) || plan.days[0];
  const meals = Array.isArray(dayData?.meals) ? dayData.meals : [];
  const startDate = plan.enrollment.enrolled_at ? new Date(plan.enrollment.enrolled_at) : new Date();
  const thisDayDate = new Date(startDate.getTime() + (viewDay - 1) * 86400000);
  const dateLabel = thisDayDate.toLocaleDateString('es', { weekday: 'short', day: 'numeric', month: 'short' }).toUpperCase();
  const planColor = plan.planRow?.color || T.accent;

  // Emoji por tipo de comida (mealType)
  const typeEmojis = {
    'Desayuno': '🥣', 'Breakfast': '🥣',
    'Snack': '🍎', 'Snack': '🍎', 'Merienda': '🍓',
    'Comida': '🥗', 'Lunch': '🥗', 'Almuerzo': '🥗',
    'Cena': '🌙', 'Dinner': '🌙',
  };

  // ¿Esta comida del plan ya fue registrada HOY? (por nombre o tipo)
  const normLog = (s) => String(s || '').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '').trim();
  function mealAlreadyLogged(m, rawLabel) {
    const type = normLog(m.mealType || rawLabel);
    const nm = normLog(m.name);
    return (plan.todayLogs || []).some(l => {
      const lt = normLog(l.meal_type); const ln = normLog(l.meal_name);
      if (nm && ln && (ln.includes(nm.slice(0, 14)) || nm.includes(ln.slice(0, 14)))) return true;
      return !!type && lt === type;
    });
  }

  return (
    <div style={{ padding: '28px 20px 0' }}>
      {/* Header minimal Anna — navegación día sin caja gigante */}
      <div style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        marginBottom: 4,
      }}>
        <button onClick={() => setViewDay(d => Math.max(1, d - 1))} disabled={viewDay <= 1} style={{
          width: 36, height: 36, borderRadius: 999, border: 'none', cursor: viewDay <= 1 ? 'default' : 'pointer',
          background: T.subtle, color: T.text, fontSize: 16, fontWeight: 700,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          opacity: viewDay <= 1 ? 0.4 : 1,
        }}>‹</button>
        <div style={{ textAlign: 'center', flex: 1 }}>
          <div style={{
            fontSize: 13, fontWeight: 700, letterSpacing: -0.1,
            color: viewDay === plan.currentDay ? T.accent : T.text,
          }}>
            Día {viewDay} de {plan.totalDays} · {dateLabel.toLowerCase().charAt(0).toUpperCase() + dateLabel.toLowerCase().slice(1)}{viewDay === plan.currentDay ? ' · HOY' : ''}
          </div>
          {viewDay !== plan.currentDay && (
            <button onClick={() => setViewDay(plan.currentDay)} style={{
              marginTop: 3, border: 'none', background: 'transparent', cursor: 'pointer',
              fontSize: 10.5, fontWeight: 700, color: T.accent, padding: 0, fontFamily: 'inherit',
            }}>Volver a hoy →</button>
          )}
        </div>
        <button onClick={() => setViewDay(d => Math.min(plan.totalDays, d + 1))} disabled={viewDay >= plan.totalDays} style={{
          width: 36, height: 36, borderRadius: 999, border: 'none', cursor: viewDay >= plan.totalDays ? 'default' : 'pointer',
          background: T.subtle, color: T.text, fontSize: 16, fontWeight: 700,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          opacity: viewDay >= plan.totalDays ? 0.4 : 1,
        }}>›</button>
      </div>

      {/* Grid Anna minimal Apple-style — foto limpia, etiqueta debajo fina */}
      {meals.length > 0 && (
        <div style={{ marginTop: 18, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
          {meals.map((m, i) => {
            const emoji = m.emoji || typeEmojis[m.mealType] || '🍽️';
            const rawLabel = m.mealType || ['Desayuno','Almuerzo','Snack','Cena','Snack'][i] || 'Almuerzo';
            const isToday = viewDay === plan.currentDay;
            const done = isToday && mealAlreadyLogged(m, rawLabel);
            return (
              <button key={i} onClick={() => {
                if (nav.openModal) {
                  nav.openModal(<MealRecipeModal T={T} meal={m} nav={nav} readOnly={!isToday} alreadyLogged={done} onClose={() => nav.closeModal()}/>);
                }
              }} style={{
                cursor: 'pointer', border: 'none', padding: 0,
                background: 'transparent', fontFamily: 'inherit',
                display: 'flex', flexDirection: 'column', gap: 10, textAlign: 'left',
              }}>
                <div style={{
                  position: 'relative', aspectRatio: '3 / 4',
                  borderRadius: 18, overflow: 'hidden',
                  background: m.image_url ? '#FFFFFF' : `linear-gradient(135deg, ${planColor}15, ${planColor}05)`,
                }}>
                  {m.image_url ? (
                    <img src={m.image_url} alt={rawLabel} style={{
                      position: 'absolute', inset: 0,
                      width: '100%', height: '100%', objectFit: 'cover',
                      filter: done ? 'saturate(0.75) brightness(0.92)' : 'none',
                    }}/>
                  ) : (
                    <div style={{
                      position: 'absolute', inset: 0,
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      fontSize: 56, opacity: done ? 0.5 : 0.85,
                    }}>{emoji}</div>
                  )}
                  {done && (
                    <div style={{
                      position: 'absolute', top: 8, right: 8,
                      width: 26, height: 26, borderRadius: 999,
                      background: T.accent, color: '#fff',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      fontSize: 13, fontWeight: 800,
                      boxShadow: '0 2px 8px rgba(0,0,0,0.25)',
                    }}>✓</div>
                  )}
                </div>
                <div style={{ paddingLeft: 2 }}>
                  <div style={{
                    fontSize: 10, fontWeight: 600, color: done ? T.accent : T.muted,
                    letterSpacing: 0.5, 
                  }}>{done ? `✓ ${rawLabel} · hecha` : rawLabel}</div>
                  <div style={{
                    fontSize: 13, fontWeight: 600, color: T.text,
                    letterSpacing: -0.2, lineHeight: 1.25, marginTop: 3,
                    display: '-webkit-box', WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
                    overflow: 'hidden',
                  }}>{m.name || 'Comida'}</div>
                </div>
              </button>
            );
          })}
        </div>
      )}

      {/* CTA de lista de compra retirado: duplicaba la acción rápida 🛒 Compra */}

      {/* Gestión del plan (mensual) — colapsada para no pesar en el día a día */}
      <button onClick={() => setShowOpts(o => !o)} style={{
        marginTop: 16, border: 'none', background: 'transparent', cursor: 'pointer',
        fontFamily: 'inherit', fontSize: 11, fontWeight: 700, color: T.muted,
        letterSpacing: 0.4, padding: '4px 0',
      }}>{showOpts ? 'Ocultar opciones del plan ▴' : 'Opciones del plan ▾'}</button>
      {showOpts && (
      <div style={{ marginTop: 6, display: 'flex', flexDirection: 'column', gap: 8 }}>
        <button onClick={async () => {
          // Avisar a la usuaria que regenerar usa una de sus generaciones del mes
          let quotaWarning = '';
          try {
            const sb = window.__PAU_SUPABASE;
            const { data: session } = await sb.auth.getSession();
            const token = session?.session?.access_token;
            const headers = {};
            if (token) headers['Authorization'] = 'Bearer ' + String(token).trim().replace(/[^\x21-\x7E]/g, '');
            const res = await fetch('/api/plans/quota', { headers });
            if (res.ok) {
              const q = await res.json();
              if (!q.isGodmode) {
                if (q.remaining <= 0) {
                  alert(`Has llegado al límite de ${q.max} planes este mes.\nPróxima generación: ${new Date(q.resetAt).toLocaleDateString('es', { day: 'numeric', month: 'long' })}.`);
                  return;
                }
                quotaWarning = `\n\nVas a usar 1 de tus ${q.max} planes del mes (te quedarán ${q.remaining - 1}).`;
              }
            }
          } catch {}
          if (!window.confirm('¿Regenerar plan?\nCreamos uno nuevo con la IA. El actual se guarda en tu historial.' + quotaWarning)) return;
          if (nav.openModal) nav.openModal(<AIPlanWizard T={T} nav={nav} onClose={() => nav.closeModal()}/>);
        }} style={{
          padding: '14px 16px', border: T.border, borderRadius: 14, cursor: 'pointer',
          background: T.card, color: T.text,
          display: 'flex', alignItems: 'center', gap: 12, fontFamily: 'inherit', textAlign: 'left',
        }}>
          <span style={{ flex: 1, fontSize: 13, fontWeight: 700 }}>Regenerar plan</span>
          {Icon.chevR(T.muted, 8)}
        </button>
        <button onClick={async () => {
          if (!window.confirm('¿Salir del plan actual?\nSe guarda en tu historial, puedes volver a activarlo después.')) return;
          try {
            const sb = window.__PAU_SUPABASE;
            const { data: { user } } = await sb.auth.getUser();
            if (!user) { nav.toast('Inicia sesión', { icon: '🔒' }); return; }
            await sb.from('user_planes')
              .update({ completed_at: new Date().toISOString() })
              .eq('user_id', user.id)
              .is('completed_at', null);
            nav.toast('Saliste del plan — guardado en historial 💗', { icon: '👋' });
            window.dispatchEvent(new CustomEvent('pau:cache-updated'));
          } catch (e) {
            console.warn('[salir-plan]', e?.message || e);
            nav.toast('Algo salió mal, inténtalo de nuevo', { icon: '⚠️' });
          }
        }} style={{
          padding: '14px 16px', border: T.border, borderRadius: 14, cursor: 'pointer',
          background: T.card, color: T.text,
          display: 'flex', alignItems: 'center', gap: 12, fontFamily: 'inherit', textAlign: 'left',
        }}>
          <span style={{ flex: 1, fontSize: 13, fontWeight: 700 }}>Salir del plan</span>
          {Icon.chevR(T.muted, 8)}
        </button>
      </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// MealRecipeModal — receta detallada con checkboxes ingredientes y pasos
// Click "Listo" → registra la comida en meal_logs
// ─────────────────────────────────────────────────────────────
// Lee las preferencias de visualización de macros del localStorage.
// Devuelve { kcal, protein, carbs, fat, any } — `any` = true si alguna está ON.
// Default: todas ON. Si existe el flag legacy 'paufit-show-macros'=0, todas OFF.
function useShowMacros() {
  const DEFAULTS = { kcal: true, protein: true, carbs: true, fat: true };
  const [prefs, setPrefs] = useState(DEFAULTS);
  useEffect(() => {
    function read() {
      try {
        // 1) Preferencias nuevas granulares
        const raw = localStorage.getItem('paufit-macros-prefs');
        if (raw) {
          const parsed = JSON.parse(raw);
          setPrefs({ ...DEFAULTS, ...parsed });
          return;
        }
        // 2) Migrar legacy (toggle único on/off)
        const legacy = localStorage.getItem('paufit-show-macros');
        if (legacy !== null) {
          const all = legacy === '1';
          setPrefs({ kcal: all, protein: all, carbs: all, fat: all });
          return;
        }
        setPrefs(DEFAULTS);
      } catch {}
    }
    read();
    const handler = () => read();
    window.addEventListener('pau:settings-updated', handler);
    window.addEventListener('storage', handler);
    return () => {
      window.removeEventListener('pau:settings-updated', handler);
      window.removeEventListener('storage', handler);
    };
  }, []);
  const any = prefs.kcal || prefs.protein || prefs.carbs || prefs.fat;
  return { ...prefs, any };
}

// Fallback: si la receta no trae macros, los estimamos desde kcal con ratios típicos Pau Fit
// (30% proteína · 40% carbs · 30% grasa) — solo aproximado, para mostrar algo coherente.
function ensureMacros(meal) {
  if (!meal || !meal.kcal) return meal;
  const hasMacros = meal.protein_g || meal.carbs_g || meal.fat_g;
  if (hasMacros) return meal;
  const kcal = meal.kcal;
  return {
    ...meal,
    protein_g: Math.round((kcal * 0.30) / 4),
    carbs_g: Math.round((kcal * 0.40) / 4),
    fat_g: Math.round((kcal * 0.30) / 9),
    _macrosEstimated: true,
  };
}

function MealRecipeModal({ T, meal: rawMeal, onClose, nav, readOnly = false, alreadyLogged = false }) {
  // Permitir registrar de nuevo solo si la usuaria lo pide explícitamente
  const [logAgain, setLogAgain] = useState(false);
  const macrosPrefs = useShowMacros();
  // Aplicar ajustes de cantidad ANTES de estimar macros — si Pau cambió
  // "200g pollo → 100g pollo" las kcal se recalculan proporcionalmente.
  const adjusted = (window.PauIngredientTweaks && window.PauIngredientTweaks.applyRecipeAdjustments)
    ? window.PauIngredientTweaks.applyRecipeAdjustments(rawMeal)
    : rawMeal;
  const meal = ensureMacros(adjusted);
  const ingredients = Array.isArray(meal.ingredients) ? meal.ingredients : [];
  // Dividir instrucciones en pasos por punto + número, o por líneas
  const rawInstr = meal.instructions || '';
  const steps = rawInstr
    .split(/(?:\d+[\.\)]\s+|\n+|\.\s+)/)
    .map(s => s.trim())
    .filter(s => s.length > 5);
  const [ingChecked, setIngChecked] = useState(new Set());
  const [stepChecked, setStepChecked] = useState(new Set());
  const [saving, setSaving] = useState(false);
  const [imageUrl, setImageUrl] = useState(meal.image_url || null);

  // ── Ajustes por ingrediente (lo tengo / cambiar cantidad / sustituir) ──
  // Persisten en localStorage por receta vía window.PauIngredientTweaks.
  // Se reflejan automáticamente en la lista de la compra.
  const [tweaks, setTweaks] = useState(() => window.PauIngredientTweaks?.read(meal.name) || {});
  const [openTweakIdx, setOpenTweakIdx] = useState(null);
  const [editingIdx, setEditingIdx] = useState(null);
  const [editValue, setEditValue] = useState('');
  useEffect(() => {
    const h = () => setTweaks(window.PauIngredientTweaks?.read(meal.name) || {});
    window.addEventListener('pau:ing-tweaks-updated', h);
    return () => window.removeEventListener('pau:ing-tweaks-updated', h);
  }, [meal.name]);
  function setHave(idx, have) {
    window.PauIngredientTweaks?.update(meal.name, idx, { have });
  }
  function setOverride(idx, override) {
    window.PauIngredientTweaks?.update(meal.name, idx, { override: override || null });
  }
  function clearTweak(idx) {
    window.PauIngredientTweaks?.clear(meal.name, idx);
  }

  const [imgLoading, setImgLoading] = useState(false);
  const [imgError, setImgError] = useState(null); // mensaje de error visible

  async function tryGenerateImage(forceRegen = false) {
    const sb = window.__PAU_SUPABASE;
    if (!sb || !meal.name) return;
    setImgError(null);
    setImgLoading(true);
    try {
      const { data: session } = await sb.auth.getSession();
      const token = session?.session?.access_token;
      const headers = { 'Content-Type': 'application/json' };
      if (token) headers['Authorization'] = 'Bearer ' + String(token).trim().replace(/[^\x21-\x7E]/g, '');
      const res = await fetch('/api/recipes/image', {
        method: 'POST', headers,
        body: JSON.stringify({ name: meal.name, description: meal.instructions || '', force: forceRegen }),
      });
      const j = await res.json().catch(() => ({}));
      if (j?.image_url) {
        setImageUrl(j.image_url);
        setImgError(null);
      } else if (j?.error) {
        setImgError(j.error);
        console.warn('[recipe-image] error:', j.error);
      } else {
        setImgError('No se pudo generar la foto');
      }
    } catch (e) {
      setImgError(e?.message || 'Error de conexión');
    }
    setImgLoading(false);
  }
  // ¿Está ya guardada como favorita?
  const favKey = (meal.name || '').toLowerCase().trim();
  const [isFav, setIsFav] = useState(() => {
    try {
      const arr = JSON.parse(localStorage.getItem('paufit-favs-recetas-custom') || '[]');
      return Array.isArray(arr) && arr.some(r => (r.name || '').toLowerCase().trim() === favKey);
    } catch { return false; }
  });
  function toggleFav() {
    try {
      const arr = JSON.parse(localStorage.getItem('paufit-favs-recetas-custom') || '[]');
      const list = Array.isArray(arr) ? arr : [];
      const exists = list.findIndex(r => (r.name || '').toLowerCase().trim() === favKey);
      if (exists >= 0) {
        list.splice(exists, 1);
        localStorage.setItem('paufit-favs-recetas-custom', JSON.stringify(list));
        setIsFav(false);
        if (nav?.toast) nav.toast('Quitada de favoritas', { icon: '🤍' });
      } else {
        list.unshift({
          name: meal.name,
          emoji: meal.emoji || '🍓',
          kcal: meal.kcal || null,
          mealType: meal.mealType || '',
          ingredients: Array.isArray(meal.ingredients) ? meal.ingredients : [],
          instructions: meal.instructions || '',
          image_url: imageUrl || null,
          saved_at: new Date().toISOString(),
        });
        // Limitar a 50 para no llenar localStorage
        if (list.length > 50) list.length = 50;
        localStorage.setItem('paufit-favs-recetas-custom', JSON.stringify(list));
        setIsFav(true);
        if (nav?.toast) nav.toast('Guardada en favoritas 💗', { icon: '❤️' });
      }
    } catch (e) {
      console.warn('[fav] toggle failed:', e?.message);
    }
  }

  // Intentar cargar foto de cache primero, si no, generar con IA
  useEffect(() => {
    if (imageUrl) return;
    const sb = window.__PAU_SUPABASE;
    if (!sb || !meal.name) return;
    let cancel = false;
    (async () => {
      try {
        const key = meal.name.toLowerCase()
          .normalize('NFD').replace(/[̀-ͯ]/g, '')
          .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80);
        // 1) Cache lookup directo (no requiere auth, RLS allows)
        const { data: cached } = await sb.from('recipe_images').select('image_url').eq('recipe_key', key).maybeSingle();
        if (cached?.image_url) {
          if (!cancel) setImageUrl(cached.image_url);
          return;
        }
        if (cancel) return;
        // 2) No está en cache → llamar a la API para generar
        await tryGenerateImage(false);
      } catch (e) {
        console.warn('[recipe-image] cache lookup failed:', e?.message);
        if (!cancel) setImgError('No se pudo conectar con Supabase');
      }
    })();
    return () => { cancel = true; };
  }, [meal.name, imageUrl]);

  async function markCompleted() {
    setSaving(true);
    try {
      const sb = window.__PAU_SUPABASE;
      if (!sb) throw new Error('Sin conexión con el servidor');
      const { data: { user } } = await sb.auth.getUser();
      if (!user) {
        setSaving(false);
        if (nav?.toast) nav.toast('Tu sesión expiró — cierra sesión y vuelve a entrar', { icon: '🔒' });
        return;
      }
      // Mapear mealType a los valores estándar
      const mt = (meal.mealType || '').toLowerCase();
      let mealKind = 'snack';
      if (mt.includes('desayuno')) mealKind = 'desayuno';
      else if (mt.includes('comida') || mt.includes('almuerzo')) mealKind = 'almuerzo';
      else if (mt.includes('cena')) mealKind = 'cena';
      // Guardar CON macros — así el anillo del día suma de verdad
      const withMacros = ensureMacros(meal);
      const { error: dbErr } = await sb.from('meal_logs').insert({
        user_id: user.id,
        meal_name: meal.name,
        meal_type: mealKind,
        logged_at: new Date().toISOString(),
        kcal: withMacros.kcal ?? null,
        protein_g: withMacros.protein_g ?? null,
        carbs_g: withMacros.carbs_g ?? null,
        fat_g: withMacros.fat_g ?? null,
        photo_url: meal.image_url || null,
      });
      if (dbErr) throw dbErr;
      try { window.PauUserRetos?.awardPoints?.(5, 'meal'); } catch {}
      if (nav?.toast) nav.toast('Comida añadida al registro 💗', { icon: '✓' });
      window.dispatchEvent(new CustomEvent('pau:cache-updated'));
      setSaving(false);
      onClose();
    } catch (e) {
      setSaving(false);
      if (nav?.toast) nav.toast('No se pudo guardar: ' + (e?.message || 'error'), { icon: '⚠️' });
      // No cerramos: que la usuaria pueda reintentar
    }
  }

  function toggleIng(i) {
    setIngChecked(s => { const n = new Set(s); n.has(i) ? n.delete(i) : n.add(i); return n; });
  }
  function toggleStep(i) {
    setStepChecked(s => { const n = new Set(s); n.has(i) ? n.delete(i) : n.add(i); return n; });
  }

  const allDone = ingredients.length > 0 && ingChecked.size >= ingredients.length
    && steps.length > 0 && stepChecked.size >= steps.length;

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 9999,
      background: 'rgba(0,0,0,0.6)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
      animation: 'fade-in 240ms ease',
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', maxWidth: 480, maxHeight: '92dvh', overflowY: 'auto',
        background: T.card, color: T.text,
        borderTopLeftRadius: 28, borderTopRightRadius: 28,
        padding: 'calc(16px + env(safe-area-inset-bottom))',
        animation: 'slide-up 380ms cubic-bezier(.34,1.56,.64,1)',
      }}>
        <div style={{ width: 38, height: 4, borderRadius: 2, background: T.muted, opacity: 0.35, margin: '0 auto 14px' }}/>

        {/* Hero: foto si la tenemos, sino emoji grande con loader si generando.
            Aspect 4:5 para coincidir con las fotos generadas estilo Instagram. */}
        <div style={{
          aspectRatio: '4 / 5', borderRadius: 18, overflow: 'hidden',
          background: imageUrl ? '#f5e8df' : `linear-gradient(135deg, ${T.accent}30, ${T.accent}10)`,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          marginBottom: 18, position: 'relative',
        }}>
          {imageUrl
            ? <img src={imageUrl} alt={meal.name} style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
            : (
              <>
                <span style={{ fontSize: 100, opacity: imgLoading ? 0.4 : 1 }}>{meal.emoji || '🍽️'}</span>
                {imgLoading && (
                  <div style={{
                    position: 'absolute', bottom: 12, left: '50%', transform: 'translateX(-50%)',
                    padding: '6px 14px', borderRadius: 999,
                    background: 'rgba(255,255,255,0.9)', color: T.text,
                    fontSize: 11, fontWeight: 700, letterSpacing: 0.3,
                    display: 'flex', alignItems: 'center', gap: 8,
                  }}>
                    <span style={{
                      width: 12, height: 12, borderRadius: 50,
                      border: '2px solid rgba(0,0,0,0.15)', borderTopColor: T.accent,
                      animation: 'spin 700ms linear infinite',
                    }}/>
                    Generando foto…
                  </div>
                )}
                {/* Error visible + botón retry */}
                {!imgLoading && imgError && (
                  <div style={{
                    position: 'absolute', bottom: 10, left: 10, right: 10,
                    padding: '10px 12px', borderRadius: 12,
                    background: 'rgba(255,255,255,0.95)', color: '#7A2222',
                    fontSize: 11, fontWeight: 600, lineHeight: 1.35,
                    display: 'flex', alignItems: 'center', gap: 8,
                  }}>
                    <span style={{ fontSize: 16 }}>⚠️</span>
                    <span style={{ flex: 1 }}>{imgError}</span>
                    <button onClick={() => tryGenerateImage(true)} style={{
                      padding: '5px 10px', borderRadius: 999, border: 'none',
                      background: T.accent, color: '#fff', cursor: 'pointer',
                      fontSize: 10, fontWeight: 800, letterSpacing: 0.2,
                    }}>Reintentar</button>
                  </div>
                )}
              </>
            )
          }
        </div>

        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div className="pf-eyebrow" style={{ color: T.muted }}>{meal.mealType || 'Comida'}</div>
            <div className="pf-display" style={{ fontSize: 24, fontWeight: 700, color: T.text, letterSpacing: -0.4, marginTop: 4, lineHeight: 1.1 }}>
              {meal.name || 'Comida'}
            </div>
          </div>
          <button onClick={toggleFav} aria-label={isFav ? 'Quitar de favoritas' : 'Guardar como favorita'} style={{
            width: 42, height: 42, borderRadius: 999, flexShrink: 0,
            border: 'none', cursor: 'pointer',
            background: isFav ? `${T.accent}20` : T.subtle,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 19, transition: 'transform 200ms cubic-bezier(.34,1.56,.64,1), background 200ms',
            transform: isFav ? 'scale(1.06)' : 'scale(1)',
          }}>
            {isFav ? '❤️' : '🤍'}
          </button>
        </div>

        <div style={{ marginTop: 12, display: 'flex', gap: 12, fontSize: 12, color: T.muted, fontWeight: 600, flexWrap: 'wrap' }}>
          {macrosPrefs.kcal && meal.kcal && <span>🔥 {meal.kcal} kcal</span>}
          {meal.time && <span>⏱ {meal.time}</span>}
        </div>

        {/* MACROS — tile estilo apps pro (P / C / G en gramos) — respeta toggles individuales */}
        {(() => {
          const items = [
            macrosPrefs.protein && meal.protein_g != null && { l: 'Proteína', v: meal.protein_g, u: 'g', c: '#E89B7A' },
            macrosPrefs.carbs && meal.carbs_g != null && { l: 'Carbs', v: meal.carbs_g, u: 'g', c: '#C5B57F' },
            macrosPrefs.fat && meal.fat_g != null && { l: 'Grasas', v: meal.fat_g, u: 'g', c: '#9AB394' },
          ].filter(Boolean);
          if (items.length === 0) return null;
          return (
            <div style={{
              marginTop: 14, padding: '12px 14px',
              background: T.subtle, borderRadius: 14,
              display: 'grid', gridTemplateColumns: `repeat(${items.length}, 1fr)`, gap: 6,
              position: 'relative',
            }}>
              {items.map((m, i) => (
                <div key={i} style={{ textAlign: 'center' }}>
                  <div style={{ fontSize: 17, fontWeight: 800, color: T.text, letterSpacing: -0.3, lineHeight: 1 }}>
                    {m.v || 0}<span style={{ fontSize: 10, fontWeight: 600, color: T.muted, marginLeft: 1 }}>{m.u}</span>
                  </div>
                  <div style={{ fontSize: 9, fontWeight: 700, color: m.c, letterSpacing: 0.4,  marginTop: 3 }}>
                    {m.l}
                  </div>
                </div>
              ))}
              {meal._macrosEstimated && (
                <div style={{
                  position: 'absolute', top: 4, right: 8,
                  fontSize: 8, color: T.muted, fontWeight: 600, opacity: 0.7,
                }} title="Estimados desde kcal">~</div>
              )}
            </div>
          );
        })()}

        {/* APARTADO 1: Ingredientes con ajustes (tengo / cambiar / sustituir) */}
        {ingredients.length > 0 && (
          <div style={{ marginTop: 22 }}>
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10 }}>
              <div className="pf-display-bold" style={{ fontSize: 13, color: T.text }}>Ingredientes</div>
              <span style={{ fontSize: 11, color: T.muted, fontWeight: 700 }}>{ingChecked.size}/{ingredients.length}</span>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {ingredients.map((ing, i) => {
                const tw = tweaks[i] || {};
                const display = tw.override || ing;
                const isHave = !!tw.have;
                const isEdited = !!tw.override;
                const open = openTweakIdx === i;
                const editing = editingIdx === i;
                const done = ingChecked.has(i);
                return (
                  <div key={i} style={{
                    background: T.subtle, borderRadius: 14,
                    padding: editing ? '10px 10px' : '0',
                    transition: 'padding 180ms ease',
                  }}>
                    {editing ? (
                      // Modo edición — input para reemplazar el ingrediente
                      <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
                        <input
                          autoFocus value={editValue}
                          onChange={(e) => setEditValue(e.target.value)}
                          onKeyDown={(e) => {
                            if (e.key === 'Enter') { setOverride(i, editValue.trim()); setEditingIdx(null); setOpenTweakIdx(null); }
                            if (e.key === 'Escape') { setEditingIdx(null); }
                          }}
                          placeholder="Ej: 200g pollo o tofu"
                          style={{
                            flex: 1, padding: '12px 14px', border: 'none', borderRadius: 10,
                            background: T.card, color: T.text, fontSize: 13, fontFamily: 'inherit', outline: 'none',
                          }}
                        />
                        <button onClick={() => { setOverride(i, editValue.trim()); setEditingIdx(null); setOpenTweakIdx(null); }} style={{
                          padding: '12px 14px', border: 'none', borderRadius: 10,
                          background: T.accent, color: '#fff', cursor: 'pointer',
                          fontSize: 12, fontWeight: 800, fontFamily: 'inherit',
                        }}>OK</button>
                        <button onClick={() => setEditingIdx(null)} aria-label="Cancelar" style={{
                          width: 36, height: 36, border: 'none', borderRadius: 999,
                          background: 'transparent', color: T.muted, cursor: 'pointer', fontSize: 22,
                        }}>×</button>
                      </div>
                    ) : (
                      <>
                        {/* Fila ingrediente — tap toggles check, "···" abre menú */}
                        <div style={{
                          display: 'flex', alignItems: 'center', gap: 12,
                          padding: '12px 14px',
                        }}>
                          <button onClick={() => toggleIng(i)} aria-label="Marcar" style={{
                            width: 22, height: 22, borderRadius: 999,
                            border: done ? 'none' : `1.5px solid ${T.accent}50`,
                            background: done ? T.accent : 'transparent',
                            display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
                            color: '#fff', fontSize: 12, fontWeight: 800, cursor: 'pointer',
                          }}>{done && '✓'}</button>
                          <button onClick={() => toggleIng(i)} style={{
                            flex: 1, minWidth: 0, textAlign: 'left',
                            border: 'none', background: 'transparent', padding: 0, cursor: 'pointer', fontFamily: 'inherit',
                            display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6,
                          }}>
                            <span style={{
                              fontSize: 13, color: T.text,
                              textDecoration: (done || isHave) ? 'line-through' : 'none',
                              opacity: (done || isHave) ? 0.55 : 1,
                            }}>{display}</span>
                            {isHave && (
                              <span style={{
                                fontSize: 9, fontWeight: 800, letterSpacing: 0.4,
                                color: T.accent, 
                              }}>· Lo tengo</span>
                            )}
                            {isEdited && !isHave && (
                              <span style={{
                                fontSize: 9, fontWeight: 800, letterSpacing: 0.4,
                                color: T.accent, 
                              }}>· Editado</span>
                            )}
                          </button>
                          <button onClick={() => setOpenTweakIdx(open ? null : i)} aria-label="Ajustes" style={{
                            width: 28, height: 28, border: 'none', borderRadius: 999,
                            background: open ? `${T.accent}20` : 'transparent', cursor: 'pointer',
                            color: T.muted, fontSize: 16, lineHeight: 1,
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                          }}>{open ? '×' : '···'}</button>
                        </div>
                        {/* Menú simple al tocar "···": "ya lo tengo" +
                            alternativas de un toque (sin prompts del navegador) */}
                        {open && (() => {
                          const sug = window.PauSubstitutions && window.PauSubstitutions.suggest(display);
                          const opts = (sug?.options || []).slice(0, 3);
                          // Conservar la cantidad original al sustituir ("150g yogur" → "150g kéfir")
                          const qtyMatch = display.match(/^(\d+[.,]?\d*\s*\w*\s+)/);
                          const qtyPrefix = qtyMatch ? qtyMatch[1] : '';
                          const pill = (bg, color) => ({
                            padding: '9px 14px', border: 'none', borderRadius: 999,
                            background: bg, color,
                            fontSize: 11, fontWeight: 800, letterSpacing: 0.3, cursor: 'pointer', fontFamily: 'inherit',
                          });
                          return (
                            <div style={{ padding: '0 14px 12px' }}>
                              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                                <button onClick={() => { setHave(i, !isHave); setOpenTweakIdx(null); }}
                                  style={pill(isHave ? T.accent : T.card, isHave ? '#fff' : T.text)}>
                                  {isHave ? '✓ YA LO TENGO' : 'YA LO TENGO'}
                                </button>
                                {(isHave || isEdited) && (
                                  <button onClick={() => { clearTweak(i); setOpenTweakIdx(null); }}
                                    style={pill('transparent', T.muted)}>DESHACER</button>
                                )}
                              </div>
                              <div style={{
                                marginTop: 10, fontSize: 10, fontWeight: 800, letterSpacing: 0.6,
                                color: T.muted, 
                              }}>¿No lo tienes? Cámbialo por:</div>
                              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 6 }}>
                                {opts.map(o => (
                                  <button key={o} onClick={() => { setOverride(i, qtyPrefix + o); setOpenTweakIdx(null); }}
                                    style={pill(T.card, T.text)}>🔄 {o}</button>
                                ))}
                                <button onClick={() => { setEditValue(display); setEditingIdx(i); }}
                                  style={pill(T.card, T.text)}>✏️ Otro…</button>
                              </div>
                            </div>
                          );
                        })()}
                      </>
                    )}
                  </div>
                );
              })}
            </div>
            <div style={{
              marginTop: 8, padding: '0 4px',
              fontSize: 10, color: T.muted, fontWeight: 600, lineHeight: 1.5,
            }}>
              💡 ¿Te falta algo? Toca <strong>···</strong> y cámbialo por otra opción con un toque.
            </div>
          </div>
        )}

        {/* APARTADO 2: Pasos de preparación con checkboxes */}
        {steps.length > 0 && (
          <div style={{ marginTop: 22 }}>
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10 }}>
              <div className="pf-eyebrow" style={{ color: T.muted }}>Cómo prepararla</div>
              <span style={{ fontSize: 11, color: T.muted, fontWeight: 700 }}>{stepChecked.size}/{steps.length}</span>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {steps.map((s, i) => {
                const done = stepChecked.has(i);
                return (
                  <button key={i} onClick={() => toggleStep(i)} style={{
                    padding: '14px 16px', background: done ? `${T.accent}15` : T.subtle, borderRadius: 14,
                    fontSize: 13, color: T.text, lineHeight: 1.5,
                    border: 'none', cursor: 'pointer',
                    display: 'flex', alignItems: 'flex-start', gap: 12, textAlign: 'left', fontFamily: 'inherit',
                  }}>
                    <span style={{
                      width: 26, height: 26, borderRadius: 999,
                      background: done ? T.accent : T.card,
                      border: done ? 'none' : `1.5px solid ${T.accent}40`,
                      display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
                      color: done ? '#fff' : T.accent, fontSize: 12, fontWeight: 800,
                    }}>{done ? '✓' : (i + 1)}</span>
                    <span style={{
                      flex: 1, opacity: done ? 0.6 : 1,
                      textDecoration: done ? 'line-through' : 'none',
                    }}>{s.endsWith('.') ? s : s + '.'}</span>
                  </button>
                );
              })}
            </div>
          </div>
        )}

        {/* CTA — Listo (añadir al registro). En días que no son HOY, solo lectura:
            evita registrar la comida del día 4 cuando vas por el día 1. */}
        {readOnly ? (
          <div style={{
            marginTop: 24, padding: '14px 16px', borderRadius: 16,
            background: T.subtle, textAlign: 'center',
            fontFamily: 'Poppins', fontSize: 12.5, fontWeight: 600, color: T.muted,
          }}>
            👀 Estás viendo otro día del plan — podrás registrarla cuando te toque
          </div>
        ) : (alreadyLogged && !logAgain) ? (
          <div style={{ marginTop: 24 }}>
            <div style={{
              padding: '14px 16px', borderRadius: 16,
              background: `${T.accent}15`, textAlign: 'center',
              fontFamily: 'Poppins', fontSize: 13, fontWeight: 700, color: T.text,
            }}>
              ✓ Ya la registraste hoy
            </div>
            <button onClick={() => setLogAgain(true)} style={{
              marginTop: 8, width: '100%', padding: '10px',
              border: 'none', background: 'transparent', cursor: 'pointer',
              fontFamily: 'Poppins', fontSize: 11.5, fontWeight: 600, color: T.muted,
              textDecoration: 'underline',
            }}>¿La comiste otra vez? Registrar de nuevo</button>
          </div>
        ) : (
        <button onClick={markCompleted} disabled={saving} style={{
          marginTop: 24, width: '100%', padding: '16px',
          border: 'none', borderRadius: 16, cursor: saving ? 'wait' : 'pointer',
          background: T.accent, color: '#fff',
          fontFamily: 'Poppins', fontSize: 15, fontWeight: 700,
          boxShadow: `0 6px 18px ${T.accent}40`,
          opacity: saving ? 0.7 : 1,
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        }}>
          {saving ? 'Guardando…' : allDone ? '🎉 ¡Listo! Añadir al registro' : '✓ Listo · Añadir al registro'}
        </button>
        )}
        <button onClick={onClose} disabled={saving} style={{
          marginTop: 8, width: '100%', padding: '12px',
          border: 'none', borderRadius: 14, cursor: 'pointer',
          background: 'transparent', color: T.muted,
          fontFamily: 'Poppins', fontSize: 13, fontWeight: 600,
        }}>Cerrar</button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// AIPlanWizard — modal multi-paso para crear plan personalizado con IA
// Recoge: objetivo, dieta, restricciones, alergias, comidas/día,
//          tiempo cocina, gustos, días → POST /api/plans/generate
// ─────────────────────────────────────────────────────────────
function AIPlanWizard({ T, nav, onClose }) {
  const [step, setStep] = useState(1);
  // 12 pasos: 5 datos físicos (Cal AI style, uno por pantalla) + 7 preferencias.
  // 1=peso · 2=peso objetivo · 3=altura · 4=fecha nac · 5=actividad
  // 6=objetivo · 7=dieta · 8=alergias · 9=comidas/día · 10=cocina+cocinas · 11=situaciones · 12=días
  const TOTAL_STEPS = 12;
  // Pre-rellenar con datos del perfil si los tiene
  const _profile = (typeof window !== 'undefined' && window.__pauCache?.profile) || {};
  const [data, setData] = useState({
    // Datos físicos (Cal AI style) — sin pre-selección
    weightKg: _profile.weight_kg || '',
    goalWeightKg: '', // sin pre-seleccionar
    heightCm: _profile.height_cm || '',
    birthDate: _profile.birth_date || '',
    activity: '', // sin pre-seleccionar
    // Unidades preferidas (kg/lb · cm/ft) — valor siempre se guarda en métrico
    weightUnit: 'kg',
    heightUnit: 'cm',
    // Preferencias — sin pre-selección
    goal: '',
    pace: '', // sostenible | moderado | acelerado (solo para perder/ganar)
    dietType: '',
    restrictions: [],
    allergies: '',
    mealsPerDay: null,
    cookTime: '',
    cuisines: [],
    dislikes: '',
    weeks: 2, // duración del plan (1 / 2 / 4 semanas) — plazos cortos motivadores
    days: 14, // = weeks * 7, calculado al enviar
    situations: [], // array de opciones rápidas (menstrual, sop, etc)
    situationsText: '', // texto libre adicional
  });

  // Validación de seguridad médica — bloquea metas peligrosas
  // Ej: 20kg con 160cm = IMC 7.8 → totalmente patológico, BLOQUEADO.
  function isGoalWeightSafe(weight, heightCm) {
    if (!weight || !heightCm) return true;
    const h = parseFloat(heightCm) / 100;
    const bmi = parseFloat(weight) / (h * h);
    // IMC < 16 = delgadez severa (riesgo médico) → siempre bloqueado.
    if (bmi < 16) return false;
    // IMC > 40: permitido SOLO si la meta baja respecto al peso actual
    // (perder peso en dirección al rango sano siempre es válido, aunque
    // la meta intermedia siga por encima). Subir o mantener ahí → bloqueado.
    if (bmi > 40) {
      const current = parseFloat(data.weightKg);
      return !!current && parseFloat(weight) < current;
    }
    return true;
  }

  // Validación por paso — solo dejas avanzar si has rellenado lo importante
  // y si la meta es médicamente segura.
  function canAdvance(currentStep) {
    if (currentStep === 1) return !!data.weightKg;
    if (currentStep === 2) return !!data.heightCm;
    if (currentStep === 3) {
      if (!data.goalWeightKg) return false;
      return isGoalWeightSafe(data.goalWeightKg, data.heightCm);
    }
    if (currentStep === 4) return !!data.birthDate;
    if (currentStep === 5) return !!data.activity;
    if (currentStep === 6) {
      if (!data.goal) return false;
      // Si la meta tiene velocidad ajustable, requiere ritmo
      if ((data.goal === 'perder' || data.goal === 'ganar') && !data.pace) return false;
      return true;
    }
    if (currentStep === 7) return !!data.dietType;
    if (currentStep === 8) return true; // alergias opcionales
    if (currentStep === 9) return !!data.mealsPerDay && !!data.cookTime;
    if (currentStep === 10) return true; // cocinas opcionales
    if (currentStep === 11) return true; // situaciones opcionales
    if (currentStep === 12) return true; // días seleccionable
    return true;
  }
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState(null);
  const [quota, setQuota] = useState(null); // { used, max, remaining, isGodmode, resetAt }
  // Estado de generación de fotos del plan (segunda fase, después del plan)
  const [photosLoading, setPhotosLoading] = useState(false);
  const [photosProgress, setPhotosProgress] = useState({ done: 0, total: 0 });

  // Genera TODAS las fotos del plan antes de entregarlo.
  // PRIMERO chequea cuáles ya están en cache (gratis instantáneo).
  // Las que falten las pide de 5 en 5 con retry agresivo si throttle.
  // Garantiza que cada foto se intenta hasta 5 veces antes de rendirse.
  // FINAL: hace 1 pasada extra para las que aún no estén en cache (limpieza).
  async function generateAllPlanPhotos(plan, headers) {
    const allMeals = [];
    const seen = new Set(); // dedupe por nombre — si una receta se repite, una sola foto
    (plan?.dailyPlan || []).forEach(d => {
      (d.meals || []).forEach(m => {
        const key = (m?.name || '').toLowerCase().trim();
        if (!m?.name || seen.has(key)) return;
        seen.add(key);
        allMeals.push(m);
      });
    });
    const total = allMeals.length;
    if (total === 0) return;
    setPhotosProgress({ done: 0, total });

    const sb = window.__PAU_SUPABASE;
    const normalize = (name) => name.toLowerCase()
      .normalize('NFD').replace(/[̀-ͯ]/g, '')
      .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80);

    async function checkCacheKeys() {
      const keys = allMeals.map(m => normalize(m.name)).filter(Boolean);
      if (!sb || keys.length === 0) return new Set();
      try {
        const { data: cached } = await sb.from('recipe_images')
          .select('recipe_key')
          .in('recipe_key', keys);
        return new Set((cached || []).map(c => c.recipe_key));
      } catch { return new Set(); }
    }

    // Pasada inicial: ¿qué ya tenemos en cache?
    let cachedKeys = await checkCacheKeys();
    let needed = allMeals.filter(m => !cachedKeys.has(normalize(m.name)));
    let doneCount = total - needed.length;
    setPhotosProgress({ done: doneCount, total });

    if (needed.length === 0) return; // todo en cache, listo

    async function generateOne(m, attempt = 1) {
      try {
        const res = await fetch('/api/recipes/image', {
          method: 'POST', headers,
          body: JSON.stringify({ name: m.name, description: m.instructions || '' }),
        });
        const j = await res.json().catch(() => ({}));
        if (j?.image_url) return true;
        // Si throttle (Replicate rate limit), esperar y reintentar (max 5)
        const throttled = j?.error && /throttle|rate.?limit|429/i.test(j.error);
        if (throttled && attempt < 5) {
          const waitMs = 6000 * attempt; // 6s, 12s, 18s, 24s backoff
          console.warn(`[plan-photos] throttled, retry ${attempt}/5 en ${waitMs}ms…`, m.name);
          await new Promise(r => setTimeout(r, waitMs));
          return generateOne(m, attempt + 1);
        }
        return false;
      } catch (e) {
        console.warn('[plan-photos] foto fallida:', m.name, e?.message);
        return false;
      }
    }

    // Pasada 1: TODAS en paralelo (Promise.all sin chunks).
    // Con $5+ credit Replicate permite burst alto. El retry maneja
    // las que casualmente caigan en throttle pasajero.
    await Promise.all(needed.map(async (m) => {
      await generateOne(m);
      doneCount++;
      setPhotosProgress({ done: doneCount, total });
    }));

    // Pasada 2 (limpieza): re-chequear cache, las que aún no estén → último intento
    try {
      cachedKeys = await checkCacheKeys();
      const stillMissing = needed.filter(m => !cachedKeys.has(normalize(m.name)));
      if (stillMissing.length > 0) {
        console.warn(`[plan-photos] limpieza: faltan ${stillMissing.length}, último intento`);
        // Serial para no triggear throttle
        for (const m of stillMissing) {
          await generateOne(m);
          // No incrementamos doneCount (ya estaba contado), solo re-intentamos
        }
      }
    } catch {}
  }

  // Fetch quota al abrir el wizard
  useEffect(() => {
    (async () => {
      try {
        const sb = window.__PAU_SUPABASE;
        if (!sb) return;
        const { data: session } = await sb.auth.getSession();
        const token = session?.session?.access_token;
        const headers = {};
        if (token) headers['Authorization'] = 'Bearer ' + String(token).trim().replace(/[^\x21-\x7E]/g, '');
        const res = await fetch('/api/plans/quota', { headers });
        if (res.ok) {
          const j = await res.json();
          setQuota(j);
        }
      } catch {}
    })();
  }, []);

  const quotaReached = quota && !quota.isGodmode && quota.remaining <= 0;

  function setField(k, v) { setData(d => ({ ...d, [k]: v })); }
  function toggleArr(k, v) {
    setData(d => {
      const cur = new Set(d[k] || []);
      if (cur.has(v)) cur.delete(v); else cur.add(v);
      return { ...d, [k]: [...cur] };
    });
  }

  async function generate() {
    setLoading(true);
    // Marcar que hay un plan en proceso — para recovery si se cierra la app
    try {
      localStorage.setItem('paufit-plan-generating', JSON.stringify({
        startedAt: Date.now(),
        data: data,
      }));
    } catch {}
    try {
      // Pasar el JWT al server porque el prototipo está en iframe y no
      // comparte cookies con la API de Next.js.
      const headers = { 'Content-Type': 'application/json' };
      let sb = null;
      try {
        sb = window.__PAU_SUPABASE;
        const { data: session } = await sb.auth.getSession();
        const rawToken = session?.session?.access_token;
        // Sanear el token: solo caracteres ASCII válidos en un header HTTP
        // (JWT son base64url puro pero por si acaso quitamos espacios/saltos)
        if (rawToken) {
          const cleanToken = String(rawToken).trim().replace(/[^\x21-\x7E]/g, '');
          if (cleanToken) headers['Authorization'] = 'Bearer ' + cleanToken;
        }
      } catch (e) {
        console.warn('[ia-plan] getSession error:', e?.message);
      }

      // Guardar datos físicos en profile antes (best-effort) para futuros planes
      try {
        if (sb && (data.weightKg || data.heightCm || data.birthDate)) {
          const { data: { user } } = await sb.auth.getUser();
          if (user) {
            const updates = {};
            if (data.weightKg) updates.weight_kg = parseFloat(data.weightKg);
            if (data.heightCm) updates.height_cm = parseFloat(data.heightCm);
            if (data.birthDate) updates.birth_date = data.birthDate;
            if (Object.keys(updates).length > 0) {
              await sb.from('profiles').update(updates).eq('id', user.id);
              if (window.__pauCache?.profile) {
                window.__pauCache.profile = { ...window.__pauCache.profile, ...updates };
              }
            }
          }
        }
      } catch (e) {
        console.warn('[ia-plan] no se pudo guardar datos en profile:', e?.message);
      }

      console.log('[ia-plan] enviando request a /api/plans/generate');
      // Convertimos semanas → días para el backend.
      // Si el wizard ya calculó kcalTarget según pace, lo enviamos para
      // que el backend lo respete en lugar de recalcular.
      const wizardWeeks = data.weeks || 8;
      const requestBody = {
        ...data,
        days: wizardWeeks * 7,
        kcalTarget: window.__pauWizardKcalTarget || data.kcalTarget,
      };
      const res = await fetch('/api/plans/generate', {
        method: 'POST',
        headers,
        body: JSON.stringify(requestBody),
      });
      console.log('[ia-plan] status', res.status);

      // Si server devolvió no-JSON (ej. HTML de error 500), no romper
      let json;
      const ctype = res.headers.get('content-type') || '';
      if (ctype.includes('application/json')) {
        json = await res.json();
      } else {
        const text = await res.text();
        console.warn('[ia-plan] respuesta no-JSON:', text.slice(0, 200));
        nav.toast('Error del servidor (' + res.status + ')', { icon: '⚠️' });
        setLoading(false);
        return;
      }

      if (!res.ok || json.error) {
        // Rate limit alcanzado
        if (res.status === 429 || json.limit_reached) {
          setQuota(q => ({ ...(q || {}), used: json.used || q?.max || 3, max: json.max || 3, remaining: 0, resetAt: json.reset_at }));
          nav.toast(json.error || 'Has llegado al límite mensual de planes', { icon: '🔒' });
          setLoading(false);
          return;
        }
        const msg = json.error || ('HTTP ' + res.status);
        console.warn('[ia-plan] error response:', json);
        nav.toast('Error: ' + msg, { icon: '⚠️' });
        setLoading(false);
        return;
      }
      // Plan generado — limpiar el marker de "en proceso"
      try { localStorage.removeItem('paufit-plan-generating'); } catch {}
      // Ahora generamos TODAS las fotos antes de mostrar success
      setLoading(false);
      setPhotosLoading(true);
      try {
        await generateAllPlanPhotos(json?.plan?.plan, headers);
      } catch (e) {
        console.warn('[ia-plan] error generando fotos:', e?.message);
      }
      setPhotosLoading(false);

      setResult(json);
      setStep(TOTAL_STEPS + 1); // pantalla de éxito
      // Refrescar cache si tienes plan ahora activo
      window.dispatchEvent(new CustomEvent('pau:cache-updated'));

      // Notificación push si la usuaria cerró la pantalla y dio permiso
      try {
        if ('Notification' in window && Notification.permission === 'granted' && document.hidden) {
          const body = (window.PauFriend && window.PauFriend.planReady()) || 'Tu plan está listo';
          new Notification('Pau Fit', {
            body,
            icon: '/icons/icon-192.png',
            badge: '/icons/icon-192.png',
            tag: 'pau-plan-ready',
            requireInteraction: false,
          });
        }
      } catch {}
      return;
    } catch (e) {
      console.error('[ia-plan] exception:', e);
      // Si fue por cerrar la app o conexión perdida, NO mostrar error agresivo —
      // el server probablemente terminó y el plan está en DB. Recovery al volver.
      const looksLikeDisconnect = /aborted|network|failed to fetch|disconnected/i.test(e?.message || '');
      if (!looksLikeDisconnect) {
        nav.toast('Error: ' + (e?.message || 'no se pudo conectar'), { icon: '⚠️' });
      } else {
        console.log('[ia-plan] desconexión detectada, recovery activado al volver');
      }
    }
    setLoading(false);
  }

  // RECOVERY: al abrir el wizard, chequear si quedó un plan a medio generar.
  // Si sí, hacemos POLLING cada 4s hasta que el server termine (max 5 min).
  // Así Pau puede cerrar la app y volver — la generación sigue en el server.
  useEffect(() => {
    let cancelled = false;
    let pollInterval = null;
    (async () => {
      let pending = null;
      try {
        const raw = localStorage.getItem('paufit-plan-generating');
        if (raw) pending = JSON.parse(raw);
      } catch {}
      if (!pending) return;
      const ageMin = (Date.now() - (pending.startedAt || 0)) / 60000;
      if (ageMin > 15) {
        // Pasaron >15 min — limpiar y olvidar
        try { localStorage.removeItem('paufit-plan-generating'); } catch {}
        return;
      }

      async function checkOnce() {
        if (cancelled) return false;
        try {
          const sb = window.__PAU_SUPABASE;
          if (!sb) return false;
          const { data: { user } } = await sb.auth.getUser();
          if (!user) return false;
          const { data: enrolls } = await sb
            .from('user_planes')
            .select('id, plan_id, enrolled_at')
            .eq('user_id', user.id)
            .is('completed_at', null)
            .order('enrolled_at', { ascending: false })
            .limit(1);
          const e = enrolls?.[0];
          if (e && e.enrolled_at) {
            const enrollMs = new Date(e.enrolled_at).getTime();
            if (enrollMs >= pending.startedAt - 30000) {
              console.log('[ia-plan] recovery: plan listo en DB');
              try { localStorage.removeItem('paufit-plan-generating'); } catch {}
              const { data: planRow } = await sb.from('planes')
                .select('id, title, description, days, kcal_per_day')
                .eq('id', e.plan_id).maybeSingle();
              if (cancelled) return true;
              setLoading(false);
              setPhotosLoading(false);
              setResult({
                ok: true, mode: 'ai',
                plan: { plan: planRow ? {
                  title: planRow.title, description: planRow.description,
                  days: planRow.days, kcalDay: planRow.kcal_per_day,
                } : { title: 'Tu plan', description: 'Tu plan está listo', days: 7 } },
              });
              setStep(TOTAL_STEPS + 1);
              window.dispatchEvent(new CustomEvent('pau:cache-updated'));
              return true;
            }
          }
          return false;
        } catch (err) {
          console.warn('[ia-plan] recovery check:', err?.message);
          return false;
        }
      }

      // 1) Check inmediato
      const found = await checkOnce();
      if (found) return;
      // 2) Mostrar overlay de "esperando" y empezar polling
      setLoading(true);
      pollInterval = setInterval(async () => {
        if (cancelled) {
          if (pollInterval) clearInterval(pollInterval);
          return;
        }
        const ok = await checkOnce();
        if (ok) {
          if (pollInterval) clearInterval(pollInterval);
        }
        // Si pasaron >15 min sin encontrar, parar
        const elapsed = (Date.now() - (pending.startedAt || 0)) / 60000;
        if (elapsed > 15) {
          if (pollInterval) clearInterval(pollInterval);
          try { localStorage.removeItem('paufit-plan-generating'); } catch {}
          if (!cancelled) {
            setLoading(false);
            nav.toast('La generación tardó demasiado. Intenta de nuevo.', { icon: '⏰' });
          }
        }
      }, 4000); // poll cada 4s
    })();
    return () => {
      cancelled = true;
      if (pollInterval) clearInterval(pollInterval);
    };
  }, []);

  // Dirección de la animación al cambiar de paso (right=avanza, left=retrocede)
  const [stepDir, setStepDir] = useState('right');
  function goStep(newStep) {
    setStepDir(newStep > step ? 'right' : 'left');
    setStep(newStep);
  }

  // Pre-poblar defaults sensatos al entrar a steps de Cal AI si están vacíos
  // Orden: 1=peso · 2=altura · 3=peso objetivo (ya tenemos altura para validar IMC)
  useEffect(() => {
    const numericDefaults = {
      1: { key: 'weightKg', val: '60' },
      2: { key: 'heightCm', val: '165' },
      3: { key: 'goalWeightKg', val: data.weightKg || '60' },
    };
    const d = numericDefaults[step];
    if (d && !data[d.key]) setField(d.key, d.val);
  }, [step]);

  // Tips rotativos durante la generación — pool grande para que no se repitan
  // tan rápido. Cambian cada 4s (antes 6s) y son ~18 mensajes únicos.
  const LOADING_TIPS = [
    { e: '🥑', t: 'Eligiendo ingredientes reales sin harinas inflamatorias…' },
    { e: '🍳', t: 'Pensando recetas ricas para cada día…' },
    { e: '🛒', t: 'Calculando cantidades para tu lista de la compra…' },
    { e: '💪', t: 'Ajustando proteína para tu objetivo…' },
    { e: '🌱', t: 'Asegurando variedad de verduras y colores…' },
    { e: '⚖️', t: 'Equilibrando macros en cada comida…' },
    { e: '✨', t: 'Personalizando el plan con tus gustos…' },
    { e: '🍓', t: 'Añadiendo frutas de temporada…' },
    { e: '🥚', t: 'Variando proteínas para que no te aburras…' },
    { e: '🌶️', t: 'Pensando especias y sabores nuevos…' },
    { e: '🥕', t: 'Calculando porciones según tu peso…' },
    { e: '🍯', t: 'Eligiendo grasas buenas (aguacate, frutos secos)…' },
    { e: '🥬', t: 'Comprobando que sean recetas reales del Método…' },
    { e: '💗', t: 'Pensando en tu energía y saciedad…' },
    { e: '🥥', t: 'Sumando antiinflamatorios naturales…' },
    { e: '🫐', t: 'Añadiendo antioxidantes a tus snacks…' },
    { e: '🥗', t: 'Revisando que cada comida sea balanceada…' },
    { e: '🧘‍♀️', t: 'Pensando en tu ciclo y descanso…' },
    { e: '☀️', t: 'Adaptando los desayunos al horario que dijiste…' },
    { e: '📸', t: 'Generando las fotos de cada receta…' },
    { e: '🍅', t: 'Revisando frescura de las verduras…' },
    { e: '🍇', t: 'Sumando antojitos saludables…' },
  ];
  const [tipIdx, setTipIdx] = useState(0);
  const [elapsed, setElapsed] = useState(0);
  useEffect(() => {
    if (!loading) { setTipIdx(0); setElapsed(0); return; }
    // Cada 4s en lugar de 6s — pasa más rápido el tiempo perceptivo
    const tipInt = setInterval(() => setTipIdx(i => (i + 1) % LOADING_TIPS.length), 4000);
    const secInt = setInterval(() => setElapsed(s => s + 1), 1000);
    return () => { clearInterval(tipInt); clearInterval(secInt); };
  }, [loading]);

  function chip(label, value, current, onClick) {
    const sel = current === value || (Array.isArray(current) && current.includes(value));
    return (
      <button onClick={onClick} style={{
        padding: '11px 15px', borderRadius: 999, cursor: 'pointer',
        border: sel ? `1.5px solid ${T.accent}` : T.border,
        background: sel ? T.accent : T.card,
        color: sel ? '#fff' : T.text,
        fontFamily: 'Poppins', fontSize: 12.5, fontWeight: 700,
        transition: 'transform 200ms cubic-bezier(.34,1.56,.64,1), background 200ms, color 200ms, border-color 200ms',
        transform: sel ? 'scale(1.02)' : 'scale(1)',
        boxShadow: sel ? `0 4px 14px ${T.accent}30` : 'none',
      }}>{label}</button>
    );
  }

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 9999,
      background: T.bg,
      animation: 'fade-in 240ms ease',
      display: 'flex', flexDirection: 'column',
      overflow: 'hidden',
    }}>
      {/* SUCCESS OVERLAY — pantalla completa centrada, NO está dentro del scroll. */}
      {step === TOTAL_STEPS + 1 && result && (
        <div style={{
          position: 'fixed', inset: 0, zIndex: 10003,
          background: T.bg,
          display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
          padding: 'calc(40px + env(safe-area-inset-top)) 28px calc(40px + env(safe-area-inset-bottom))',
          animation: 'fade-in 400ms ease',
        }}>
          <div style={{ fontSize: 72, marginBottom: 18, animation: 'pau-breath 2400ms ease-in-out infinite' }}>🎉</div>
          <div className="pf-display" style={{ fontSize: 28, fontWeight: 700, color: T.text, letterSpacing: -0.5, textAlign: 'center' }}>
            ¡Tu plan está listo!
          </div>
          <div style={{ fontSize: 14, color: T.muted, marginTop: 10, lineHeight: 1.5, maxWidth: 320, textAlign: 'center' }}>
            {result.plan?.plan?.title}
          </div>
          <div style={{
            marginTop: 24, padding: '16px 18px',
            background: T.card, borderRadius: 20, border: T.border,
            textAlign: 'left', width: '100%', maxWidth: 360,
          }}>
            <div style={{ fontSize: 12.5, color: T.text, lineHeight: 1.5 }}>
              {result.plan?.plan?.description}
            </div>
            <div style={{ marginTop: 12, fontSize: 11, color: T.muted, display: 'flex', gap: 10 }}>
              <span>📅 {result.plan?.plan?.days} días</span>
              <span>🔥 ~{result.plan?.plan?.kcalDay} kcal/día</span>
            </div>
          </div>
          {result.mode === 'demo' && (
            <div style={{
              marginTop: 14, padding: '10px 12px',
              background: 'rgba(217,176,116,0.15)', borderRadius: 10,
              fontSize: 11, color: T.muted, lineHeight: 1.4,
              maxWidth: 360, textAlign: 'center',
            }}>
              💡 Modo demo (sin IA). Configura ANTHROPIC_API_KEY en Vercel.
            </div>
          )}
          <button onClick={() => {
            // Refrescar cache PRIMERO, luego cerrar wizard + ir a Comidas
            window.dispatchEvent(new CustomEvent('pau:cache-updated'));
            // navegar antes de cerrar — así el componente recetas re-fetcha con el plan ya en DB
            if (nav.tab) nav.tab('recetas');
            // Pequeño delay para asegurar que la navegación se procesa
            setTimeout(() => {
              onClose();
              // Forzar otro refresh tras el close por si hay componentes que se montaron tarde
              window.dispatchEvent(new CustomEvent('pau:cache-updated'));
            }, 100);
          }} style={{
            marginTop: 28, width: '100%', maxWidth: 360, padding: '16px',
            border: 'none', borderRadius: 20, cursor: 'pointer',
            background: T.accent, color: '#fff',
            fontFamily: 'Poppins', fontSize: 15, fontWeight: 700,
            boxShadow: `0 10px 28px ${T.accent}48`,
          }}>Ver mi plan →</button>
        </div>
      )}

      {/* OVERLAY UNIFICADO — un solo overlay para todo el proceso (Claude + fotos).
          La usuaria no necesita saber que las fotos las hace IA aparte. */}
      {(loading || photosLoading) && (
        <div style={{
          position: 'fixed', inset: 0, zIndex: 10001,
          background: T.bg,
          display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
          padding: '40px 28px',
          animation: 'fade-in 320ms ease',
        }}>
          {/* Keyframes inline — garantizar que se aplican en el iframe */}
          <style>{`
            @keyframes pauWizSpin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
            @keyframes pauWizBreath {
              0%, 100% { transform: scale(1); opacity: 0.95; }
              50% { transform: scale(1.08); opacity: 1; }
            }
            @keyframes pauWizPulse {
              0% { transform: scale(0.9); opacity: 0.6; }
              100% { transform: scale(1.4); opacity: 0; }
            }
          `}</style>
          {/* Anillo animado tipo Apple */}
          <div style={{ position: 'relative', width: 140, height: 140, marginBottom: 28 }}>
            <div style={{
              position: 'absolute', inset: 0, borderRadius: '50%',
              border: `2px solid ${T.accent}40`,
              animation: 'pauWizPulse 2400ms ease-out infinite',
            }}/>
            <div style={{
              position: 'absolute', inset: 0, borderRadius: '50%',
              border: `2.5px solid ${T.subtle}`,
            }}/>
            <div style={{
              position: 'absolute', inset: 0, borderRadius: '50%',
              border: '2.5px solid transparent',
              borderTopColor: T.accent,
              borderRightColor: `${T.accent}80`,
              animation: 'pauWizSpin 1800ms linear infinite',
            }}/>
            <div style={{
              position: 'absolute', inset: 0,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 52,
              animation: 'pauWizBreath 2400ms ease-in-out infinite',
            }}>{LOADING_TIPS[tipIdx].e}</div>
          </div>

          <div className="pf-display" style={{ fontSize: 24, fontWeight: 700, color: T.text, letterSpacing: -0.4, textAlign: 'center', marginBottom: 10 }}>
            Creando tu plan personalizado
          </div>
          <div style={{
            fontSize: 13, color: T.muted, textAlign: 'center', lineHeight: 1.5,
            maxWidth: 320, minHeight: 42,
          }}>
            {photosLoading ? LOADING_TIPS[Math.min(LOADING_TIPS.length - 1, Math.max(2, tipIdx))].t : LOADING_TIPS[tipIdx].t}
          </div>

          {/* Barra de progreso unificada (sin mencionar fotos por dentro) */}
          <div style={{ marginTop: 26, width: '100%', maxWidth: 320 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, color: T.muted, fontWeight: 600, marginBottom: 6 }}>
              <span>{elapsed}s</span>
              <span>{photosLoading && photosProgress.total > 0
                ? `${Math.round(((photosProgress.done / photosProgress.total) * 80) + 20)}%`
                : `${Math.min(20, Math.round((elapsed / 30) * 20))}%`}</span>
            </div>
            <div style={{ height: 8, background: T.subtle, borderRadius: 999, overflow: 'hidden' }}>
              <div style={{
                height: '100%',
                // 0-20% durante creación del plan, 20-100% durante generación visual
                width: photosLoading && photosProgress.total > 0
                  ? `${20 + (photosProgress.done / photosProgress.total) * 80}%`
                  : `${Math.min(20, (elapsed / 30) * 20)}%`,
                background: T.accent, borderRadius: 999,
                transition: 'width 600ms cubic-bezier(.34,1.56,.64,1)',
              }}/>
            </div>
          </div>

          {/* Mensaje tranquilizador + opción salir background */}
          <div style={{
            marginTop: 26, padding: '16px 18px',
            background: `${T.accent}10`, borderRadius: 14,
            fontSize: 12, color: T.text, textAlign: 'center', lineHeight: 1.5,
            maxWidth: 340,
          }}>
            <div style={{ fontWeight: 700, marginBottom: 4 }}>💗 Puedes salir un momento</div>
            <div style={{ color: T.muted }}>
              Déjala en segundo plano (no cierres la app) y vuelve en 1-2 min. Cuando esté listo te avisamos.
            </div>
          </div>

          {/* Botón para activar notificación si la usuaria quiere cerrar */}
          {'Notification' in window && Notification.permission !== 'granted' && (
            <button onClick={async () => {
              try {
                const result = await Notification.requestPermission();
                if (result === 'granted') {
                  nav.toast('Te avisaremos cuando esté listo 💗', { icon: '🔔' });
                }
              } catch {}
            }} style={{
              marginTop: 16, padding: '10px 18px',
              border: `1.5px solid ${T.accent}`, borderRadius: 999, cursor: 'pointer',
              background: 'transparent', color: T.accent,
              fontFamily: 'Poppins', fontSize: 12, fontWeight: 700,
              display: 'flex', alignItems: 'center', gap: 6,
            }}>🔔 Avisarme cuando esté listo</button>
          )}
        </div>
      )}

      <div style={{
        flex: 1, overflowY: 'auto', WebkitOverflowScrolling: 'touch',
        padding: 'calc(14px + env(safe-area-inset-top)) 22px calc(120px + env(safe-area-inset-bottom))',
        animation: 'slide-up 420ms cubic-bezier(.34,1.56,.64,1)',
      }}>

        {/* Header pro style */}
        {step <= TOTAL_STEPS && (
          <>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
              <button onClick={onClose} aria-label="Cerrar" style={{
                width: 32, height: 32, borderRadius: 999,
                border: 'none', background: T.subtle, cursor: 'pointer',
                fontSize: 14, color: T.muted, padding: 0,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontWeight: 700,
              }}>×</button>
              <div style={{
                padding: '6px 12px', borderRadius: 999, background: `${T.accent}15`,
                fontFamily: 'Poppins', fontSize: 11, fontWeight: 700, color: T.accent,
                letterSpacing: 0.3,
              }}>
                ✨ Plan IA · {step}/{TOTAL_STEPS}
              </div>
              <span style={{ width: 32 }}/>
            </div>

            {/* Progress dots — más "apps pro" que una barra */}
            <div style={{ display: 'flex', gap: 5, marginBottom: 18 }}>
              {Array.from({ length: TOTAL_STEPS }).map((_, i) => (
                <div key={i} style={{
                  flex: 1, height: 4, borderRadius: 999,
                  background: i < step ? T.accent : T.subtle,
                  transition: 'background 280ms ease',
                }}/>
              ))}
            </div>

            {/* Quota banner — "Te quedan X de N este mes" */}
            {quota && !quota.isGodmode && (
              <div style={{
                marginBottom: 20, padding: '10px 14px', borderRadius: 14,
                background: quotaReached ? `${T.muted}15` : `${T.accent}10`,
                fontSize: 11, fontWeight: 600,
                color: quotaReached ? T.muted : T.text,
                display: 'flex', alignItems: 'center', gap: 8,
              }}>
                <span style={{ fontSize: 14 }}>{quotaReached ? '🔒' : '✨'}</span>
                <span style={{ flex: 1, lineHeight: 1.4 }}>
                  {quotaReached
                    ? `Has usado tus ${quota.max} planes este mes. Próxima generación: ${new Date(quota.resetAt).toLocaleDateString('es', { day: 'numeric', month: 'long' })}.`
                    : `Te quedan ${quota.remaining} de ${quota.max} planes este mes. Cada plan se queda guardado.`}
                </span>
              </div>
            )}
            {quota?.isGodmode && (
              <div style={{
                marginBottom: 20, padding: '8px 14px', borderRadius: 14,
                background: `${T.accent}15`,
                fontSize: 11, fontWeight: 700,
                color: T.accent, display: 'flex', alignItems: 'center', gap: 8,
              }}>
                <span style={{ fontSize: 12 }}>👑</span>
                <span>Modo Pau — sin límite</span>
              </div>
            )}
          </>
        )}

        {/* CADA STEP: pantalla completa con animación slide — re-anima en cada cambio gracias a key={step} */}
        <div key={`step-${step}`} style={{
          animation: stepDir === 'right'
            ? 'step-in 380ms cubic-bezier(.2,.8,.2,1) both'
            : 'step-in-back 380ms cubic-bezier(.2,.8,.2,1) both',
          minHeight: 'calc(100dvh - 280px)',
          display: 'flex', flexDirection: 'column', justifyContent: 'flex-start',
        }}>

        {/* STEPS 1-5: datos físicos uno por pantalla estilo Cal AI
            Orden: 1=peso · 2=altura · 3=peso objetivo (con preview IMC)
            Reordenado para poder validar el IMC del objetivo en vivo. */}
        {(() => {
          const numericSteps = {
            1: { k: 'weightKg', title: '¿Cuánto pesas ahora?', subtitle: 'Lo necesito para tus calorías reales.', unit: 'kg', min: 30, max: 250, def: 60, step: 0.5, isWeight: true },
            2: { k: 'heightCm', title: '¿Cuánto mides?', subtitle: 'Para calcular bien tu metabolismo.', unit: 'cm', min: 120, max: 220, def: 165, step: 1, isHeight: true },
            3: { k: 'goalWeightKg', title: '¿Cuál es tu peso objetivo?', subtitle: 'A dónde quieres llegar. Puedes mantener tu peso.', unit: 'kg', min: 30, max: 250, def: 60, step: 0.5, isWeight: true, isGoal: true },
          };
          const cfg = numericSteps[step];
          if (!cfg) return null;
          // ── Soporte de unidades imperial / métrico ──
          // El valor siempre se almacena en métrico (kg/cm) para el backend.
          // Si la usuaria elige lb o ft, convertimos al display y al editar.
          const useImperial = cfg.isWeight ? data.weightUnit === 'lb' : data.heightUnit === 'ft';
          const KG_TO_LB = 2.20462;
          const CM_TO_IN = 0.393701;
          function metricToDisplay(metric) {
            if (cfg.isWeight) return useImperial ? metric * KG_TO_LB : metric;
            return useImperial ? metric * CM_TO_IN : metric;
          }
          function displayToMetric(display) {
            if (cfg.isWeight) return useImperial ? display / KG_TO_LB : display;
            return useImperial ? display / CM_TO_IN : display;
          }
          const minDisp = metricToDisplay(cfg.min);
          const maxDisp = metricToDisplay(cfg.max);
          // Stepper en unidad mostrada (lb va de 1 en 1, in también)
          const stepDisp = useImperial ? (cfg.isWeight ? 1 : 1) : cfg.step;
          const unitLabel = cfg.isWeight ? (useImperial ? 'lb' : 'kg') : (useImperial ? 'in' : 'cm');
          const valMetric = parseFloat(data[cfg.k]) || cfg.def;
          const valDisplay = metricToDisplay(valMetric);
          // Format helper para altura imperial → "5'7""
          function formatFtIn(inches) {
            const total = Math.round(inches);
            const ft = Math.floor(total / 12);
            const inc = total - ft * 12;
            return `${ft}'${inc}"`;
          }
          // Lo que la usuaria ve en el input
          const inputValue = useImperial
            ? Math.round(valDisplay).toString()
            : (data[cfg.k] || '');
          function setVal(displayV) {
            // CLAMP estricto cuando usas +/- (input controlado)
            const clamped = Math.max(minDisp, Math.min(maxDisp, displayV));
            const metric = displayToMetric(clamped);
            const fixed = cfg.step >= 1 ? Math.round(metric) : Math.round(metric * 10) / 10;
            setField(cfg.k, String(fixed));
          }
          function handleInputChange(raw) {
            // Mientras escribe NO clampamos al min para que pueda teclear "6" → "60"
            // sin saltar a 30. Solo cortamos valores absurdos enormes (> 5 dígitos).
            const cleaned = raw.replace(/[^0-9.]/g, '');
            if (cleaned === '') { setField(cfg.k, ''); return; }
            const parsed = parseFloat(cleaned);
            if (Number.isNaN(parsed)) return;
            // Seguridad: si pega un número absurdo, lo ignoramos (no clampamos, dejamos el anterior)
            if (parsed > maxDisp * 10 || cleaned.length > 6) return;
            const metric = displayToMetric(parsed);
            const fixed = cfg.step >= 1 ? Math.round(metric) : Math.round(metric * 10) / 10;
            setField(cfg.k, String(fixed));
          }
          function handleInputBlur() {
            // Al salir del campo: asegurar que está en rango (clamp final)
            const v = parseFloat(data[cfg.k]);
            if (Number.isNaN(v) || data[cfg.k] === '' || data[cfg.k] === undefined) {
              // Si está vacío, dejamos vacío (canAdvance bloquea de todas formas)
              return;
            }
            const disp = metricToDisplay(v);
            if (disp < minDisp || disp > maxDisp) {
              setVal(Math.max(minDisp, Math.min(maxDisp, disp)));
            }
          }
          function toggleUnit() {
            if (cfg.isWeight) {
              setField('weightUnit', data.weightUnit === 'lb' ? 'kg' : 'lb');
            } else {
              setField('heightUnit', data.heightUnit === 'ft' ? 'cm' : 'ft');
            }
          }
          // Conversión informativa para mostrar debajo
          const altDisplay = (() => {
            if (cfg.isWeight) {
              if (useImperial) return `≈ ${valMetric.toFixed(1)} kg`;
              return `≈ ${Math.round(valMetric * KG_TO_LB)} lb`;
            }
            if (useImperial) return `≈ ${Math.round(valMetric)} cm`;
            return `≈ ${formatFtIn(valMetric * CM_TO_IN)}`;
          })();
          // Cálculos en vivo para el step de peso objetivo
          let bmiInfo = null;
          if (cfg.isGoal && data.heightCm) {
            const h = parseFloat(data.heightCm) / 100;
            const w = parseFloat(data[cfg.k]) || cfg.def;
            const current = parseFloat(data.weightKg) || w;
            const bmi = w / (h * h);
            const safeMin = Math.round(18.5 * h * h * 10) / 10;
            const safeMax = Math.round(25 * h * h * 10) / 10;
            const losing = w < current;       // la meta baja respecto al peso actual
            const maintaining = w === current;
            let zone, color, label, msg;
            // Lenguaje amable y DIRECCIONAL: lo que importa no es dónde está la
            // meta, sino hacia dónde te mueve. Bajar hacia el rango = siempre bien.
            if (bmi < 16) {
              zone = 'danger'; color = '#B05050'; label = 'Meta demasiado baja';
              msg = `Esta meta es peligrosa para tu salud. Para ${data.heightCm} cm no bajes de ${safeMin} kg.`;
            } else if (bmi < 18.5) {
              zone = 'warn'; color = '#D4A574'; label = 'Algo por debajo';
              msg = `Estás rozando el mínimo saludable (${safeMin} kg para tu altura). Ve con cuidado.`;
            } else if (bmi < 25) {
              zone = 'ok'; color = '#5C7A56'; label = 'Dentro del rango sano';
              msg = losing
                ? `De ${current} a ${w} kg — meta clara y saludable. A por ella 💪`
                : 'Rango perfecto para tu altura.';
            } else if (losing) {
              // Meta por encima del rango PERO bajando → paso realista, se celebra
              zone = 'ok'; color = '#5C7A56'; label = 'Meta realista ✓';
              msg = `Bajar de ${current} a ${w} kg es un primer paso genial. Cuando llegues, te pones la siguiente meta 💗`;
            } else if (maintaining) {
              zone = 'ok'; color = '#5C7A56'; label = 'Mantener tu peso';
              msg = 'Mantener también es una meta válida. Nos centramos en fuerza y hábitos.';
            } else if (bmi <= 40) {
              zone = 'warn'; color = '#D4A574'; label = 'Meta al alza';
              msg = 'Tu meta sube de peso. Si buscas ganar músculo, perfecto — el plan se adapta.';
            } else {
              zone = 'danger'; color = '#B05050'; label = 'Revisa tu meta';
              msg = `Subir hasta ${w} kg no es seguro. Elige una meta igual o por debajo de tu peso actual.`;
            }
            bmiInfo = { bmi, zone, color, label, msg, safeMin, safeMax };
          }
          return (
            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', paddingTop: 20 }}>
              <div className="pf-display" style={{ fontSize: 26, fontWeight: 700, color: T.text, letterSpacing: -0.5, lineHeight: 1.15, maxWidth: 320 }}>
                {cfg.title}
              </div>
              <div style={{ fontSize: 13, color: T.muted, marginTop: 10, lineHeight: 1.5, maxWidth: 320 }}>
                {cfg.subtitle}
              </div>

              {/* Toggle de unidad kg ↔ lb o cm ↔ ft */}
              <div style={{
                marginTop: 28, padding: 3, borderRadius: 999,
                background: T.subtle, display: 'inline-flex', gap: 2,
              }}>
                {[
                  cfg.isWeight ? { v: 'kg', l: 'kg' } : { v: 'cm', l: 'cm' },
                  cfg.isWeight ? { v: 'lb', l: 'lb' } : { v: 'ft', l: 'ft / in' },
                ].map(o => {
                  const sel = cfg.isWeight ? data.weightUnit === o.v || (o.v === 'kg' && !data.weightUnit) : data.heightUnit === o.v || (o.v === 'cm' && !data.heightUnit);
                  return (
                    <button key={o.v} onClick={() => {
                      if (cfg.isWeight) setField('weightUnit', o.v);
                      else setField('heightUnit', o.v);
                    }} style={{
                      padding: '8px 18px', borderRadius: 999, border: 'none', cursor: 'pointer',
                      background: sel ? T.card : 'transparent',
                      color: sel ? T.text : T.muted,
                      fontSize: 12, fontWeight: 700, letterSpacing: 0.3,
                      fontFamily: 'inherit',
                      boxShadow: sel ? '0 1px 3px rgba(0,0,0,0.08)' : 'none',
                    }}>{o.l}</button>
                  );
                })}
              </div>

              {/* Selector grande Cal AI style — stepper con número grande */}
              <div style={{ marginTop: 22, display: 'flex', alignItems: 'center', gap: 18 }}>
                <button onClick={() => setVal(valDisplay - stepDisp)} aria-label="menos" style={{
                  width: 52, height: 52, borderRadius: 999, border: 'none', cursor: 'pointer',
                  background: T.subtle, color: T.text,
                  fontSize: 24, fontWeight: 600,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  WebkitTapHighlightColor: 'transparent',
                }}>−</button>
                <div style={{
                  minWidth: 160, padding: '14px 20px',
                  textAlign: 'center',
                }}>
                  {/* Si es altura imperial mostramos "5'7"" formateado */}
                  {cfg.isHeight && useImperial ? (
                    <div style={{
                      fontFamily: 'Poppins', fontSize: 56, fontWeight: 800,
                      color: T.text, letterSpacing: -1, lineHeight: 1,
                    }}>{formatFtIn(valDisplay)}</div>
                  ) : (
                    <input
                      type="number" step={stepDisp} min={minDisp} max={maxDisp}
                      inputMode="decimal"
                      value={useImperial ? Math.round(valDisplay) : (data[cfg.k] || '')}
                      placeholder={String(cfg.def)}
                      onChange={(e) => handleInputChange(e.target.value)}
                      onBlur={handleInputBlur}
                      style={{
                        width: '100%', border: 'none', background: 'transparent', outline: 'none',
                        fontFamily: 'Poppins', fontSize: 64, fontWeight: 800,
                        color: bmiInfo && bmiInfo.zone === 'danger' ? '#B05050' : T.text,
                        textAlign: 'center', letterSpacing: -2,
                        lineHeight: 1,
                      }}
                    />
                  )}
                  <div style={{ fontSize: 13, color: T.muted, fontWeight: 600, marginTop: 4, letterSpacing: 0.2 }}>
                    {unitLabel}
                  </div>
                  {/* Conversión informativa en gris */}
                  <div style={{ fontSize: 11, color: T.muted, marginTop: 8, fontWeight: 600 }}>
                    {altDisplay}
                  </div>
                </div>
                <button onClick={() => setVal(valDisplay + stepDisp)} aria-label="más" style={{
                  width: 52, height: 52, borderRadius: 999, border: 'none', cursor: 'pointer',
                  background: T.subtle, color: T.text,
                  fontSize: 24, fontWeight: 600,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  WebkitTapHighlightColor: 'transparent',
                }}>+</button>
              </div>

              {/* Preview IMC en vivo para el peso objetivo — solo en step 3 */}
              {bmiInfo && (
                <div style={{
                  marginTop: 30, padding: '14px 18px',
                  borderRadius: 14, maxWidth: 320, width: '100%',
                  background: bmiInfo.zone === 'danger' ? '#FFE5E5'
                    : bmiInfo.zone === 'warn' ? '#FFF4E5'
                    : '#E8F3E5',
                  border: `1px solid ${bmiInfo.color}40`,
                }}>
                  <div style={{
                    fontSize: 11, fontWeight: 800, letterSpacing: 0.4,
                    color: bmiInfo.color, 
                  }}>{bmiInfo.label}</div>
                  <div style={{
                    fontSize: 12, color: T.text, marginTop: 6, lineHeight: 1.5,
                  }}>
                    {bmiInfo.msg}
                  </div>
                  {bmiInfo.zone === 'danger' && (
                    <div style={{ fontSize: 11, color: '#B05050', marginTop: 8, fontWeight: 700 }}>
                      ⚠ No podemos generar este plan. Ajusta tu meta.
                    </div>
                  )}
                </div>
              )}
            </div>
          );
        })()}

        {/* STEP 4: Fecha de nacimiento */}
        {step === 4 && (
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', paddingTop: 20 }}>
            <div className="pf-display" style={{ fontSize: 26, fontWeight: 700, color: T.text, letterSpacing: -0.5, lineHeight: 1.15, maxWidth: 320 }}>
              ¿Cuándo naciste?
            </div>
            <div style={{ fontSize: 13, color: T.muted, marginTop: 10, lineHeight: 1.5, maxWidth: 320 }}>
              Tu edad ayuda a calcular tus calorías reales.
            </div>
            <div style={{
              marginTop: 36, padding: '24px 22px', borderRadius: 20,
              background: T.subtle, width: '100%', maxWidth: 320,
            }}>
              <input
                type="date" value={data.birthDate}
                onChange={(e) => setField('birthDate', e.target.value)}
                style={{
                  width: '100%', border: 'none', background: 'transparent', outline: 'none',
                  fontFamily: 'Poppins', fontSize: 22, fontWeight: 700, color: T.text, textAlign: 'center',
                }}
              />
            </div>
          </div>
        )}

        {/* STEP 5: Nivel de actividad */}
        {step === 5 && (
          <div>
            <div className="pf-display" style={{ fontSize: 24, fontWeight: 700, color: T.text, letterSpacing: -0.4, textAlign: 'center', marginTop: 10 }}>
              ¿Qué tan activa eres?
            </div>
            <div style={{ fontSize: 13, color: T.muted, marginTop: 8, lineHeight: 1.5, textAlign: 'center' }}>
              Para ajustar tus calorías al nivel real.
            </div>
            <div style={{ marginTop: 22, display: 'flex', flexDirection: 'column', gap: 10 }}>
              {[
                { v: 'sedentaria', e: '💻', t: 'Sedentaria', s: 'Trabajo de escritorio, casi no me muevo' },
                { v: 'ligera', e: '🚶‍♀️', t: 'Ligera', s: 'Camino algo, entreno 1-2 veces semana' },
                { v: 'moderada', e: '💪', t: 'Moderada', s: 'Entreno 3-5 veces semana' },
                { v: 'activa', e: '🏃‍♀️', t: 'Activa', s: 'Entreno 6+ veces semana o trabajo físico' },
                { v: 'muy_activa', e: '🔥', t: 'Muy activa', s: 'Atleta / entrenando 2 veces al día' },
              ].map(o => (
                <button key={o.v} onClick={() => setField('activity', o.v)} style={{
                  padding: '14px 16px', borderRadius: 14, cursor: 'pointer',
                  border: data.activity === o.v ? `1.5px solid ${T.accent}` : T.border,
                  background: data.activity === o.v ? `${T.accent}15` : T.card,
                  color: T.text, textAlign: 'left', fontFamily: 'inherit',
                  display: 'flex', alignItems: 'center', gap: 14,
                }}>
                  <span style={{ fontSize: 26 }}>{o.e}</span>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 14, fontWeight: 700 }}>{o.t}</div>
                    <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>{o.s}</div>
                  </div>
                  {data.activity === o.v && <span style={{ color: T.accent, fontSize: 20 }}>✓</span>}
                </button>
              ))}
            </div>
          </div>
        )}

        {/* STEP 6: Objetivo + ritmo (Cal AI style)
            Objetivo elige perder/ganar/etc. Si aplica → ritmo aparece debajo. */}
        {step === 6 && (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Cuál es tu objetivo?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Adaptamos el plan a lo que quieras conseguir.
            </div>
            <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 10 }}>
              {[
                { v: 'perder', e: '🔥', t: 'Perder grasa', s: 'Déficit calórico moderado' },
                { v: 'ganar', e: '💪', t: 'Ganar músculo', s: 'Lean bulk — superávit limpio' },
                { v: 'recomposicion', e: '✨', t: 'Perder grasa y ganar músculo', s: 'Recomposición — mismo peso, distinta composición' },
                { v: 'mantener', e: '🌸', t: 'Mantener peso', s: 'Comer equilibrado y rico' },
                { v: 'salud', e: '🌱', t: 'Comer más sano', s: 'Sin objetivo de peso' },
              ].map(o => (
                <button key={o.v} onClick={() => setField('goal', o.v)} style={{
                  padding: '14px 16px', borderRadius: 14, cursor: 'pointer',
                  border: data.goal === o.v ? `1.5px solid ${T.accent}` : T.border,
                  background: data.goal === o.v ? `${T.accent}15` : T.card,
                  color: T.text, textAlign: 'left', fontFamily: 'inherit',
                  display: 'flex', alignItems: 'center', gap: 12,
                }}>
                  <span style={{ fontSize: 24 }}>{o.e}</span>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 14, fontWeight: 700 }}>{o.t}</div>
                    <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>{o.s}</div>
                  </div>
                  {data.goal === o.v && <span style={{ color: T.accent, fontSize: 18 }}>✓</span>}
                </button>
              ))}
            </div>

            {/* Selector de ritmo — solo si la meta tiene velocidad ajustable */}
            {(data.goal === 'perder' || data.goal === 'ganar') && (() => {
              // Para perder: 0.4 / 0.6 / 0.8 kg/sem
              // Para ganar músculo (lean bulk): 0.15 / 0.3 / 0.45 kg/sem
              const isLoss = data.goal === 'perder';
              const paceTable = isLoss
                ? [
                    { v: 'sostenible', e: '🌱', t: 'Sostenible', kgWeek: 0.4, s: 'Lo más sano. Sin pasar hambre.' },
                    { v: 'moderado',   e: '💪', t: 'Moderado',   kgWeek: 0.6, s: 'Recomendado. Balance perfecto.' },
                    { v: 'acelerado',  e: '⚡', t: 'Acelerado',  kgWeek: 0.8, s: 'Más exigente. Necesitas constancia.' },
                  ]
                : [
                    { v: 'sostenible', e: '🌱', t: 'Limpio',     kgWeek: 0.15, s: 'Ganancia casi pura. Sin grasa.' },
                    { v: 'moderado',   e: '💪', t: 'Moderado',   kgWeek: 0.30, s: 'Recomendado. Balance músculo/grasa.' },
                    { v: 'acelerado',  e: '⚡', t: 'Agresivo',   kgWeek: 0.45, s: 'Más músculo, algo más de grasa.' },
                  ];
              // Para menores: solo permitir sostenible
              const isMinorPace = (() => {
                if (!data.birthDate) return false;
                try {
                  const ms = Date.now() - new Date(data.birthDate).getTime();
                  return ms / (365.25 * 24 * 3600 * 1000) < 18;
                } catch { return false; }
              })();
              return (
                <div style={{ marginTop: 24 }}>
                  <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 10 }}>
                    {isLoss ? '¿A qué ritmo quieres perder?' : '¿A qué ritmo quieres ganar?'}
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                    {paceTable.map(p => {
                      const blocked = isMinorPace && p.v !== 'sostenible';
                      const sel = data.pace === p.v;
                      return (
                        <button key={p.v} disabled={blocked} onClick={() => setField('pace', p.v)} style={{
                          padding: '14px 16px', borderRadius: 14, cursor: blocked ? 'not-allowed' : 'pointer',
                          border: sel ? `1.5px solid ${T.accent}` : T.border,
                          background: sel ? `${T.accent}15` : T.card,
                          color: T.text, textAlign: 'left', fontFamily: 'inherit',
                          display: 'flex', alignItems: 'center', gap: 12,
                          opacity: blocked ? 0.4 : 1,
                        }}>
                          <span style={{ fontSize: 22 }}>{p.e}</span>
                          <div style={{ flex: 1 }}>
                            <div style={{ fontSize: 13, fontWeight: 700, display: 'flex', alignItems: 'center', gap: 8 }}>
                              {p.t}
                              <span style={{
                                fontSize: 10, fontWeight: 700, color: T.muted,
                                padding: '2px 7px', borderRadius: 6, background: T.subtle,
                              }}>{isLoss ? '−' : '+'}{p.kgWeek} kg/sem</span>
                            </div>
                            <div style={{ fontSize: 11, color: T.muted, marginTop: 3 }}>{p.s}</div>
                            {blocked && (
                              <div style={{ fontSize: 10, color: T.accent, marginTop: 3, fontWeight: 700 }}>
                                Solo disponible para mayores de 18
                              </div>
                            )}
                          </div>
                          {sel && <span style={{ color: T.accent, fontSize: 18 }}>✓</span>}
                        </button>
                      );
                    })}
                  </div>
                </div>
              );
            })()}
          </div>
        )}

        {/* STEP 7: Dieta + restricciones */}
        {step === 7 && (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Algún tipo de dieta?
            </div>
            <div style={{ marginTop: 14, display: 'flex', flexWrap: 'wrap', gap: 8 }}>
              {chip('🍗 Como todo', 'todo', data.dietType, () => setField('dietType', 'todo'))}
              {chip('🥦 Vegetariana', 'vegetariana', data.dietType, () => setField('dietType', 'vegetariana'))}
              {chip('🌱 Vegana', 'vegana', data.dietType, () => setField('dietType', 'vegana'))}
              {chip('🐟 Pescetariana', 'pescetariana', data.dietType, () => setField('dietType', 'pescetariana'))}
            </div>
            <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 22, marginBottom: 10 }}>Sin (selecciona)</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
              {chip('Gluten', 'gluten', data.restrictions, () => toggleArr('restrictions', 'gluten'))}
              {chip('Lactosa', 'lactosa', data.restrictions, () => toggleArr('restrictions', 'lactosa'))}
              {chip('Frutos secos', 'frutos secos', data.restrictions, () => toggleArr('restrictions', 'frutos secos'))}
              {chip('Huevo', 'huevo', data.restrictions, () => toggleArr('restrictions', 'huevo'))}
              {chip('Marisco', 'marisco', data.restrictions, () => toggleArr('restrictions', 'marisco'))}
              {chip('Soja', 'soja', data.restrictions, () => toggleArr('restrictions', 'soja'))}
            </div>
          </div>
        )}

        {/* STEP 8: Alergias y cosas que no te gustan */}
        {step === 8 && (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Alergias o cosas que no te gusten?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Opcional. Cualquier cosa específica (puedes saltar).
            </div>
            <div style={{ marginTop: 18 }}>
              <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 6 }}>Alergias</div>
              <input value={data.allergies} onChange={e => setField('allergies', e.target.value)}
                placeholder="ej. fresas, kiwi" style={{
                width: '100%', padding: '12px 14px', borderRadius: 14,
                border: T.border, background: T.card, color: T.text,
                fontFamily: 'Poppins', fontSize: 13, outline: 'none',
              }}/>
            </div>
            <div style={{ marginTop: 14 }}>
              <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 6 }}>No me gusta</div>
              <input value={data.dislikes} onChange={e => setField('dislikes', e.target.value)}
                placeholder="ej. coliflor, hígado" style={{
                width: '100%', padding: '12px 14px', borderRadius: 14,
                border: T.border, background: T.card, color: T.text,
                fontFamily: 'Poppins', fontSize: 13, outline: 'none',
              }}/>
            </div>
          </div>
        )}

        {/* STEP 9: Comidas / día + tiempo cocina */}
        {step === 9 && (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Cuántas comidas al día?
            </div>
            <div style={{ marginTop: 14, display: 'flex', gap: 8 }}>
              {[3, 4, 5].map(n => chip(`${n} comidas`, n, data.mealsPerDay, () => setField('mealsPerDay', n)))}
            </div>
            <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 22, marginBottom: 10 }}>Tiempo de cocina</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {[
                { v: 'rapido', e: '⚡', t: 'Rápido (max 15 min)', s: 'Apenas tengo tiempo' },
                { v: 'normal', e: '🍳', t: 'Normal (20-30 min)', s: 'Cocino en mi día a día' },
                { v: 'encanta', e: '👩‍🍳', t: 'Me encanta cocinar', s: 'Recetas más elaboradas, ok hasta 45 min' },
              ].map(o => (
                <button key={o.v} onClick={() => setField('cookTime', o.v)} style={{
                  padding: '12px 14px', borderRadius: 14, cursor: 'pointer',
                  border: data.cookTime === o.v ? `1.5px solid ${T.accent}` : T.border,
                  background: data.cookTime === o.v ? `${T.accent}15` : T.card,
                  color: T.text, textAlign: 'left', fontFamily: 'inherit',
                  display: 'flex', alignItems: 'center', gap: 12,
                }}>
                  <span style={{ fontSize: 22 }}>{o.e}</span>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 13, fontWeight: 700 }}>{o.t}</div>
                    <div style={{ fontSize: 11, color: T.muted, marginTop: 1 }}>{o.s}</div>
                  </div>
                </button>
              ))}
            </div>
          </div>
        )}

        {/* STEP 10: Cocinas favoritas */}
        {step === 10 && (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Cocinas favoritas?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Marca las que más te gustan (puedes saltar).
            </div>
            <div style={{ marginTop: 16, display: 'flex', flexWrap: 'wrap', gap: 8 }}>
              {['Mediterránea', 'Asiática', 'Latina', 'Italiana', 'Casera', 'Healthy bowls', 'Mexicana', 'Variada'].map(c => (
                chip(c, c, data.cuisines, () => toggleArr('cuisines', c))
              ))}
            </div>
          </div>
        )}

        {/* STEP 11: Situaciones especiales — opciones rápidas + textarea libre */}
        {step === 11 && (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Alguna situación especial?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Selecciona lo que se aplique y/o escribe lo que quieras. Opcional.
            </div>
            <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 8 }}>
              {[
                // 'menstrual' quitado (2026-07-04, pedido de Pau): el plan dura
                // semanas y la regla días — no tiene sentido como condición fija.
                // El ciclo ya se registra en su propia pantalla (CicloScreen).
                { v: 'sop', e: '⚖️', t: 'SOP / resistencia insulina' },
                { v: 'lactancia', e: '🤱', t: 'Estoy lactando' },
                { v: 'embarazada', e: '🤰', t: 'Estoy embarazada' },
                { v: 'menopausia', e: '🌺', t: 'Menopausia / perimenopausia' },
              ].map(o => {
                const sel = (data.situations || []).includes(o.v);
                return (
                  <button key={o.v} onClick={() => toggleArr('situations', o.v)} style={{
                    padding: '12px 14px', borderRadius: 14, cursor: 'pointer',
                    border: sel ? `1.5px solid ${T.accent}` : T.border,
                    background: sel ? `${T.accent}15` : T.card,
                    color: T.text, textAlign: 'left', fontFamily: 'inherit',
                    display: 'flex', alignItems: 'center', gap: 12,
                  }}>
                    <span style={{ fontSize: 22 }}>{o.e}</span>
                    <span style={{ flex: 1, fontSize: 13, fontWeight: 700 }}>{o.t}</span>
                    {sel && <span style={{ color: T.accent, fontSize: 18 }}>✓</span>}
                  </button>
                );
              })}
            </div>
            <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 22, marginBottom: 8 }}>O cuéntame algo más</div>
            <textarea
              value={data.situationsText || ''}
              onChange={(e) => setField('situationsText', e.target.value)}
              placeholder="Ej. 'Me hincho con muchos carbs' o 'Suelo tener antojo de dulce por la noche'…"
              rows={3}
              style={{
                width: '100%',
                padding: '12px 14px',
                border: T.border, borderRadius: 14,
                background: T.card, color: T.text,
                fontFamily: 'Poppins', fontSize: 13, lineHeight: 1.5,
                outline: 'none', resize: 'vertical',
                minHeight: 80,
              }}
            />
          </div>
        )}

        {/* STEP 12: Duración + resumen profesional con BMI / kcal calculadas */}
        {step === 12 && (() => {
          // ── Cálculos profesionales en vivo (mismo método que el backend) ──
          const weight = parseFloat(data.weightKg) || 60;
          const goalWeight = parseFloat(data.goalWeightKg) || weight;
          const height = parseFloat(data.heightCm) || 165;
          let age = 28;
          if (data.birthDate) {
            try {
              const ms = Date.now() - new Date(data.birthDate).getTime();
              age = Math.max(14, Math.floor(ms / (365.25 * 24 * 3600 * 1000)));
            } catch {}
          }
          const bmi = weight / Math.pow(height / 100, 2);
          // Etiquetas amables — sin "obesidad" ni "sobrepeso"
          const bmiCat = bmi < 18.5 ? { l: 'Algo bajo', c: '#D4A574' }
            : bmi < 25 ? { l: 'En rango', c: '#5C7A56' }
            : bmi < 30 ? { l: 'Algo alto', c: '#D4A574' }
            : { l: 'Por encima', c: '#D4A574' };
          const bmr = Math.round(10 * weight + 6.25 * height - 5 * age - 161);
          const activityFactor = ({
            sedentaria: 1.2, ligera: 1.35, moderada: 1.5, activa: 1.7, muy_activa: 1.85,
          })[data.activity || 'moderada'] || 1.5;
          const tdee = Math.round(bmr * activityFactor);
          const diffKg = goalWeight - weight;
          // Mapeo de ritmo → kg/semana según meta
          const paceKgPerWeek = (() => {
            if (data.goal === 'perder') {
              return ({ sostenible: 0.4, moderado: 0.6, acelerado: 0.8 })[data.pace] || 0.6;
            }
            if (data.goal === 'ganar') {
              return ({ sostenible: 0.15, moderado: 0.30, acelerado: 0.45 })[data.pace] || 0.30;
            }
            return 0;
          })();
          // Déficit/superávit calórico desde pace (1 kg grasa = 7700 kcal)
          let kcalTarget = tdee;
          if (data.goal === 'perder') {
            kcalTarget = Math.round(tdee - (paceKgPerWeek * 7700 / 7));
          } else if (data.goal === 'ganar') {
            kcalTarget = Math.round(tdee + (paceKgPerWeek * 7700 / 7));
          } else if (data.goal === 'recomposicion') {
            kcalTarget = Math.round(tdee - 100); // ligero déficit + entreno fuerza
          }
          // Guardar kcalTarget para que el backend lo respete
          if (kcalTarget !== data.kcalTarget) {
            // No setField aquí (causa loop) — escribimos directo en window cache
            window.__pauWizardKcalTarget = kcalTarget;
            window.__pauWizardPaceKgPerWeek = paceKgPerWeek;
          }
          // Proteína recomendada (g/kg según objetivo)
          const proteinPerKg = data.goal === 'perder' ? 2.1
            : data.goal === 'ganar' ? 1.9
            : data.goal === 'recomposicion' ? 2.0
            : 1.7;
          const proteinG = Math.round(weight * proteinPerKg);
          const waterMl = Math.round(weight * 35);

          // Validación médica — bloqueamos SOLO cuando el OBJETIVO es peligroso.
          // No importa cuántos kg quieras bajar — si vas a un peso saludable,
          // bajar de 100 a 80 (20%) es perfectamente normal y sano.
          const warnings = [];
          const blockers = [];
          const goalBmi = goalWeight / Math.pow(height / 100, 2);
          const lossPct = diffKg < 0 ? Math.abs(diffKg) / weight * 100 : 0;
          const isMinor = age < 18;
          const isVeryYoung = age < 14;

          // ── BLOQUEADORES (solo si el OBJETIVO es médicamente peligroso) ──
          // 1. IMC objetivo en zona peligrosa absoluta (16-40 = rango seguro)
          if (goalBmi < 16) {
            blockers.push(`Tu peso objetivo (${goalWeight} kg → IMC ${goalBmi.toFixed(1)}) es peligroso para tu salud. Para ${height} cm el mínimo saludable es ${Math.round(18.5 * Math.pow(height/100, 2) * 10)/10} kg. Ajusta tu meta.`);
          }
          if (goalBmi > 40) {
            blockers.push(`Tu peso objetivo (${goalWeight} kg → IMC ${goalBmi.toFixed(1)}) requiere supervisión médica. Habla con un profesional antes de seguir.`);
          }
          // 2. Menor de 14 — sin importar nada
          if (isVeryYoung) {
            blockers.push('Eres muy joven para usar planes de alimentación de la app. Habla con un adulto y un profesional.');
          }
          // 3. Menor de edad ya en peso bajo + quiere perder más
          //    → señal de alarma de trastorno alimenticio
          if (isMinor && data.goal === 'perder' && bmi < 19) {
            blockers.push('Tu IMC ya está en peso bajo. Como eres menor de edad, no generamos planes de pérdida en esta zona — habla con tu pediatra.');
          }

          // ── ADVERTENCIAS (avisan pero permiten generar) ──
          if (bmi < 18.5 && data.goal === 'perder' && !blockers.length) {
            warnings.push('Tu IMC ya es bajo. Considera mantener o ganar masa en lugar de perder.');
          }
          if (data.goal === 'perder' && lossPct > 10 && !blockers.length) {
            warnings.push(`Vas a hacer un cambio grande (${lossPct.toFixed(0)}% de tu peso). El plan apunta a 0.4-0.5 kg/semana — sano y sostenible. Cuando termine podrás generar otro para seguir.`);
          }
          if (data.goal === 'perder' && Math.abs(diffKg) > 0 && Math.abs(diffKg) < 2 && bmi < 25 && !isMinor && !blockers.length) {
            warnings.push('Ya estás en peso saludable. Quizá lo tuyo es recomposición (tonificar) en lugar de perder.');
          }
          if (isMinor && !blockers.length) {
            warnings.push(`Eres menor de edad (${age} años). El plan se enfoca en alimentación equilibrada y crecimiento sano.`);
          }
          window.__pauPlanBlockers = blockers;

          const goalText = {
            perder: 'Perder grasa', ganar: 'Ganar músculo',
            recomposicion: 'Recomposición', mantener: 'Mantener',
            salud: 'Comer mejor',
          }[data.goal] || 'Tu plan';

          // Mensaje motivador adaptado a la meta — plazos CORTOS (1/2/4 sem)
          const weeks = data.weeks || 2;
          const totalKg = Math.round(paceKgPerWeek * weeks * 10) / 10;
          const weekLabel = weeks === 1 ? 'una semana' : `${weeks} semanas`;
          const goalMessage = (() => {
            if (data.goal === 'perder') {
              if (weeks === 1) return {
                title: `En 7 días bajarás ~${totalKg} kg`,
                body: 'Notarás ropa más cómoda y cara más definida desde el día 3-4.',
              };
              return {
                title: `En ${weekLabel} bajarás ~${totalKg} kg`,
                body: 'Ropa más holgada, cara definida y energía desde la primera semana.',
              };
            }
            if (data.goal === 'ganar') {
              return {
                title: `En ${weekLabel} ganarás ~${totalKg} kg de músculo`,
                body: weeks <= 2
                  ? 'Activas el proceso. El músculo visible se nota en 3-4 semanas.'
                  : 'Cambios visibles con entreno de fuerza + proteína alta.',
              };
            }
            if (data.goal === 'recomposicion') {
              return {
                title: `${weekLabel} para verte distinta`,
                body: 'Mismo peso, distinta composición. Más músculo, menos grasa.',
              };
            }
            if (data.goal === 'mantener') {
              return {
                title: `${weekLabel} para consolidar tu hábito`,
                body: 'Tu energía, digestión y sueño mejorarán en 7 días.',
              };
            }
            return {
              title: `${weekLabel} de comida real`,
              body: 'Más energía, menos antojos y mejor digestión desde el día 3.',
            };
          })();

          return (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Cuánto dura tu plan?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Plazos cortos para ver cambios reales. Después puedes generar otro.
            </div>
            <div style={{ marginTop: 14, display: 'flex', gap: 8 }}>
              {[
                { v: 1, l: '7 días',   s: 'Prueba rápida' },
                { v: 2, l: '14 días',  s: 'Recomendado' },
                { v: 4, l: '28 días',  s: 'Mes completo' },
              ].map(o => {
                const sel = data.weeks === o.v;
                return (
                  <button key={o.v} onClick={() => setField('weeks', o.v)} style={{
                    flex: 1, padding: '14px 8px', borderRadius: 14, cursor: 'pointer',
                    border: sel ? `1.5px solid ${T.accent}` : T.border,
                    background: sel ? `${T.accent}15` : T.card,
                    color: T.text, fontFamily: 'inherit',
                  }}>
                    <div style={{ fontSize: 13, fontWeight: 800 }}>{o.l}</div>
                    <div style={{ fontSize: 10, color: T.muted, marginTop: 3, fontWeight: 600 }}>{o.s}</div>
                  </button>
                );
              })}
            </div>

            {/* Mensaje motivador adaptado a la meta */}
            <div style={{
              marginTop: 18, padding: '18px 20px',
              background: `linear-gradient(135deg, ${T.accent}15, ${T.accent}05)`,
              borderRadius: 18,
            }}>
              <div className="pf-display" style={{
                fontSize: 18, fontWeight: 700, color: T.text,
                letterSpacing: -0.4, lineHeight: 1.25,
              }}>
                {goalMessage.title}
              </div>
              <div style={{ fontSize: 12, color: T.muted, marginTop: 8, lineHeight: 1.5 }}>
                {goalMessage.body}
              </div>
              {data.goal === 'perder' && totalKg > 0 && Math.abs(diffKg) > totalKg + 0.5 && (
                <div style={{
                  marginTop: 12, padding: '10px 12px',
                  background: '#FFFFFF', borderRadius: 14,
                  fontSize: 11, color: T.muted, lineHeight: 1.5,
                }}>
                  💡 Quieres bajar {Math.abs(diffKg).toFixed(1)} kg en total. Este plan te lleva {totalKg} kg. Después podrás generar otro para seguir bajando.
                </div>
              )}
            </div>

            {/* BLOQUEADORES MÉDICOS — banner rojo arriba si los hay */}
            {blockers.length > 0 && (
              <div style={{ marginTop: 22 }}>
                {blockers.map((b, i) => (
                  <div key={i} style={{
                    padding: '14px 16px', marginTop: i === 0 ? 0 : 8,
                    background: '#FFE5E5', borderRadius: 14,
                    border: '1px solid rgba(176, 80, 80, 0.4)',
                    fontSize: 12.5, color: '#7A2222', lineHeight: 1.5,
                    display: 'flex', alignItems: 'flex-start', gap: 10,
                  }}>
                    <span style={{ fontSize: 16, lineHeight: 1 }}>⚠️</span>
                    <span style={{ flex: 1, fontWeight: 600 }}>{b}</span>
                  </div>
                ))}
              </div>
            )}

            {/* RESUMEN PROFESIONAL — cards estilo Apple Health */}
            <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 26, marginBottom: 10 }}>
              Tu plan personalizado
            </div>

            {/* Card grande: kcal objetivo + objetivo */}
            <div style={{
              padding: '20px 22px',
              background: T.card, borderRadius: 18,
              border: T.border,
            }}>
              <div style={{ fontSize: 11, fontWeight: 600, color: T.muted, letterSpacing: 0.4,  }}>
                {goalText}
              </div>
              <div style={{
                fontFamily: 'Poppins', fontSize: 38, fontWeight: 700, color: T.text,
                letterSpacing: -1.2, lineHeight: 1, marginTop: 6,
              }}>
                ~{kcalTarget} <span style={{ fontSize: 16, fontWeight: 500, color: T.muted, letterSpacing: 0 }}>kcal/día</span>
              </div>
              <div style={{ fontSize: 11, color: T.muted, marginTop: 8, lineHeight: 1.5 }}>
                Basado en tu peso, altura, edad y nivel de actividad. Es orientativo — el método Pau Fit no obliga a contarlas.
              </div>
            </div>

            {/* Grid 2x2: BMI · BMR · Proteína · Agua */}
            <div style={{ marginTop: 10, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <div style={{ padding: '14px 16px', background: T.card, borderRadius: 20, border: T.border }}>
                <div style={{ fontSize: 10, fontWeight: 600, color: T.muted, letterSpacing: 0.4,  }}>IMC</div>
                <div style={{ fontFamily: 'Poppins', fontSize: 22, fontWeight: 700, color: T.text, letterSpacing: -0.4, marginTop: 4 }}>
                  {bmi.toFixed(1)}
                </div>
                <div style={{ fontSize: 10, fontWeight: 600, color: bmiCat.c, marginTop: 2 }}>
                  {bmiCat.l}
                </div>
              </div>
              <div style={{ padding: '14px 16px', background: T.card, borderRadius: 20, border: T.border }}>
                <div style={{ fontSize: 10, fontWeight: 600, color: T.muted, letterSpacing: 0.4,  }}>Metabolismo</div>
                <div style={{ fontFamily: 'Poppins', fontSize: 22, fontWeight: 700, color: T.text, letterSpacing: -0.4, marginTop: 4 }}>
                  {tdee}
                </div>
                <div style={{ fontSize: 10, fontWeight: 600, color: T.muted, marginTop: 2 }}>
                  kcal mantener
                </div>
              </div>
              <div style={{ padding: '14px 16px', background: T.card, borderRadius: 20, border: T.border }}>
                <div style={{ fontSize: 10, fontWeight: 600, color: T.muted, letterSpacing: 0.4,  }}>Proteína</div>
                <div style={{ fontFamily: 'Poppins', fontSize: 22, fontWeight: 700, color: T.text, letterSpacing: -0.4, marginTop: 4 }}>
                  {proteinG}g
                </div>
                <div style={{ fontSize: 10, fontWeight: 600, color: T.muted, marginTop: 2 }}>
                  {proteinPerKg}g por kg
                </div>
              </div>
              <div style={{ padding: '14px 16px', background: T.card, borderRadius: 20, border: T.border }}>
                <div style={{ fontSize: 10, fontWeight: 600, color: T.muted, letterSpacing: 0.4,  }}>Agua</div>
                <div style={{ fontFamily: 'Poppins', fontSize: 22, fontWeight: 700, color: T.text, letterSpacing: -0.4, marginTop: 4 }}>
                  {(waterMl/1000).toFixed(1)}L
                </div>
                <div style={{ fontSize: 10, fontWeight: 600, color: T.muted, marginTop: 2 }}>
                  por día
                </div>
              </div>
            </div>

            {/* Advertencias profesionales (si aplican) */}
            {warnings.length > 0 && (
              <div style={{ marginTop: 10 }}>
                {warnings.map((w, i) => (
                  <div key={i} style={{
                    padding: '12px 14px', marginTop: i === 0 ? 0 : 8,
                    background: '#FFF4E5', borderRadius: 14,
                    border: '1px solid rgba(212, 165, 116, 0.3)',
                    fontSize: 12, color: '#8A5A2B', lineHeight: 1.5,
                    display: 'flex', alignItems: 'flex-start', gap: 10,
                  }}>
                    <span style={{ fontSize: 16, lineHeight: 1 }}>💡</span>
                    <span style={{ flex: 1 }}>{w}</span>
                  </div>
                ))}
              </div>
            )}

            {/* Detalles del plan abajo (info ya recogida) */}
            <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 22, marginBottom: 8 }}>Detalles</div>
            <div style={{
              padding: '14px 18px',
              background: T.card, borderRadius: 14, border: T.border,
              fontSize: 12, color: T.text, lineHeight: 1.7,
            }}>
              <div><span style={{ color: T.muted }}>Duración</span> · {data.days} días</div>
              <div><span style={{ color: T.muted }}>Comidas/día</span> · {data.mealsPerDay}</div>
              <div><span style={{ color: T.muted }}>Dieta</span> · {data.dietType === 'todo' ? 'Omnívora' : (data.dietType || 'Omnívora').charAt(0).toUpperCase() + (data.dietType || 'omnívora').slice(1)}</div>
              <div><span style={{ color: T.muted }}>Cocina</span> · {data.cookTime === 'rapido' ? 'Rápida (≤15 min)' : data.cookTime === 'encanta' ? 'Elaborada (≤45 min)' : 'Normal (20-30 min)'}</div>
              {data.restrictions.length > 0 && <div><span style={{ color: T.muted }}>Sin</span> · {data.restrictions.join(', ')}</div>}
              {Array.isArray(data.situations) && data.situations.length > 0 && <div><span style={{ color: T.muted }}>Situación</span> · {data.situations.join(', ')}</div>}
            </div>
          </div>
          );
        })()}

        </div> {/* fin wrapper step animado */}

      </div>

      {/* NAV bar fija al fondo de la pantalla — estilo apps pro */}
      {step <= TOTAL_STEPS && (
        <div style={{
          position: 'absolute', bottom: 0, left: 0, right: 0,
          padding: '16px 22px calc(20px + env(safe-area-inset-bottom))',
          background: `linear-gradient(to top, ${T.bg} 78%, ${T.bg}00)`,
          display: 'flex', gap: 10,
        }}>
          {step > 1 && (
            <button onClick={() => goStep(step - 1)} disabled={loading} style={{
              width: 60, padding: '16px',
              border: T.border, borderRadius: 20, cursor: 'pointer',
              background: T.card, color: T.text,
              fontFamily: 'Poppins', fontSize: 18, fontWeight: 700,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>←</button>
          )}
          {step < TOTAL_STEPS ? (
            <button onClick={() => goStep(step + 1)} disabled={!canAdvance(step)} style={{
              flex: 1, padding: '16px',
              border: 'none', borderRadius: 20, cursor: canAdvance(step) ? 'pointer' : 'not-allowed',
              background: canAdvance(step) ? T.accent : T.muted, color: '#fff',
              fontFamily: 'Poppins', fontSize: 15.5, fontWeight: 700,
              letterSpacing: 0.1,
              boxShadow: canAdvance(step) ? `0 10px 28px ${T.accent}48` : 'none',
              opacity: canAdvance(step) ? 1 : 0.6,
              transition: 'all 200ms cubic-bezier(.34,1.56,.64,1)',
            }}>Continuar</button>
          ) : (
            <button onClick={() => {
              // Bloqueo final por seguridad médica (banner rojo arriba)
              const blockers = Array.isArray(window.__pauPlanBlockers) ? window.__pauPlanBlockers : [];
              if (blockers.length > 0) {
                alert('No podemos generar este plan:\n\n' + blockers.map(b => '• ' + b).join('\n\n'));
                return;
              }
              generate();
            }} disabled={loading || quotaReached || (Array.isArray(window.__pauPlanBlockers) && window.__pauPlanBlockers.length > 0)} style={{
              flex: 1, padding: '16px',
              border: 'none', borderRadius: 20, cursor: (loading || quotaReached) ? 'not-allowed' : 'pointer',
              background: quotaReached ? T.muted : T.accent, color: '#fff',
              fontFamily: 'Poppins', fontSize: 15.5, fontWeight: 700,
              letterSpacing: 0.1,
              boxShadow: quotaReached ? 'none' : `0 10px 28px ${T.accent}48`,
              opacity: (loading || quotaReached) ? 0.85 : 1,
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
            }}>
              {loading && (
                <span style={{
                  width: 16, height: 16, border: '2px solid rgba(255,255,255,0.3)',
                  borderTopColor: '#fff', borderRadius: 50,
                  animation: 'spin 700ms linear infinite',
                }}/>
              )}
              {loading ? 'Creando tu plan…' : quotaReached ? 'Límite alcanzado' : '✨ Crear mi plan'}
            </button>
          )}
        </div>
      )}
    </div>
  );
}

function PlanesList({ T, isPremium, nav, searchQ = '' }) {
  // Los planes son SIEMPRE personalizados por la IA Pau Fit.
  // Si la usuaria YA tiene plan activo, NO mostramos "Crea tu plan" porque
  // el ActivePlanDashboard arriba ya muestra todo. Solo mostramos el CTA
  // cuando NO hay plan activo (primera vez o tras "Salir del plan").
  const [hasActivePlan, setHasActivePlan] = useState(null); // null = loading
  const [reloadTick, setReloadTick] = useState(0);

  useEffect(() => {
    const handler = () => setReloadTick(x => x + 1);
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);

  useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setHasActivePlan(false); return; }
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) { setHasActivePlan(false); return; }
        const { data, error } = await sb.from('user_planes')
          .select('id')
          .eq('user_id', user.id)
          .is('completed_at', null)
          .limit(1);
        if (error) { setHasActivePlan(false); return; }
        setHasActivePlan(Boolean(data && data.length > 0));
      } catch {
        setHasActivePlan(false);
      }
    })();
  }, [reloadTick]);

  // Mientras carga (null), NO mostramos nada para evitar el flash del CTA
  if (hasActivePlan === null) return null;

  // Si ya tiene plan, NO mostrar el CTA "Crea tu plan" — ActivePlanDashboard
  // arriba ya muestra el plan completo. No mostramos nada (limpio).
  if (hasActivePlan) return null;

  // Sin plan: CTA grande estilo Anna, mínimo texto
  return (
    <div style={{ padding: '20px 20px 0' }}>
      <button onClick={() => {
        if (nav.openModal) nav.openModal(<AIPlanWizard T={T} nav={nav} onClose={() => nav.closeModal()}/>);
      }} style={{
        width: '100%', padding: '28px 22px', cursor: 'pointer',
        border: 'none', borderRadius: 28,
        background: `linear-gradient(135deg, ${T.accent}, ${T.deep})`,
        color: '#fff', textAlign: 'left', fontFamily: 'inherit',
        position: 'relative', overflow: 'hidden',
      }}>
        <div style={{
          position: 'absolute', top: -40, right: -30, width: 200, height: 200,
          borderRadius: '50%', background: 'rgba(255,255,255,0.14)',
        }}/>
        <div style={{ position: 'relative' }}>
          <div className="pf-eyebrow" style={{ color: 'rgba(255,255,255,0.85)' }}>
            Tu plan · IA
          </div>
          <div className="pf-display-bold" style={{
            fontSize: 26, color: '#fff', marginTop: 10,
            lineHeight: 1, letterSpacing: -1,
            textTransform: 'none',
          }}>
            Crea tu plan<br/>personalizado
          </div>
          <div style={{
            marginTop: 18, display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '10px 16px', borderRadius: 999,
            background: '#fff', color: T.text,
            fontSize: 12, fontWeight: 800, letterSpacing: 0.3,
          }}>
            Empezar →
          </div>
        </div>
      </button>
    </div>
  );
}

function RecetasList({ T, nav, searchQ = '' }) {
  const cats = ['Todas', 'Desayuno', 'Almuerzo', 'Snack', 'Cena'];
  const [cat, setCat] = useState('Todas');
  const [favs, setFavs] = useState(new Set());
  const toggleFav = (id, title) => {
    setFavs(prev => {
      const n = new Set(prev);
      if (n.has(id)) { n.delete(id); nav.toast('Quitado de favoritos', { icon: '🤍' }); }
      else { n.add(id); nav.toast(`${title} guardado 💗`, { icon: '❤️' }); }
      return n;
    });
  };

  // ── SOLO recetas publicadas por Pau (tabla `recetas`) ──
  // Decisión de Pau (2026-07-04): fuera las recetas auto-generadas de los
  // planes (sin foto y a montones) y fuera el catálogo de muestra.
  const [dbRecetas, setDbRecetas] = useState(null);
  useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setDbRecetas([]); return; }
    let cancel = false;
    (async () => {
      try {
        if (window.__pauCache?.recetas) { if (!cancel) setDbRecetas(window.__pauCache.recetas); return; }
        const { data } = await sb.from('recetas')
          .select('id, title, emoji, image_url, kcal, prep_minutes, category, description')
          .eq('is_published', true)
          .order('created_at', { ascending: false });
        const mapped = (data || []).map(r => ({ ...r, minutes: r.prep_minutes || 15, desc: r.description }));
        window.__pauCache = window.__pauCache || {};
        window.__pauCache.recetas = mapped;
        if (!cancel) setDbRecetas(mapped);
      } catch (e) {
        console.warn('[recetas-db]', e?.message);
        if (!cancel) setDbRecetas([]);
      }
    })();
    return () => { cancel = true; };
  }, []);

  const q = (searchQ || '').toLowerCase().trim();
  const combined = dbRecetas || [];
  const list = combined.filter(r => {
    if (cat !== 'Todas' && r.category !== cat) return false;
    if (q && !(`${r.title} ${r.desc || ''} ${r.category || ''}`).toLowerCase().includes(q)) return false;
    return true;
  });

  function openRecipe(r) {
    nav.go('receta', r.id);
  }

  return (
    <div>
      {/* Filtros estilo Anna — pills minimal sin scroll horizontal aparente */}
      <div style={{ display: 'flex', gap: 8, padding: '20px 20px 4px', overflowX: 'auto' }} className="hide-scroll">
        {cats.map(f => <Pill key={f} active={cat===f} onClick={() => setCat(f)} dark={T.dark} color={T.accent}>{f}</Pill>)}
      </div>
      {dbRecetas !== null && list.length === 0 && (
        <div style={{ padding: '40px 24px', textAlign: 'center', color: T.muted, fontSize: 13 }}>
          <div style={{ fontSize: 36, marginBottom: 10 }}>🥣</div>
          Muy pronto — recetas nuevas de Pau cada semana 💗
        </div>
      )}
      {/* Grid — 2 columnas, fotos grandes, mínimo texto */}
      <div style={{ padding: '18px 20px 0', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        {list.map(r => (
          <div key={r.id} onClick={() => openRecipe(r)} style={{
            cursor: 'pointer', borderRadius: 22, overflow: 'hidden',
            background: T.card, position: 'relative',
          }}>
            <div style={{ position: 'relative', aspectRatio: '3 / 4' }}>
              {r.image_url ? (
                <img src={r.image_url} alt={r.title} style={{
                  position: 'absolute', inset: 0,
                  width: '100%', height: '100%', objectFit: 'cover',
                }}/>
              ) : (
                <RecipeImage receta={r} T={T} style={{ position: 'absolute', inset: 0 }}/>
              )}
              {/* Gradiente muy sutil — Anna minimalista */}
              <div style={{
                position: 'absolute', inset: 0,
                background: 'linear-gradient(180deg, transparent 65%, rgba(0,0,0,0.22) 100%)',
              }}/>
              {/* Favorito flotante */}
              <button onClick={(e) => { e.stopPropagation(); toggleFav(r.id, r.title); }} style={{
                position: 'absolute', top: 10, right: 10,
                width: 32, height: 32, borderRadius: 999, border: 'none',
                background: 'rgba(255,255,255,0.95)', cursor: 'pointer',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
              }}>{Icon.heart(favs.has(r.id) ? T.accent : '#0E0A0C', favs.has(r.id))}</button>
              {/* Título sobre la foto — sombra mínima */}
              <div style={{ position: 'absolute', bottom: 10, left: 12, right: 12, color: '#fff' }}>
                <div className="pf-display-bold" style={{
                  fontSize: 11, color: '#fff', lineHeight: 1.1,
                  textShadow: '0 1px 2px rgba(0,0,0,0.35)',
                  display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
                  overflow: 'hidden',
                }}>{r.title}</div>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// COMUNIDAD — chat + ranking + reto en pareja + Instagram
// ═════════════════════════════════════════════════════════════
function ComunidadScreen({ theme, nav }) {
  const T = theme;

  return (
    <div style={{ paddingBottom: 120 }}>
      <PauTopBar T={T} nav={nav}/>
      <div style={{ padding: '14px 20px 0' }}>
        <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95 }}>
          Comunidad
        </div>
      </div>

      {/* Ranking banner — abre top 100 */}
      <div onClick={() => nav.go('ranking')} style={{
        margin: '20px 20px 0', padding: '20px',
        background: `linear-gradient(135deg, ${T.accent}, ${T.deep})`,
        borderRadius: 20, cursor: 'pointer', position: 'relative', overflow: 'hidden',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14, position: 'relative' }}>
          <div style={{
            width: 56, height: 56, borderRadius: 20,
            background: 'rgba(255,255,255,0.25)', backdropFilter: 'blur(10px)',
            display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 30,
          }}>🏆</div>
          <div style={{ flex: 1, color: '#fff' }}>
            <span className="pf-eyebrow" style={{ color: 'rgba(255,255,255,0.85)' }}>Ranking Pau Fit</span>
            <div style={{ fontSize: 17, fontWeight: 800, marginTop: 4, letterSpacing: -0.4 }}>Top 100</div>
            <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.85)', marginTop: 2 }}>
              Compite por puntos · tu posición {((window.__pauCache?.profile?.is_public) ?? true) ? 'es pública' : 'es privada'}
            </div>
          </div>
          {Icon.chevR('#fff', 11)}
        </div>
      </div>

      {/* Chat retirado a petición de Pau (2026-07-09): sin equipo de
          moderación no quiere chat abierto. El código (ChatScreen) queda
          listo para reactivarlo cuando tenga moderadoras. */}

      {/* Reto en pareja — compartir invitación */}
      <div style={{ padding: '14px 20px 0' }}>
        <button onClick={() => {
          if (nav.openModal) nav.openModal(<RetoEnParejaSheet T={T} nav={nav} onClose={() => nav.closeModal()}/>);
        }} style={{
          width: '100%',
          padding: '20px', border: T.border, borderRadius: 20,
          background: T.card, cursor: 'pointer', textAlign: 'left',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
            <span style={{ fontSize: 32 }}>👯‍♀️</span>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 700, color: T.text, letterSpacing: -0.2 }}>Reto en pareja</div>
              <div style={{ fontSize: 12, color: T.muted, marginTop: 2 }}>Invita a una amiga a entrenar contigo</div>
            </div>
            {Icon.chevR(T.muted, 8)}
          </div>
        </button>
      </div>

      {/* Redes sociales oficiales — funcionales */}
      <div style={{ padding: '14px 20px 0' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 }}>
          {[
            { name: 'Instagram', handle: '@paufityt', url: 'https://www.instagram.com/paufityt', emoji: '📸' },
            { name: 'TikTok', handle: '@paulamorenoyt', url: 'https://www.tiktok.com/@paulamorenoyt', emoji: '🎵' },
            { name: 'YouTube', handle: '@paufityt', url: 'https://www.youtube.com/@paufityt', emoji: '▶️' },
          ].map(s => (
            <button key={s.name} onClick={() => {
              if (window.PauNative?.isNativeApp) window.PauNative.openExternal(s.url);
              else window.open(s.url, '_blank', 'noopener');
            }} style={{
              padding: '14px 8px', border: T.border, borderRadius: 20,
              background: T.card, cursor: 'pointer', textAlign: 'center',
              display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
              fontFamily: 'inherit',
            }}>
              <div style={{ fontSize: 11, fontWeight: 700, color: T.text }}>{s.name}</div>
              <div style={{ fontSize: 9, color: T.muted, fontWeight: 600 }}>{s.handle}</div>
            </button>
          ))}
        </div>
      </div>

      {/* Hashtag Instagram */}
      <div style={{
        margin: '14px 20px 0', padding: '16px 20px',
        background: T.subtle, borderRadius: 20,
        display: 'flex', alignItems: 'center', gap: 12,
      }}>
        <span style={{ fontSize: 22 }}>💗</span>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 14, fontWeight: 800, color: T.accent, letterSpacing: -0.2 }}>#YoSiemprePuedo</div>
          <div style={{ fontSize: 11, color: T.muted, marginTop: 2, fontWeight: 500 }}>Etiqueta tu progreso en Instagram</div>
        </div>
      </div>

      {/* Empty state Testimonios — se llenará cuando lleguen reseñas reales */}
      <div style={{ padding: '40px 20px 0', textAlign: 'center' }}>
        <div style={{ fontSize: 40, marginBottom: 10, opacity: 0.6 }}>💬</div>
        <div style={{ fontSize: 13, fontWeight: 700, color: T.text }}>Sé la primera en dejar testimonio</div>
        <div style={{ fontSize: 11, color: T.muted, marginTop: 6, lineHeight: 1.5 }}>
          Completa un reto y comparte tu experiencia con la comunidad.
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// RetoEnParejaSheet — modal para invitar a una amiga
// Genera link con token único y muestra reto activo / sugerido
// ─────────────────────────────────────────────────────────────
function RetoEnParejaSheet({ T, nav, onClose }) {
  const cache = (typeof window !== 'undefined' && window.__pauCache) || {};
  const name = cache.profile?.full_name || cache.profile?.username || 'tu amiga';
  const userId = cache.profile?.id || 'anon';
  // Reto activo o reto del mes como sugerencia
  const activeReto = cache.activeReto;
  const featured = activeReto
    ? window.pauRetoById(activeReto.reto_id)
    : (() => {
        const now = new Date();
        const idx = (now.getFullYear() * 12 + now.getMonth()) % RETOS.length;
        return window.pauRetos()[idx];
      })();
  // Generar token simple (idempotente por usuario)
  const token = btoa(`${userId}-${featured?.id || 'free'}`).replace(/=/g, '').slice(0, 12);
  const inviteUrl = `https://app.paufit.co/?invite=${token}&reto=${featured?.id || ''}`;
  const shareText = `💗 ${name} te invita a hacer "${featured?.title || 'un reto fitness'}" en Pau Fit\n\n${featured?.days || 14} días juntas. ¡Sin excusas!`;

  async function shareNow() {
    if (window.PauShare) {
      const r = await window.PauShare({ title: 'Reto en pareja · Pau Fit', text: shareText, url: inviteUrl });
      if (r.shared) {
        nav.toast(r.method === 'native' ? '¡Invitación enviada! 💗' : 'Enlace copiado 💗', { icon: '👯‍♀️' });
        onClose();
      }
    }
  }
  async function copyLink() {
    try {
      await navigator.clipboard.writeText(inviteUrl);
      nav.toast('Enlace copiado 💗', { icon: '📋' });
    } catch {
      nav.toast('No se pudo copiar', { icon: '⚠️' });
    }
  }
  function viaWhatsApp() {
    const msg = encodeURIComponent(`${shareText}\n\n${inviteUrl}`);
    window.open(`https://wa.me/?text=${msg}`, '_blank', 'noopener');
  }
  function viaInstagram() {
    // Instagram no soporta deep link a chat. Copiamos y avisamos.
    copyLink();
    nav.toast('Pégalo en tu historia o DM 💗', { icon: '📷' });
  }

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 9999,
      background: 'rgba(0,0,0,0.6)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', maxWidth: 480, maxHeight: '85vh', overflow: 'auto',
        background: T.card,
        borderTopLeftRadius: 24, borderTopRightRadius: 24, padding: '20px 22px 32px',
      }}>
        <div style={{ textAlign: 'center', marginBottom: 18 }}>
          <div style={{ fontSize: 44, lineHeight: 1 }}>👯‍♀️</div>
          <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.4, marginTop: 8 }}>
            Reto en pareja
          </div>
          <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.5 }}>
            Todo es más fácil con una amiga. Invítala a entrenar contigo 💗
          </div>
        </div>

        {/* Card del reto */}
        {featured && (
          <div style={{
            position: 'relative', borderRadius: 20, overflow: 'hidden',
            aspectRatio: '16 / 9', marginBottom: 18,
            background: 'rgba(0,0,0,0.04)',
          }}>
            {featured.image && (
              <img src={featured.image} alt={featured.title} style={{
                position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover',
              }}/>
            )}
            <div style={{
              position: 'absolute', inset: 0,
              background: 'linear-gradient(180deg, rgba(0,0,0,0.15) 0%, transparent 35%, rgba(0,0,0,0.75) 100%)',
            }}/>
            <div style={{
              position: 'absolute', top: 10, left: 10,
              padding: '3px 9px', borderRadius: 999,
              background: 'rgba(255,255,255,0.92)', color: '#0E0A0C',
              fontSize: 9, fontWeight: 700, letterSpacing: 1,
            }}>{activeReto ? 'TU RETO ACTUAL' : 'SUGERIDO'}</div>
            <div style={{
              position: 'absolute', bottom: 10, left: 12, right: 12, color: '#fff',
            }}>
              <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: -0.3, textShadow: '0 2px 8px rgba(0,0,0,0.4)' }}>
                {featured.title}
              </div>
              <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.92)', marginTop: 2 }}>
                {featured.days} días · ~{featured.minPerDay} min/día
              </div>
            </div>
          </div>
        )}

        {/* Opciones de compartir */}
        <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 10 }}>Invitar por</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 }}>
          <button onClick={shareNow} style={{
            padding: '14px 8px', borderRadius: 14, cursor: 'pointer',
            border: 'none', background: T.accent, color: '#fff', fontFamily: 'inherit',
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
          }}>
            <span style={{ fontSize: 22 }}>↗</span>
            <span style={{ fontSize: 11, fontWeight: 700 }}>Compartir</span>
          </button>
          <button onClick={viaWhatsApp} style={{
            padding: '14px 8px', borderRadius: 14, cursor: 'pointer',
            border: T.border, background: T.card, color: T.text, fontFamily: 'inherit',
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
          }}>
            <span style={{ fontSize: 22 }}>💬</span>
            <span style={{ fontSize: 11, fontWeight: 700 }}>WhatsApp</span>
          </button>
          <button onClick={viaInstagram} style={{
            padding: '14px 8px', borderRadius: 14, cursor: 'pointer',
            border: T.border, background: T.card, color: T.text, fontFamily: 'inherit',
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
          }}>
            <span style={{ fontSize: 22 }}>📷</span>
            <span style={{ fontSize: 11, fontWeight: 700 }}>Instagram</span>
          </button>
        </div>

        {/* Link copiable */}
        <div style={{
          marginTop: 14, padding: '12px 14px',
          background: T.subtle, borderRadius: 14,
          display: 'flex', alignItems: 'center', gap: 10,
        }}>
          <div style={{
            flex: 1, minWidth: 0, overflow: 'hidden',
            textOverflow: 'ellipsis', whiteSpace: 'nowrap',
            fontSize: 11, color: T.muted, fontFamily: 'monospace',
          }}>{inviteUrl}</div>
          <button onClick={copyLink} style={{
            padding: '6px 12px', borderRadius: 999, border: 'none', cursor: 'pointer',
            background: T.accent, color: '#fff',
            fontSize: 11, fontWeight: 700,
          }}>Copiar</button>
        </div>

        {/* Cómo funciona */}
        <div style={{
          marginTop: 18, padding: '12px 14px',
          background: 'rgba(168,197,160,0.12)', borderRadius: 14,
        }}>
          <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 6 }}>Cómo funciona</div>
          <div style={{ fontSize: 11, color: T.text, lineHeight: 1.5 }}>
            1. Compartes este link con una amiga<br/>
            2. Ella se registra y se une al mismo reto<br/>
            3. Entrenan a la vez y se motivan juntas
          </div>
        </div>
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// PERFIL
// ═════════════════════════════════════════════════════════════
function PerfilScreen({ theme, isPremium, isPau, nav }) {
  const T = theme;
  // Bump para re-render cuando cache cambia (avatar nuevo, edit profile)
  const [, setCacheBump] = useState(0);
  useEffect(() => {
    const handler = () => setCacheBump(x => x + 1);
    window.addEventListener('pau:cache-updated', handler);
    return () => window.removeEventListener('pau:cache-updated', handler);
  }, []);
  // ── DATOS REALES ──
  const cache = (typeof window !== 'undefined' && window.__pauCache) || {};
  const profile = cache.profile || {};
  const stats = cache.stats || { totalDays: 0, retosCompleted: 0, streak: 0 };
  const achievements = cache.achievements || [];
  const unlockedCount = achievements.filter(a => a.unlocked).length;
  const fullName = profile.full_name || profile.username || '';
  const userEmail = profile.email || '';
  const avatarUrl = profile.avatar_url || null;
  // Solo iniciales reales (no del email)
  const initials = fullName
    ? fullName.split(' ').map(s => s[0]).slice(0,2).join('').toUpperCase()
    : null;

  return (
    <div style={{ paddingBottom: 120 }}>
      <div style={{ padding: '14px 20px 0', display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between' }}>
        <div>
          <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95 }}>
            Perfil
          </div>
        </div>
        <button onClick={() => nav.go('ajustes')} style={{
          width: 38, height: 38, borderRadius: 999, border: 'none',
          background: T.card, cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          border: T.border,
        }}>
          <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
            <circle cx="10" cy="10" r="2.5" stroke={T.text} strokeWidth="1.6"/>
            <path d="M10 1.5v3M10 15.5v3M3.5 10h-2M18.5 10h-2M5.4 5.4l-1.4-1.4M16 16l-1.4-1.4M5.4 14.6l-1.4 1.4M16 4l-1.4 1.4" stroke={T.text} strokeWidth="1.6" strokeLinecap="round"/>
          </svg>
        </button>
      </div>

      {/* Avatar + name */}
      <div style={{
        margin: '20px 20px 0', padding: '24px',
        background: T.card, borderRadius: 20, border: T.border,
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12,
      }}>
        <div style={{ position: 'relative' }}>
          <button onClick={() => nav.go('editarPerfil')} style={{
            width: 90, height: 90, borderRadius: 999,
            background: avatarUrl ? '#fff' : `linear-gradient(135deg, ${T.accent}, ${T.deep})`,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 30, fontWeight: 800, color: '#fff', letterSpacing: -0.5,
            border: 'none', cursor: 'pointer', padding: 0, overflow: 'hidden',
          }}>
            {avatarUrl
              ? <img src={avatarUrl} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
              : initials
                ? initials
                : <img src="/branding/logo.png" alt="Pau Fit" style={{ width: 48, height: 48, objectFit: 'contain' }}/>
            }
          </button>
          <button onClick={() => nav.go('editarPerfil')} style={{
            position: 'absolute', bottom: -2, right: -2,
            width: 30, height: 30, borderRadius: 999, border: `2px solid ${T.card}`,
            background: T.text, color: T.card, cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <svg width="13" height="13" viewBox="0 0 13 13"><path d="M9 1.5l2.5 2.5L4 11.5H1.5V9L9 1.5z" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinejoin="round"/></svg>
          </button>
        </div>
        <div style={{ textAlign: 'center' }}>
          <div className="pf-display" style={{ fontSize: 24, fontWeight: 800, color: T.text, letterSpacing: -0.6 }}>
            {fullName || 'Tu perfil'}
          </div>
          {/* Usuario + antigüedad — pedido de Pau */}
          {profile.username && (
            <div style={{ fontSize: 12, color: T.accent, marginTop: 3, fontWeight: 700 }}>@{profile.username}</div>
          )}
          {profile.created_at && (
            <div style={{ fontSize: 10.5, color: T.muted, marginTop: 3, fontWeight: 600 }}>
              Miembro desde {new Date(profile.created_at).toLocaleDateString('es', { month: 'long', year: 'numeric' })}
            </div>
          )}
          {userEmail && (
            <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>{userEmail}</div>
          )}
          <div style={{
            display: 'inline-flex', alignItems: 'center', gap: 6, marginTop: 6,
            padding: '4px 12px', borderRadius: 999,
            background: isPremium ? '#0E0A0C' : T.subtle,
            color: isPremium ? (T.gold || '#C9956B') : T.muted,
            fontSize: 10, fontWeight: 800, letterSpacing: 0.8,
          }}>
            {isPremium ? '★ PREMIUM' : 'PLAN GRATUITO'}
          </div>
        </div>
      </div>

      {/* Stats REALES (vacías si usuaria nueva) */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10, padding: '14px 20px 0' }}>
        <StatBlock value={stats.retosCompleted || 0} label="Retos" T={T}/>
        <StatBlock value={stats.totalDays || 0} label="Días activos" T={T}/>
        <StatBlock value={stats.streak || 0} label="Racha" T={T} accent/>
      </div>

      {/* Insignias REALES (solo las desbloqueadas — vacío si nuevas) */}
      <div style={{ padding: '24px 22px 8px', display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
        <span className="pf-eyebrow" style={{ color: T.muted }}>Insignias · {unlockedCount}</span>
        <button onClick={() => nav.go('logros')} style={{
          border: 'none', background: 'transparent', cursor: 'pointer',
          fontSize: 11, fontWeight: 700, color: T.accent, letterSpacing: 0.3, 
        }}>Todos</button>
      </div>
      {unlockedCount > 0 ? (
        <div style={{ display: 'flex', gap: 14, padding: '8px 22px 4px', overflowX: 'auto' }} className="hide-scroll">
          {achievements.filter(a => a.unlocked).slice(0, 6).map(a => (
            <InsigniaCard key={a.id} b={a} T={T}/>
          ))}
        </div>
      ) : (
        <div style={{
          margin: '0 20px', padding: '24px 18px', background: T.subtle,
          borderRadius: 20, textAlign: 'center',
          fontSize: 12, color: T.muted, lineHeight: 1.5,
        }}>
          🌱 Empieza tu primer reto para desbloquear insignias.
        </div>
      )}

      {/* Mi progreso */}
      <div style={{ padding: '20px 22px 8px' }}>
        <span className="pf-eyebrow" style={{ color: T.muted }}>Mi progreso</span>
      </div>
      <div style={{ margin: '0 20px', background: T.card, borderRadius: 20, overflow: 'hidden', border: T.border }}>
        {[
          { e: '📐', t: 'Progreso fotográfico', detail: 'Fotos y medidas', kind: 'progreso' },
          { e: '🏆', t: 'Mis logros', detail: `${unlockedCount} / ${achievements.length || 0}`, kind: 'logros' },
          { e: '📋', t: 'Historial completo', detail: 'rutinas, retos, recetas', kind: 'historial' },
          { e: '🏅', t: 'Ranking', detail: 'Top 100', kind: 'ranking' },
          { e: '🔖', t: 'Guardados', detail: 'retos, rutinas y recetas', kind: 'guardados' },
          { e: '🛒', t: 'Lista de la compra', detail: 'desde tu plan activo', kind: 'lista-compra' },
          { e: '📤', t: 'Compartir mi semana', detail: 'formato historia', action: () => {
            if (nav.openModal && window.WeeklyRecapSheet) {
              nav.openModal(React.createElement(window.WeeklyRecapSheet, { T, nav, onClose: () => nav.closeModal() }));
            }
          } },
        ].map((o, i, arr) => (
          <div key={i} onClick={() => o.action ? o.action() : nav.go(o.kind)} style={{
            display: 'flex', alignItems: 'center', gap: 12,
            padding: '14px 16px', cursor: 'pointer',
            borderBottom: i < arr.length-1 ? (T.dark ? '0.5px solid rgba(255,255,255,0.06)' : '0.5px solid rgba(0,0,0,0.05)') : 'none',
          }}>
            <span style={{ flex: 1, fontSize: 14, fontWeight: 600, color: T.text }}>{o.t}</span>
            <span style={{ fontSize: 11, color: T.muted, fontWeight: 500 }}>{o.detail}</span>
            {Icon.chevR(T.muted, 9)}
          </div>
        ))}
      </div>

      {/* Premium card */}
      {!isPremium && (
        <div onClick={() => nav.go('paywall')} style={{
          margin: '14px 20px 0', padding: '20px',
          background: '#0E0A0C',
          borderRadius: 20, cursor: 'pointer', position: 'relative', overflow: 'hidden',
        }}>
          <span className="pf-eyebrow" style={{ color: T.accent, position: 'relative' }}>PAU FIT PREMIUM</span>
          <div className="pf-display" style={{ fontSize: 24, fontWeight: 800, color: '#fff', marginTop: 6, letterSpacing: -0.6, position: 'relative' }}>
            Desde 3,33€/mes
          </div>
          <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.65)', marginTop: 4, position: 'relative' }}>7 días gratis · cancela cuando quieras</div>
        </div>
      )}

      {/* Options */}
      <div style={{ margin: '14px 20px 0', background: T.card, borderRadius: 20, overflow: 'hidden', border: T.border }}>
        {[
          { e: '🔔', t: 'Notificaciones', kind: 'notif' },
          { e: '🌐', t: 'Idioma', detail: 'Español', action: () => nav.toast('Idioma: Español · más idiomas en camino', { icon: '🌐' }) },
          { e: '💗', t: 'Compartir Pau Fit', action: () => {
            const cache = window.__pauCache || {};
            const name = cache.profile?.full_name || cache.profile?.username || '';
            const text = name
              ? `${name} te recomienda Pau Fit — la app fitness con retos guiados 💗`
              : 'Mira Pau Fit — la app fitness con retos guiados 💗';
            window.PauShare && window.PauShare({ title: 'Pau Fit', text, url: 'https://app.paufit.co' }).then(function(r){ if (r.shared) nav.toast(r.method === 'native' ? 'Compartido' : 'Enlace copiado 💗', { icon: '↗' }) });
          }},
          { e: '💬', t: 'Ayuda y soporte', action: () => {
            const url = 'mailto:hello@paufit.co?subject=Ayuda%20Pau%20Fit';
            if (window.PauNative && window.PauNative.isNativeApp) window.PauNative.openExternal(url);
            else window.open(url, '_blank', 'noopener');
          }},
          ...(isPau ? [{ e: '👑', t: 'Panel admin (Pau)', kind: 'admin' }] : []),
          { e: '↗️', t: 'Cerrar sesión', danger: true, kind: 'logout' },
        ].map((o, i, arr) => (
          <div key={i} onClick={async () => {
            if (o.kind === 'logout') {
              // LOGOUT REAL: cerrar sesión en Supabase + limpiar cache
              const ok = window.confirm('¿Cerrar sesión?');
              if (!ok) return;
              try {
                const sb = window.__PAU_SUPABASE;
                if (sb) await sb.auth.signOut();
                window.__pauCache = {};
                try { localStorage.removeItem('paufit-auth'); } catch {}
                nav.toast('Sesión cerrada', { icon: '👋' });
                // El listener onAuthStateChange en app.jsx volverá a LoginScreen
              } catch (e) {
                nav.toast('Error: ' + (e.message || e), { icon: '⚠️' });
              }
              return;
            }
            if (o.kind) nav.go(o.kind);
            else if (o.action) o.action();
          }} style={{
            display: 'flex', alignItems: 'center', gap: 12,
            padding: '14px 16px', cursor: 'pointer',
            borderBottom: i < arr.length-1 ? (T.dark ? '0.5px solid rgba(255,255,255,0.06)' : '0.5px solid rgba(0,0,0,0.05)') : 'none',
          }}>
            <span style={{ flex: 1, fontSize: 14, fontWeight: 600, color: o.danger ? '#E63149' : T.text }}>{o.t}</span>
            {o.detail && <span style={{ fontSize: 11, color: T.muted, fontWeight: 500 }}>{o.detail}</span>}
            {!o.danger && Icon.chevR(T.muted, 8)}
          </div>
        ))}
      </div>

      <div style={{ textAlign: 'center', padding: '24px 0 8px', fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.4 }}>
        Pau Fit · v1.0.0 · #YoSiemprePuedo
      </div>
    </div>
  );
}

function StatBlock({ value, label, T, accent }) {
  return (
    <div style={{
      background: T.card, borderRadius: 20, padding: '16px 12px',
      textAlign: 'center', border: T.border,
    }}>
      <div className="pf-display" style={{ fontSize: 30, fontWeight: 800, color: accent ? T.accent : T.text, letterSpacing: -0.8, lineHeight: 1 }}>{value}</div>
      <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 6 }}>{label}</div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// SweetCravingSheet — modal con ideas de antojo dulce saludable.
// Cada idea es una RECETA completa (ingredientes + pasos) y tiene foto
// cacheada en recipe_images como cualquier receta del plan.
// Tap en una idea → abre MealRecipeModal con todos los detalles.
// ─────────────────────────────────────────────────────────────
function SweetCravingSheet({ T, nav, onClose }) {
  const ideas = (window.PauFriend && window.PauFriend.sweetIdeas()) || [];
  // SIEMPRE las mismas 5 (pedido de Pau) — solo rotan si tocas "Ver otras".
  // El offset se guarda en localStorage para que sea estable entre sesiones.
  const [offset, setOffset] = useState(() => {
    try { return parseInt(localStorage.getItem('paufit-antojos-offset') || '0', 10) || 0; }
    catch { return 0; }
  });
  const shown = React.useMemo(() => {
    if (!ideas.length) return [];
    const n = Math.min(5, ideas.length);
    return Array.from({ length: n }, (_, k) => ideas[(offset + k) % ideas.length]);
  }, [offset]);
  function verOtras() {
    const next = (offset + 5) % (ideas.length || 1);
    setOffset(next);
    try { localStorage.setItem('paufit-antojos-offset', String(next)); } catch {}
  }

  // Cargar fotos de cache si existen (no genera nuevas, solo lee)
  const [photos, setPhotos] = useState({});
  useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) return;
    (async () => {
      const keys = shown.map(s => (s.name || '').toLowerCase()
        .normalize('NFD').replace(/[̀-ͯ]/g, '')
        .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80))
        .filter(Boolean);
      if (keys.length === 0) return;
      try {
        const { data } = await sb.from('recipe_images')
          .select('recipe_key, image_url')
          .in('recipe_key', keys);
        const map = {};
        (data || []).forEach(r => { map[r.recipe_key] = r.image_url; });
        setPhotos(map);
      } catch {}
    })();
  }, [shown]);

  function openRecipe(idea) {
    onClose();
    // Abre MealRecipeModal con la idea completa (que ya tiene ingredients e instructions).
    // El modal generará/cargará la foto automáticamente igual que con las del plan.
    setTimeout(() => {
      if (nav.openModal) nav.openModal(<MealRecipeModal T={T} meal={idea} nav={nav} onClose={() => nav.closeModal()}/>);
    }, 60);
  }

  function keyOf(name) {
    return (name || '').toLowerCase()
      .normalize('NFD').replace(/[̀-ͯ]/g, '')
      .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80);
  }

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 9999,
      background: 'rgba(0,0,0,0.55)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
      animation: 'fade-in 220ms ease',
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', maxWidth: 480,
        background: T.bg, color: T.text,
        borderTopLeftRadius: 28, borderTopRightRadius: 28,
        padding: '14px 22px calc(24px + env(safe-area-inset-bottom))',
        animation: 'slide-up 380ms cubic-bezier(.34,1.56,.64,1)',
        maxHeight: '88dvh', overflowY: 'auto',
      }}>
        <div style={{ width: 38, height: 4, borderRadius: 2, background: T.muted, opacity: 0.3, margin: '0 auto 14px' }}/>
        <div className="pf-display" style={{
          fontSize: 28, fontWeight: 800, color: T.text, letterSpacing: -1, lineHeight: 0.95,
        }}>
          Antojo dulce
        </div>
        <div style={{ fontSize: 12, color: T.muted, marginTop: 6, fontWeight: 500, lineHeight: 1.4 }}>
          5 recetas Pau Fit cuando te ataca lo dulce — toca para ver
        </div>
        {/* Grid 2 columnas con fotos reales */}
        <div style={{ marginTop: 18, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          {shown.map((it, i) => {
            const photo = photos[keyOf(it.name)];
            return (
              <button key={i} onClick={() => openRecipe(it)} style={{
                cursor: 'pointer', border: 'none', padding: 0,
                position: 'relative', aspectRatio: '3 / 4',
                borderRadius: 20, overflow: 'hidden', fontFamily: 'inherit',
                background: photo ? '#FFFFFF' : `linear-gradient(135deg, ${T.accent}15, ${T.accent}05)`,
              }}>
                {photo ? (
                  <img src={photo} alt={it.name} style={{
                    position: 'absolute', inset: 0,
                    width: '100%', height: '100%', objectFit: 'cover',
                  }}/>
                ) : (
                  <div style={{
                    position: 'absolute', inset: 0,
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    fontSize: 56, opacity: 0.85,
                  }}>{it.emoji || '🍯'}</div>
                )}
                <div style={{
                  position: 'absolute', inset: 0,
                  background: 'linear-gradient(180deg, transparent 55%, rgba(0,0,0,0.45) 100%)',
                }}/>
                <div style={{
                  position: 'absolute', bottom: 10, left: 10, right: 10,
                  textAlign: 'left', color: '#fff',
                }}>
                  <div style={{
                    fontFamily: 'Poppins', fontSize: 11.5, fontWeight: 700,
                    lineHeight: 1.15, letterSpacing: -0.15,
                    textShadow: '0 1px 3px rgba(0,0,0,0.4)',
                    display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
                    overflow: 'hidden',
                  }}>{it.name}</div>
                  {it.kcal && (
                    <div style={{
                      fontSize: 9, fontWeight: 700, marginTop: 3,
                      color: 'rgba(255,255,255,0.9)', letterSpacing: 0.3,
                      textShadow: '0 1px 2px rgba(0,0,0,0.4)',
                    }}>{it.kcal} kcal · {it.time}</div>
                  )}
                </div>
              </button>
            );
          })}
        </div>
        {/* Solo cambian si TÚ quieres — rotación manual */}
        {ideas.length > 5 && (
          <button onClick={verOtras} style={{
            marginTop: 14, width: '100%', padding: '13px',
            border: T.border, borderRadius: 999, cursor: 'pointer',
            background: T.card, color: T.text,
            fontFamily: 'Poppins', fontSize: 12, fontWeight: 800, letterSpacing: 0.4,
          }}>🔀 VER OTRAS IDEAS</button>
        )}
        <button onClick={onClose} style={{
          marginTop: 10, width: '100%', padding: '14px',
          border: 'none', borderRadius: 999, cursor: 'pointer',
          background: T.text, color: T.bg,
          fontFamily: 'Poppins', fontSize: 13, fontWeight: 800, letterSpacing: 0.4,
        }}>CERRAR</button>
      </div>
    </div>
  );
}

Object.assign(window, { HomeScreen, RetosScreen, RecetasScreen, ComunidadScreen, PerfilScreen, MealRecipeModal, SweetCravingSheet });
