// Pau Fit — Sub-screens (Logros, Progreso, Historial, Rutina detail)

const { useState: useS3, useEffect: useE3 } = React;

// ═════════════════════════════════════════════════════════════
// LOGROS
// ═════════════════════════════════════════════════════════════
function LogrosScreen({ theme, nav }) {
  const T = theme;
  // Insignias REALES desde cache (calculadas en app.jsx con PauUserRetos.computeAchievements)
  // Si no hay cache, fallback al template del mock pero con todas locked
  const cache = (typeof window !== 'undefined' && window.__pauCache) || {};
  const realAchievements = cache.achievements;
  const LOGROS_DATA = Array.isArray(realAchievements) && realAchievements.length > 0
    ? realAchievements
    : LOGROS.map(l => ({ ...l, unlocked: false, progress: 0 }));
  const unlocked = LOGROS_DATA.filter(l => l.unlocked).length;
  const total = LOGROS_DATA.length;
  const pct = total > 0 ? unlocked / total : 0;
  const points = (cache.profile && cache.profile.points) || 0;
  const stats = cache.stats || { streak: 0, totalDays: 0 };

  return (
    <div style={{ paddingBottom: 110 }}>
      <div style={{ padding: '12px 20px 6px' }}>
        <BackBtn onClick={() => nav.back()}/>
        <div style={{ marginTop: 14 }}>
          <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95 }}>
            Logros
          </div>
          <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 10 }}>
            {unlocked} de {total} alcanzados
          </div>
          <div style={{ marginTop: 12 }}>
            <div style={{ height: 5, background: `${T.text}10`, borderRadius: 999, overflow: 'hidden' }}>
              <div style={{ width: `${pct*100}%`, height: '100%', background: T.accent, borderRadius: 999, transition: 'width 600ms ease' }}/>
            </div>
          </div>
        </div>
      </div>

      {/* Puntos + Racha + Ranking button — explicación clara */}
      <div style={{ padding: '20px 20px 0' }}>
        <div style={{
          display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10,
        }}>
          <div style={{
            padding: '14px 16px', background: T.card, borderRadius: 20, border: T.border,
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
          }}>
            <span style={{ fontSize: 22 }}>⭐</span>
            <div style={{ fontSize: 20, fontWeight: 800, color: T.text, letterSpacing: -0.3 }}>{points}</div>
            <div style={{ fontSize: 10, fontWeight: 600, color: T.muted, letterSpacing: 0.4,  }}>Puntos</div>
          </div>
          <div style={{
            padding: '14px 16px', background: T.card, borderRadius: 20, border: T.border,
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
          }}>
            <span style={{ fontSize: 22 }}>🔥</span>
            <div style={{ fontSize: 20, fontWeight: 800, color: T.text, letterSpacing: -0.3 }}>{stats.streak || 0}</div>
            <div style={{ fontSize: 10, fontWeight: 600, color: T.muted, letterSpacing: 0.4,  }}>
              {(stats.streak === 1) ? 'Día racha' : 'Días racha'}
            </div>
          </div>
        </div>

        {/* Botón Ver Ranking */}
        <button onClick={() => nav.go('ranking')} style={{
          marginTop: 12, width: '100%', padding: '16px 18px',
          border: T.border, borderRadius: 20, cursor: 'pointer',
          background: T.card,
          color: T.text, fontFamily: 'Poppins',
          display: 'flex', alignItems: 'center', gap: 12, textAlign: 'left',
        }}>
          <span style={{ fontSize: 24 }}>🏆</span>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 13, fontWeight: 700, letterSpacing: -0.2 }}>Ver Ranking · Top 100</div>
            <div style={{ fontSize: 11, color: T.muted, marginTop: 1 }}>Mira el progreso de la comunidad 💗</div>
          </div>
          {Icon.chevR(T.muted, 9)}
        </button>

        {/* Cómo ganar puntos — explicación */}
        <div style={{
          marginTop: 14, padding: '16px 18px',
          background: T.card, borderRadius: 20, border: T.border,
        }}>
          <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 10 }}>Cómo ganar puntos</div>
          {[
            { e: '✅', t: 'Completar un día del reto', v: '+50' },
            { e: '🏆', t: 'Completar un reto entero', v: '+250' },
            { e: '💪🏻', t: 'Completar una rutina', v: '+30' },
            { e: '🍳', t: 'Marcar una receta como hecha', v: '+5' },
            { e: '📸', t: 'Subir foto de progreso', v: '+10' },
            { e: '📏', t: 'Registrar medidas', v: '+15' },
            { e: '🍓', t: 'Anotar una comida', v: '+5' },
            { e: '💬', t: 'Dejar un testimonio (al completar)', v: '+20' },
            { e: '👯‍♀️', t: 'Invitar a una amiga que se une', v: '+100' },
          ].map((row, i, arr) => (
            <div key={i} style={{
              display: 'flex', alignItems: 'center', gap: 12,
              padding: '10px 0',
              borderBottom: i < arr.length - 1 ? `0.5px solid ${T.subtle}` : 'none',
            }}>
              <span style={{ fontSize: 18, width: 24, textAlign: 'center' }}>{row.e}</span>
              <span style={{ flex: 1, fontSize: 12, fontWeight: 500, color: T.text }}>{row.t}</span>
              <span style={{ fontSize: 12, fontWeight: 800, color: T.accent }}>{row.v} ⭐</span>
            </div>
          ))}
        </div>
      </div>

      {/* Gráfico semanal — últimas 8 semanas */}
      <WeeklyStatsChart T={T}/>

      {/* Header insignias */}
      <div style={{ padding: '24px 20px 0' }}>
        <div className="pf-eyebrow" style={{ color: T.muted }}>Insignias</div>
        <div className="pf-display" style={{ fontSize: 20, fontWeight: 700, color: T.text, letterSpacing: -0.3, marginTop: 2 }}>
          Tus insignias
        </div>
      </div>

      <div style={{ padding: '14px 20px 0', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        {LOGROS_DATA.map(l => (
          <div key={l.id} style={{
            padding: 14, borderRadius: 20,
            background: T.card,
            border: l.unlocked
              ? `1.5px solid ${T.accent}55`
              : T.border,
            boxShadow: 'none',
            display: 'flex', flexDirection: 'column', gap: 6,
            opacity: l.unlocked ? 1 : 0.55,
            position: 'relative', overflow: 'hidden',
            transition: 'all 240ms ease',
          }}>
            <div style={{
              fontSize: 36, filter: l.unlocked ? 'none' : 'grayscale(1)',
              position: 'relative',
            }}>{l.emoji}</div>
            <div style={{ fontFamily: 'Poppins', fontSize: 13, fontWeight: 700, color: T.text, letterSpacing: -0.2, lineHeight: 1.2, position: 'relative' }}>{l.title}</div>
            <div style={{ fontFamily: 'Poppins', fontSize: 11, color: T.muted, lineHeight: 1.3, position: 'relative' }}>{l.desc}</div>
            {!l.unlocked && (
              <div style={{ marginTop: 4 }}>
                <div style={{ height: 4, background: T.subtle, borderRadius: 999, overflow: 'hidden' }}>
                  <div style={{ width: `${l.progress*100}%`, height: '100%', background: T.accent }}/>
                </div>
              </div>
            )}
            {l.unlocked && (
              <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 6, position: 'relative' }}>
                <div style={{
                  padding: '3px 9px',
                  background: T.accent, color: '#fff', borderRadius: 999,
                  fontFamily: 'Poppins', fontSize: 9, fontWeight: 800,
                  letterSpacing: 0.6, 
                }}>✓ Alcanzado</div>
                <button onClick={(e) => {
                  e.stopPropagation();
                  const userName = cache.profile?.full_name || cache.profile?.username || '';
                  const text = userName
                    ? `🏆 ${userName} acaba de desbloquear "${l.title}" en Pau Fit 💗\n\n${l.desc}\n\n¿Te apuntas? ¡Cada acción suma!`
                    : `🏆 ¡Acabo de desbloquear "${l.title}" en Pau Fit 💗\n\n${l.desc}\n\n¿Te apuntas? ¡Cada acción suma!`;
                  if (window.PauShare) {
                    window.PauShare({
                      title: `${l.emoji} ${l.title} · Pau Fit`,
                      text,
                      url: 'https://app.paufit.co',
                    }).then(r => {
                      if (r.shared) nav.toast(r.method === 'native' ? '¡Compartido!' : 'Enlace copiado 💗', { icon: '↗' });
                    });
                  }
                }} style={{
                  padding: '3px 8px', borderRadius: 999, border: 'none', cursor: 'pointer',
                  background: T.card, color: T.muted,
                  fontFamily: 'Poppins', fontSize: 9, fontWeight: 700,
                  display: 'flex', alignItems: 'center', gap: 3,
                }}>↗ Compartir</button>
              </div>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// WeeklyStatsChart — barras SVG de últimas 8 semanas
// Lee day_completions reales y agrupa por semana ISO
// ─────────────────────────────────────────────────────────────
function WeeklyStatsChart({ T }) {
  const [weeks, setWeeks] = useS3(null);
  const [loading, setLoading] = useS3(true);

  useE3(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setLoading(false); return; }
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) { setLoading(false); return; }
        // Últimas 8 semanas
        const start = new Date();
        start.setDate(start.getDate() - 56);
        start.setHours(0, 0, 0, 0);
        const { data } = await sb
          .from('day_completions')
          .select('completed_at')
          .eq('user_id', user.id)
          .gte('completed_at', start.toISOString());
        // Agrupar por inicio de semana (lunes) en local timezone
        const buckets = {};
        for (let i = 7; i >= 0; i--) {
          const d = new Date();
          d.setDate(d.getDate() - i * 7);
          // ir al lunes de esa semana
          const dow = (d.getDay() + 6) % 7; // Lun=0
          d.setDate(d.getDate() - dow);
          d.setHours(0, 0, 0, 0);
          const key = d.toLocaleDateString('en-CA');
          buckets[key] = { date: new Date(d), count: 0 };
        }
        (data || []).forEach(r => {
          const d = new Date(r.completed_at);
          const dow = (d.getDay() + 6) % 7;
          d.setDate(d.getDate() - dow);
          d.setHours(0, 0, 0, 0);
          const key = d.toLocaleDateString('en-CA');
          if (buckets[key]) buckets[key].count += 1;
        });
        const sorted = Object.values(buckets).sort((a, b) => a.date - b.date);
        setWeeks(sorted);
      } catch (e) {
        console.warn('[weekly-chart]', e?.message);
      }
      setLoading(false);
    })();
  }, []);

  if (loading) return (
    <div style={{ padding: '24px 20px 0' }}>
      <div className="pf-eyebrow" style={{ color: T.muted }}>Stats semanales</div>
      <div style={{
        marginTop: 8, padding: 24, background: T.card, borderRadius: 20, border: T.border,
        textAlign: 'center', color: T.muted, fontSize: 12,
      }}>Cargando…</div>
    </div>
  );

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

  const maxCount = Math.max(1, ...weeks.map(w => w.count));
  const totalThisMonth = weeks.slice(-4).reduce((a, w) => a + w.count, 0);
  const totalLastMonth = weeks.slice(0, 4).reduce((a, w) => a + w.count, 0);
  const diff = totalThisMonth - totalLastMonth;
  const diffPct = totalLastMonth > 0 ? Math.round((diff / totalLastMonth) * 100) : null;

  // SVG dimensions
  const W = 320, H = 120;
  const padL = 14, padR = 14, padT = 16, padB = 26;
  const innerW = W - padL - padR;
  const innerH = H - padT - padB;
  const barW = innerW / weeks.length * 0.65;
  const gap = innerW / weeks.length * 0.35;

  return (
    <div style={{ padding: '24px 20px 0' }}>
      <div style={{
        display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
        padding: '0 2px 8px',
      }}>
        <div className="pf-eyebrow" style={{ color: T.muted }}>Stats semanales</div>
        {diffPct !== null && (
          <span style={{
            fontSize: 11, fontWeight: 700,
            color: diff >= 0 ? '#5C8A56' : '#C75E5E',
          }}>
            {diff >= 0 ? '↑' : '↓'} {Math.abs(diffPct)}% vs hace 1 mes
          </span>
        )}
      </div>
      <div style={{
        padding: '14px 12px 6px',
        background: T.card, borderRadius: 20, border: T.border,
      }}>
        <div style={{
          display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
          padding: '0 6px 10px',
        }}>
          <div>
            <div style={{ fontSize: 24, fontWeight: 800, color: T.text, letterSpacing: -0.5, lineHeight: 1 }}>
              {totalThisMonth}
            </div>
            <div style={{ fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.4,  marginTop: 3 }}>
              Días últimas 4 sem
            </div>
          </div>
          <div style={{ textAlign: 'right' }}>
            <div style={{ fontSize: 13, fontWeight: 700, color: T.text }}>
              Media {(totalThisMonth / 4).toFixed(1)} / sem
            </div>
            <div style={{ fontSize: 10, color: T.muted, marginTop: 2 }}>
              {weeks.length} semanas
            </div>
          </div>
        </div>
        <svg viewBox={`0 0 ${W} ${H}`} width="100%" preserveAspectRatio="none" style={{ display: 'block' }}>
          {/* Líneas guía horizontales — referencia */}
          {[0.25, 0.5, 0.75].map(p => {
            const y = padT + innerH * (1 - p);
            return <line key={p} x1={padL} y1={y} x2={W - padR} y2={y}
              stroke={T.muted} strokeWidth="0.5" strokeDasharray="2 3" opacity="0.3"/>;
          })}
          {/* Línea horizontal del eje */}
          <line x1={padL} y1={padT + innerH} x2={W - padR} y2={padT + innerH}
            stroke={T.muted} strokeWidth="1" opacity="0.4"/>
          {weeks.map((w, i) => {
            const x = padL + (i + 0.175) * (innerW / weeks.length);
            const h = (w.count / maxCount) * innerH;
            const y = padT + innerH - h;
            const isLast = i === weeks.length - 1;
            const label = String(w.date.getDate()) + '/' + String(w.date.getMonth() + 1);
            return (
              <g key={i}>
                <rect x={x} y={y} width={barW} height={Math.max(2, h)}
                  rx="3" ry="3"
                  fill={isLast ? T.accent : T.muted}
                  opacity={isLast ? 1 : 0.45}/>
                <text x={x + barW / 2} y={padT + innerH + 12}
                  fontSize="9" fontFamily="Poppins" fontWeight="600"
                  fill={T.muted} textAnchor="middle">{label}</text>
                {w.count > 0 && (
                  <text x={x + barW / 2} y={y - 4}
                    fontSize="9" fontFamily="Poppins" fontWeight="700"
                    fill={isLast ? T.accent : T.text} textAnchor="middle">{w.count}</text>
                )}
              </g>
            );
          })}
        </svg>
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// PhotoEditModal — editar ángulo, privacidad, fecha o borrar foto
// ═════════════════════════════════════════════════════════════
function PhotoEditModal({ T, photo, onClose, onSaved }) {
  const [angle, setAngle] = useS3(photo.angle || 'frente');
  const [isPublic, setIsPublic] = useS3(!!photo.shared);
  const [saving, setSaving] = useS3(false);

  async function applyChanges() {
    setSaving(true);
    const sb = window.__PAU_SUPABASE;
    try {
      const { data: { user } } = await sb.auth.getUser();
      if (!user) throw new Error('Inicia sesión');
      // Renombrar el archivo en Storage con el nuevo angle + privacidad
      // formato: {angle}-{priv|pub}-{timestamp}.{ext}
      const oldPath = photo.path;
      const ts = (photo.name.match(/-(\d+)\./)?.[1]) || Date.now();
      const ext = (photo.name.split('.').pop() || 'jpg').toLowerCase();
      const newName = `${angle}-${isPublic ? 'pub' : 'priv'}-${ts}.${ext}`;
      const newPath = `${user.id}/${newName}`;
      if (newPath !== oldPath) {
        const { error: moveErr } = await sb.storage.from('progress-photos').move(oldPath, newPath);
        if (moveErr) throw moveErr;
      }
      if (window.__pauNav?.toast) window.__pauNav.toast('Cambios guardados 💗', { icon: '✓' });
      onSaved && onSaved();
    } catch (e) {
      if (window.__pauNav?.toast) window.__pauNav.toast('Error: ' + (e?.message || e), { icon: '⚠️' });
    }
    setSaving(false);
  }

  async function deletePhoto() {
    if (!window.confirm('¿Borrar esta foto? Esta acción no se puede deshacer.')) return;
    setSaving(true);
    const sb = window.__PAU_SUPABASE;
    try {
      const { error } = await sb.storage.from('progress-photos').remove([photo.path]);
      if (error) throw error;
      if (window.__pauNav?.toast) window.__pauNav.toast('Foto borrada', { icon: '🗑️' });
      onSaved && onSaved();
    } catch (e) {
      if (window.__pauNav?.toast) window.__pauNav.toast('Error: ' + (e?.message || e), { icon: '⚠️' });
    }
    setSaving(false);
  }

  return (
    <div onClick={saving ? undefined : 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 */}
        <div style={{
          aspectRatio: '3 / 4', maxHeight: 220, borderRadius: 18, overflow: 'hidden',
          background: '#0E0A0C', margin: '0 auto', maxWidth: 200,
        }}>
          <img src={photo.url} alt={photo.date} style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
        </div>

        <div style={{ textAlign: 'center', marginTop: 14 }}>
          <div className="pf-eyebrow" style={{ color: T.muted }}>{photo.date}</div>
          <div className="pf-display" style={{ fontSize: 18, fontWeight: 700, color: T.text, marginTop: 2 }}>
            Editar foto
          </div>
        </div>

        {/* Ángulo */}
        <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 22, marginBottom: 10 }}>Ángulo</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8 }}>
          {[
            { v: 'frente', e: '🚶‍♀️', t: 'Frente' },
            { v: 'lateral', e: '↔️', t: 'Lateral' },
            { v: 'atras', e: '🔙', t: 'Atrás' },
          ].map(o => (
            <button key={o.v} onClick={() => setAngle(o.v)} style={{
              padding: '12px 8px', borderRadius: 14, cursor: 'pointer',
              border: angle === o.v ? `1.5px solid ${T.accent}` : T.border,
              background: angle === o.v ? `${T.accent}15` : T.subtle,
              color: T.text, fontFamily: 'inherit',
              display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
            }}>
              <span style={{ fontSize: 22 }}>{o.e}</span>
              <span style={{ fontSize: 11, fontWeight: 700 }}>{o.t}</span>
            </button>
          ))}
        </div>

        {/* Privacidad */}
        <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 22, marginBottom: 10 }}>Privacidad</div>
        <div style={{ display: 'flex', gap: 8 }}>
          <button onClick={() => setIsPublic(false)} style={{
            flex: 1, padding: '12px', borderRadius: 14, cursor: 'pointer',
            border: !isPublic ? `1.5px solid ${T.accent}` : T.border,
            background: !isPublic ? `${T.accent}15` : T.subtle,
            color: T.text, fontFamily: 'inherit',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
          }}>
            <span style={{ fontSize: 16 }}>🔒</span>
            <span style={{ fontSize: 12, fontWeight: 700 }}>Privada</span>
          </button>
          <button onClick={() => setIsPublic(true)} style={{
            flex: 1, padding: '12px', borderRadius: 14, cursor: 'pointer',
            border: isPublic ? `1.5px solid ${T.accent}` : T.border,
            background: isPublic ? `${T.accent}15` : T.subtle,
            color: T.text, fontFamily: 'inherit',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
          }}>
            <span style={{ fontSize: 16 }}>👥</span>
            <span style={{ fontSize: 12, fontWeight: 700 }}>Pública</span>
          </button>
        </div>

        {/* Botones */}
        <button onClick={applyChanges} disabled={saving} style={{
          marginTop: 24, width: '100%', padding: '14px',
          border: 'none', borderRadius: 14, cursor: saving ? 'wait' : 'pointer',
          background: T.accent, color: '#fff',
          fontFamily: 'Poppins', fontSize: 14, fontWeight: 700,
          boxShadow: `0 6px 18px ${T.accent}40`,
          opacity: saving ? 0.7 : 1,
        }}>{saving ? 'Guardando…' : 'Guardar cambios'}</button>
        <button onClick={deletePhoto} disabled={saving} style={{
          marginTop: 8, width: '100%', padding: '12px',
          border: 'none', borderRadius: 14, cursor: 'pointer',
          background: 'transparent', color: '#A53939',
          fontFamily: 'Poppins', fontSize: 13, fontWeight: 600,
        }}>🗑️ Borrar foto</button>
        <button onClick={onClose} disabled={saving} style={{
          marginTop: 4, width: '100%', padding: '10px',
          border: 'none', borderRadius: 14, cursor: 'pointer',
          background: 'transparent', color: T.muted,
          fontFamily: 'Poppins', fontSize: 13, fontWeight: 600,
        }}>Cancelar</button>
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// PROGRESO — fotos + medidas
// ═════════════════════════════════════════════════════════════

// ─────────────────────────────────────────────────────────────
// WeightChart — evolución del peso (línea, un solo color de marca)
// Specs dataviz: línea 2px, grid recesivo, tooltip al tocar,
// etiqueta directa solo en el último punto, texto en tinta (no color).
// ─────────────────────────────────────────────────────────────
function WeightChart({ T, rows }) {
  const [hoverIdx, setHoverIdx] = useS3(null);
  const data = React.useMemo(() => {
    return (rows || [])
      .filter(r => r.weight != null && r.taken_at)
      .slice()
      .sort((a, b) => new Date(a.taken_at) - new Date(b.taken_at))
      .map(r => ({ d: new Date(r.taken_at), v: parseFloat(r.weight) }));
  }, [rows]);

  if (data.length < 2) return null; // con 1 punto no hay evolución

  const W = 320, H = 150, padL = 34, padR = 14, padT = 14, padB = 24;
  const vs = data.map(p => p.v);
  let vMin = Math.min(...vs), vMax = Math.max(...vs);
  const span = Math.max(vMax - vMin, 1);
  vMin -= span * 0.15; vMax += span * 0.15;
  const x = (i) => padL + (i / (data.length - 1)) * (W - padL - padR);
  const y = (v) => padT + (1 - (v - vMin) / (vMax - vMin)) * (H - padT - padB);
  const path = data.map((p, i) => `${i === 0 ? 'M' : 'L'}${x(i).toFixed(1)},${y(p.v).toFixed(1)}`).join(' ');
  const last = data[data.length - 1];
  const first = data[0];
  const delta = last.v - first.v;
  const gridColor = T.dark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.06)';
  const gridVals = [vMin + (vMax - vMin) * 0.2, vMin + (vMax - vMin) * 0.55, vMin + (vMax - vMin) * 0.9];
  const fmtD = (d) => d.toLocaleDateString('es', { day: 'numeric', month: 'short' }).replace('.', '');

  function onMove(e) {
    const rect = e.currentTarget.getBoundingClientRect();
    const clientX = (e.touches ? e.touches[0].clientX : e.clientX);
    const px = ((clientX - rect.left) / rect.width) * W;
    let best = 0, bd = Infinity;
    data.forEach((_, i) => { const d2 = Math.abs(x(i) - px); if (d2 < bd) { bd = d2; best = i; } });
    setHoverIdx(best);
  }

  const hi = hoverIdx != null ? data[hoverIdx] : null;

  return (
    <div style={{ margin: '14px 20px 0', padding: '16px 14px 10px', background: T.card, border: T.border, borderRadius: 20, position: 'relative' }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', padding: '0 4px' }}>
        <span style={{ fontSize: 11, color: T.muted, fontWeight: 600, letterSpacing: 0.5,  }}>Tu peso</span>
        <span style={{ fontSize: 11, color: T.muted, fontWeight: 600 }}>
          {delta === 0 ? 'igual que al inicio' : `${delta > 0 ? '▲' : '▼'} ${Math.abs(delta).toFixed(1)} kg desde el inicio`}
        </span>
      </div>
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: 'auto', display: 'block', marginTop: 6, touchAction: 'pan-y' }}
        onMouseMove={onMove} onMouseLeave={() => setHoverIdx(null)}
        onTouchStart={onMove} onTouchMove={onMove} onTouchEnd={() => setHoverIdx(null)}>
        {gridVals.map((gv, i) => (
          <g key={i}>
            <line x1={padL} x2={W - padR} y1={y(gv)} y2={y(gv)} stroke={gridColor} strokeWidth="1"/>
            <text x={padL - 6} y={y(gv) + 3} textAnchor="end" fontSize="9" fill={T.muted} fontWeight="600">{gv.toFixed(0)}</text>
          </g>
        ))}
        <text x={x(0)} y={H - 6} textAnchor="start" fontSize="9" fill={T.muted} fontWeight="600">{fmtD(first.d)}</text>
        <text x={x(data.length - 1)} y={H - 6} textAnchor="end" fontSize="9" fill={T.muted} fontWeight="600">{fmtD(last.d)}</text>
        <path d={path} fill="none" stroke={T.accent} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
        {hi != null && (
          <line x1={x(hoverIdx)} x2={x(hoverIdx)} y1={padT} y2={H - padB} stroke={gridColor} strokeWidth="1"/>
        )}
        {hi != null && (
          <circle cx={x(hoverIdx)} cy={y(hi.v)} r="4.5" fill={T.accent} stroke={T.card} strokeWidth="2"/>
        )}
        {hoverIdx == null && (
          <>
            <circle cx={x(data.length - 1)} cy={y(last.v)} r="4" fill={T.accent} stroke={T.card} strokeWidth="2"/>
            <text x={Math.min(x(data.length - 1), W - padR - 4)} y={y(last.v) - 9} textAnchor="end" fontSize="11" fontWeight="700" fill={T.text}>
              {last.v.toFixed(1)} kg
            </text>
          </>
        )}
      </svg>
      {hi != null && (
        <div style={{
          position: 'absolute', top: 8, left: '50%', transform: 'translateX(-50%)',
          padding: '5px 12px', borderRadius: 999, background: T.text, color: T.card,
          fontSize: 11, fontWeight: 700, whiteSpace: 'nowrap', pointerEvents: 'none',
        }}>
          {fmtD(hi.d)} · {hi.v.toFixed(1)} kg
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// AntesDespuesCard — primera vs última foto de progreso
// ─────────────────────────────────────────────────────────────
function AntesDespuesCard({ T, photos }) {
  if (!photos || photos.length < 2) return null;
  const primera = photos[photos.length - 1];
  const ultima = photos[0];
  const Col = ({ p, label }) => (
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ borderRadius: 14, overflow: 'hidden', aspectRatio: '3 / 4', background: T.subtle }}>
        <img src={p.url} alt={label} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}/>
      </div>
      <div style={{ marginTop: 6, textAlign: 'center' }}>
        <div style={{ fontSize: 10, fontWeight: 700, color: T.muted, letterSpacing: 0.5,  }}>{label}</div>
        <div style={{ fontSize: 10.5, color: T.muted, marginTop: 1 }}>{p.date}</div>
      </div>
    </div>
  );
  return (
    <div style={{ margin: '14px 20px 0', padding: '14px', background: T.card, border: T.border, borderRadius: 20 }}>
      <div style={{ fontSize: 11, color: T.muted, fontWeight: 600, letterSpacing: 0.5,  marginBottom: 10 }}>
        Antes y ahora
      </div>
      <div style={{ display: 'flex', gap: 10 }}>
        <Col p={primera} label="Antes"/>
        <Col p={ultima} label="Ahora"/>
      </div>
    </div>
  );
}

function ProgresoScreen({ theme, nav }) {
  const T = theme;
  const [tab, setTab] = useS3('fotos');
  // MedidasTab ahora lee de Supabase real — estos fallback son por compat.
  const latest = MEDIDAS[0] || {};
  const prev = MEDIDAS[MEDIDAS.length - 1] || {};

  return (
    <div style={{ paddingBottom: 110 }}>
      <div style={{ padding: '12px 20px 4px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between' }}>
          <BackBtn onClick={() => nav.back()}/>
        </div>
        <div style={{ marginTop: 14 }}>
          <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95 }}>
            Progreso
          </div>
        </div>
      </div>

      {/* "Vas bien" — señales reales basadas en su data, sin métricas negativas
          Solo muestra lo que ESTÁ pasando bien para reforzar constancia. */}
      <VasBienCard T={T}/>

      {/* Segmented */}
      <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: tab === 'fotos' ? 4 : 'calc(50% + 0px)',
          width: 'calc(50% - 4px)', background: T.card, borderRadius: 9,
          boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
          transition: 'left 240ms cubic-bezier(.2,.8,.2,1)',
        }}/>
        {[
          { id: 'fotos', label: 'Fotos' },
          { id: 'medidas', label: 'Medidas' },
        ].map(s => (
          <button key={s.id} onClick={() => setTab(s.id)} style={{
            flex: 1, padding: '8px', border: 'none', background: 'transparent', cursor: 'pointer',
            position: 'relative', zIndex: 1,
            fontFamily: 'Poppins', fontSize: 12, fontWeight: 600,
            color: tab === s.id ? T.text : T.muted, letterSpacing: -0.1,
          }}>{s.label}</button>
        ))}
      </div>

      {tab === 'fotos' ? <FotosTab T={T}/> : <MedidasTab T={T} latest={latest} prev={prev}/>}
    </div>
  );
}

function FotosTab({ T }) {
  // Fotos REALES desde Supabase Storage (no placeholders invisibles)
  const [photos, setPhotos] = useS3(null);
  const [uploading, setUploading] = useS3(false);
  const fileInputRef = React.useRef(null);

  async function loadPhotos() {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setPhotos([]); return; }
    const { data: { user } } = await sb.auth.getUser();
    if (!user) { setPhotos([]); return; }
    try {
      const { data, error } = await sb.storage
        .from('progress-photos')
        .list(user.id, { limit: 100, sortBy: { column: 'created_at', order: 'desc' } });
      if (error) throw error;
      const filtered = (data || []).filter(f => f.name && !f.name.startsWith('.'));
      // Para bucket privado, obtener signed URLs (válidas 1h)
      const items = await Promise.all(filtered.map(async (f) => {
        const path = `${user.id}/${f.name}`;
        // Intentar signedUrl primero (funciona bucket privado o público)
        let url = null;
        try {
          const { data: signed } = await sb.storage.from('progress-photos').createSignedUrl(path, 3600);
          if (signed?.signedUrl) url = signed.signedUrl;
        } catch {}
        // Fallback a publicUrl si signedUrl falló
        if (!url) {
          const { data: urlData } = sb.storage.from('progress-photos').getPublicUrl(path);
          url = urlData?.publicUrl || null;
        }
        // Parsear metadata del filename: {angle}-{priv|pub}-{timestamp}.{ext}
        let angle = null, isPublic = false;
        const m = f.name.match(/^(frente|lateral|atras)-(priv|pub)-/);
        if (m) { angle = m[1]; isPublic = m[2] === 'pub'; }
        return {
          id: f.id || f.name,
          name: f.name,
          path,
          url,
          date: new Date(f.created_at || f.updated_at || Date.now()).toLocaleDateString('es', { day: 'numeric', month: 'short', year: 'numeric' }),
          angle,
          shared: isPublic,
        };
      }));
      setPhotos(items);
    } catch (e) {
      console.warn('[progress-photos] error:', e.message || e);
      setPhotos([]);
    }
  }

  React.useEffect(() => { loadPhotos(); }, []);

  function openNewPhotoFlow() {
    // Abre wizard multi-foto (frente / lado / atrás) saltables
    if (window.__pauNav?.openModal) {
      window.__pauNav.openModal(
        <MultiPhotoWizard
          T={T}
          onClose={() => window.__pauNav.closeModal()}
          onSaved={async () => {
            window.__pauNav.closeModal();
            window.__pauNav.toast('Fotos guardadas 💗', { icon: '📸' });
            await loadPhotos();
          }}
        />
      );
    }
  }
  // Compat: se queda por si hay otro callsite
  async function handleFile(e) {
    e.target.value = '';
    openNewPhotoFlow();
  }

  return (
    <div>
      {/* Antes y ahora — fase 3 del roadmap */}
      <AntesDespuesCard T={T} photos={photos}/>
      <div style={{ padding: '18px 20px 4px', display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
        <span style={{ fontFamily: 'Poppins', fontSize: 11, color: T.muted, fontWeight: 600, letterSpacing: 0.5,  }}>Tu timeline</span>
        <button
          onClick={openNewPhotoFlow}
          disabled={uploading}
          style={{
            padding: '6px 12px', border: 'none', borderRadius: 999, cursor: uploading ? 'wait' : 'pointer',
            background: T.accent, color: '#fff',
            fontFamily: 'Poppins', fontSize: 11, fontWeight: 600,
            opacity: uploading ? 0.7 : 1,
          }}>
          ＋ Nuevas fotos
        </button>
      </div>

      {/* Loading — Skeleton grid 2x2 */}
      {photos === null && (
        <div style={{ padding: '12px 20px 0', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          {[0,1,2,3].map(i => (
            <div key={i} className="pf-skeleton" style={{ aspectRatio: '3 / 4', borderRadius: 20 }}/>
          ))}
        </div>
      )}

      {/* Vacío con CTA */}
      {photos && photos.length === 0 && (
        <EmptyState
          emoji="📸" T={T}
          title="Tu primer antes y después"
          description="Privadas por defecto."
          ctaLabel="Empezar"
          onCta={openNewPhotoFlow}
        />
      )}

      {/* Grid de fotos REALES — click → modal editar/borrar */}
      {photos && photos.length > 0 && (
        <div style={{ padding: '12px 20px 0', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          {photos.map(p => (
            <button key={p.id} onClick={() => {
              if (window.__pauNav?.openModal) {
                window.__pauNav.openModal(
                  <PhotoEditModal
                    T={T} photo={p}
                    onClose={() => window.__pauNav.closeModal()}
                    onSaved={async () => { window.__pauNav.closeModal(); await loadPhotos(); }}
                  />
                );
              }
            }} style={{
              aspectRatio: '3 / 4', borderRadius: 20, position: 'relative', overflow: 'hidden',
              background: '#0E0A0C', border: 'none', padding: 0, cursor: 'pointer',
            }}>
              {p.url && (
                <img
                  src={p.url}
                  alt={p.date}
                  style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}
                  onError={(e) => { e.target.style.display = 'none'; }}
                />
              )}
              <div style={{
                position: 'absolute', inset: 0,
                background: 'linear-gradient(180deg, rgba(0,0,0,0.3) 0%, transparent 35%, transparent 60%, rgba(0,0,0,0.7) 100%)',
                pointerEvents: 'none',
              }}/>
              <div style={{
                position: 'absolute', top: 8, right: 8,
                padding: '3px 8px', borderRadius: 999, backdropFilter: 'blur(8px)',
                background: p.shared ? 'rgba(217,71,126,0.85)' : 'rgba(45,42,38,0.7)',
                fontFamily: 'Poppins', fontSize: 9, color: '#fff', fontWeight: 600,
                display: 'flex', alignItems: 'center', gap: 3,
              }}>{p.shared ? '👥 Pública' : '🔒 Privada'}</div>
              {p.angle && (
                <div style={{
                  position: 'absolute', top: 8, left: 8,
                  padding: '3px 8px', borderRadius: 999, backdropFilter: 'blur(8px)',
                  background: 'rgba(45,42,38,0.7)',
                  fontFamily: 'Poppins', fontSize: 9, color: '#fff', fontWeight: 600,
                  letterSpacing: 0.3, textTransform: 'capitalize',
                }}>{p.angle === 'frente' ? '🚶‍♀️ Frente' : p.angle === 'lateral' ? '↔️ Lateral' : '🔙 Atrás'}</div>
              )}
              <div style={{
                position: 'absolute', bottom: 8, left: 8, right: 8,
                fontFamily: 'Poppins', fontSize: 11, fontWeight: 600, color: '#fff',
                textShadow: '0 1px 4px rgba(0,0,0,0.5)',
                textAlign: 'left',
              }}>{p.date}</div>
            </button>
          ))}
        </div>
      )}

      {/* Compare CTA — funcional: abre slider antes/después */}
      {photos && photos.length >= 2 && (
        <div style={{
          margin: '20px 18px 0', padding: '16px',
          background: T.card, borderRadius: 20, border: T.border,
          display: 'flex', alignItems: 'center', gap: 12,
        }}>
          <span style={{ fontSize: 28 }}>⚖️</span>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: 'Poppins', fontSize: 13, fontWeight: 600, color: T.text }}>Compara antes y después</div>
            <div style={{ fontFamily: 'Poppins', fontSize: 11, color: T.muted, marginTop: 1 }}>
              {photos.length} fotos · Desliza para ver tu cambio
            </div>
          </div>
          <button onClick={() => {
            // Auto-pick: oldest (last in array) = before, newest (first) = after
            const before = photos[photos.length - 1];
            const after = photos[0];
            if (window.__pauNav?.openModal) {
              window.__pauNav.openModal(
                <BeforeAfterSlider
                  T={T}
                  beforeUrl={before.url} beforeDate={before.date}
                  afterUrl={after.url} afterDate={after.date}
                  onClose={() => window.__pauNav.closeModal()}
                />
              );
            }
          }} style={{
            padding: '8px 14px', border: 'none', borderRadius: 999, cursor: 'pointer',
            background: T.accent, color: '#fff',
            fontFamily: 'Poppins', fontSize: 12, fontWeight: 600,
          }}>Comparar</button>
        </div>
      )}

      {/* Disclaimer privacidad — solo si hay fotos públicas (informar contexto) */}
      {photos && photos.some(p => p.shared) && (
        <div style={{
          margin: '14px 20px 0', padding: '10px 14px',
          background: 'rgba(168,197,160,0.12)', borderRadius: 14,
          display: 'flex', alignItems: 'center', gap: 8,
        }}>
          <span style={{ fontSize: 14 }}>🔒</span>
          <span style={{ fontFamily: 'Poppins', fontSize: 11, color: T.muted }}>
            Solo las marcadas como públicas se ven en la comunidad.
          </span>
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// BeforeAfterSlider — modal con slider drag entre 2 fotos
// La imagen "after" está encima; el slider revela la "before" debajo
// ─────────────────────────────────────────────────────────────
function BeforeAfterSlider({ T, beforeUrl, beforeDate, afterUrl, afterDate, onClose }) {
  const [pos, setPos] = useS3(50); // 0-100
  const containerRef = React.useRef(null);

  function startDrag(e) {
    e.preventDefault();
    function move(ev) {
      const rect = containerRef.current?.getBoundingClientRect();
      if (!rect) return;
      const x = (ev.touches ? ev.touches[0].clientX : ev.clientX) - rect.left;
      const pct = Math.max(0, Math.min(100, (x / rect.width) * 100));
      setPos(pct);
    }
    function up() {
      window.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', up);
      window.removeEventListener('touchmove', move);
      window.removeEventListener('touchend', up);
    }
    window.addEventListener('mousemove', move);
    window.addEventListener('mouseup', up);
    window.addEventListener('touchmove', move);
    window.addEventListener('touchend', up);
  }

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 9999,
      background: 'rgba(0,0,0,0.85)',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      padding: 20, gap: 16,
    }}>
      <button onClick={onClose} style={{
        position: 'absolute', top: 18, right: 18,
        width: 36, height: 36, borderRadius: 999, border: 'none', cursor: 'pointer',
        background: 'rgba(255,255,255,0.15)', color: '#fff',
        fontSize: 18, fontWeight: 600,
      }}>×</button>

      <div style={{ textAlign: 'center', color: '#fff' }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: 1.2, opacity: 0.7,  }}>
          Antes vs después
        </div>
        <div style={{ fontFamily: 'Poppins', fontSize: 18, fontWeight: 600, marginTop: 4 }}>
          Tu transformación 💗
        </div>
      </div>

      <div
        ref={containerRef}
        onMouseDown={startDrag}
        onTouchStart={startDrag}
        style={{
          position: 'relative', width: '100%', maxWidth: 360,
          aspectRatio: '3 / 4', borderRadius: 20, overflow: 'hidden',
          background: '#000', cursor: 'ew-resize', userSelect: 'none',
          touchAction: 'none',
        }}
      >
        {/* BEFORE — debajo, ocupa todo */}
        {beforeUrl && (
          <img src={beforeUrl} alt="antes" draggable={false} style={{
            position: 'absolute', inset: 0, width: '100%', height: '100%',
            objectFit: 'cover', userSelect: 'none', pointerEvents: 'none',
          }}/>
        )}
        {/* AFTER — encima, recortada por clipPath según pos */}
        {afterUrl && (
          <img src={afterUrl} alt="después" draggable={false} style={{
            position: 'absolute', inset: 0, width: '100%', height: '100%',
            objectFit: 'cover', userSelect: 'none', pointerEvents: 'none',
            clipPath: `inset(0 0 0 ${pos}%)`,
            WebkitClipPath: `inset(0 0 0 ${pos}%)`,
          }}/>
        )}
        {/* Divider vertical */}
        <div style={{
          position: 'absolute', top: 0, bottom: 0, left: `${pos}%`,
          width: 3, background: '#fff', transform: 'translateX(-50%)',
          boxShadow: '0 0 8px rgba(0,0,0,0.4)',
        }}>
          <div style={{
            position: 'absolute', top: '50%', left: '50%',
            transform: 'translate(-50%, -50%)',
            width: 36, height: 36, borderRadius: 999,
            background: '#fff', boxShadow: '0 2px 12px rgba(0,0,0,0.3)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 14, fontWeight: 700, color: '#0E0A0C',
          }}>⇆</div>
        </div>
        {/* Etiquetas */}
        <div style={{
          position: 'absolute', top: 12, left: 12,
          padding: '4px 10px', borderRadius: 999,
          background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(8px)',
          color: '#fff', fontSize: 10, fontWeight: 700, letterSpacing: 0.5,
        }}>ANTES · {beforeDate}</div>
        <div style={{
          position: 'absolute', top: 12, right: 12,
          padding: '4px 10px', borderRadius: 999,
          background: 'rgba(217,71,126,0.85)', backdropFilter: 'blur(8px)',
          color: '#fff', fontSize: 10, fontWeight: 700, letterSpacing: 0.5,
        }}>DESPUÉS · {afterDate}</div>
      </div>

      <input
        type="range"
        min={0} max={100} value={pos}
        onChange={(e) => setPos(Number(e.target.value))}
        style={{
          width: '100%', maxWidth: 360,
          accentColor: T.accent,
        }}
      />

      <div style={{
        fontSize: 11, color: 'rgba(255,255,255,0.65)', textAlign: 'center',
      }}>Arrastra el círculo para comparar</div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// PhotoUploadWizard — modal multi-paso para subir foto progreso
// Steps: 1. Preview + ángulo, 2. Privacidad, 3. Medidas (opcional)
// Cualquier paso es saltable. Solo la foto es obligatoria.
// ─────────────────────────────────────────────────────────────
function PhotoUploadWizard({ T, file, onClose, onSaved }) {
  const [step, setStep] = useS3(1);
  const [angle, setAngle] = useS3('frente'); // frente | lateral | atras
  const [isPublic, setIsPublic] = useS3(false);
  const [weight, setWeight] = useS3('');
  const [waist, setWaist] = useS3('');
  const [hip, setHip] = useS3('');
  const [chest, setChest] = useS3('');
  const [notes, setNotes] = useS3('');
  const [saving, setSaving] = useS3(false);
  const previewUrl = React.useMemo(() => URL.createObjectURL(file), [file]);
  React.useEffect(() => () => URL.revokeObjectURL(previewUrl), [previewUrl]);

  async function save() {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { window.__pauNav?.toast('Sin conexión', { icon: '⚠️' }); return; }
    setSaving(true);
    try {
      const { data: { user } } = await sb.auth.getUser();
      if (!user) throw new Error('Sin sesión');
      const ext = file.name.split('.').pop()?.toLowerCase() || 'jpg';
      // Codificar metadata en el nombre: {angle}-{priv|pub}-{timestamp}.{ext}
      const fname = `${angle}-${isPublic ? 'pub' : 'priv'}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`;
      const path = `${user.id}/${fname}`;
      const { error: upErr } = await sb.storage.from('progress-photos').upload(path, file, {
        cacheControl: '3600', upsert: false, contentType: file.type,
      });
      if (upErr) throw upErr;
      // Si añadió medidas, guardarlas (mismo timestamp para vincular)
      const measurements = {};
      if (weight) measurements.weight = parseFloat(weight);
      if (waist) measurements.waist = parseFloat(waist);
      if (hip) measurements.hip = parseFloat(hip);
      if (chest) measurements.chest_cm = parseFloat(chest);
      if (Object.keys(measurements).length > 0) {
        try {
          await sb.from('user_measurements').insert({
            user_id: user.id,
            ...measurements,
            notes: notes || null,
            taken_at: new Date().toISOString(),
          });
          window.PauUserRetos?.awardPoints?.(15, 'measurement');
        } catch (mErr) {
          console.warn('[measurements]', mErr?.message);
        }
      }
      onSaved && onSaved();
    } catch (err) {
      console.warn('[wizard-upload]', err);
      window.__pauNav?.toast('Error: ' + (err.message || 'no se pudo subir'), { icon: '⚠️' });
    } finally {
      setSaving(false);
    }
  }

  function chip(label, value, selected, onClick) {
    return (
      <button onClick={onClick} style={{
        padding: '10px 14px', borderRadius: 999, cursor: 'pointer',
        border: selected ? `1.5px solid ${T.accent}` : T.border,
        background: selected ? T.accent : T.card,
        color: selected ? '#fff' : T.text,
        fontFamily: 'Poppins', fontSize: 12, fontWeight: 600,
        transition: 'all 200ms ease',
      }}>{label}</button>
    );
  }

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 9999,
      background: 'rgba(0,0,0,0.7)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
    }}>
      <div style={{
        width: '100%', maxWidth: 480, maxHeight: '90vh',
        background: T.card, color: T.text,
        borderTopLeftRadius: 24, borderTopRightRadius: 24,
        overflow: 'auto', display: 'flex', flexDirection: 'column',
      }}>
        {/* Header */}
        <div style={{
          padding: '16px 20px 12px',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          borderBottom: `0.5px solid ${T.subtle}`,
        }}>
          <button onClick={onClose} style={{
            border: 'none', background: 'transparent', cursor: 'pointer',
            fontSize: 16, color: T.muted, padding: 4,
          }}>×</button>
          <div style={{ fontFamily: 'Poppins', fontSize: 13, fontWeight: 700, color: T.text }}>
            Foto de progreso · paso {step} de 3
          </div>
          {step < 3 ? (
            <button onClick={() => setStep(step + 1)} style={{
              border: 'none', background: 'transparent', cursor: 'pointer',
              fontSize: 12, color: T.muted, fontWeight: 600,
            }}>Saltar →</button>
          ) : (
            <span style={{ width: 60 }}/>
          )}
        </div>

        {/* Progress bar */}
        <div style={{ height: 3, background: T.subtle, position: 'relative' }}>
          <div style={{
            position: 'absolute', top: 0, left: 0, height: '100%',
            width: `${(step / 3) * 100}%`,
            background: T.accent,
            transition: 'width 240ms ease',
          }}/>
        </div>

        {/* Preview de la foto — siempre visible */}
        <div style={{
          padding: '16px 20px 0',
          display: 'flex', justifyContent: 'center',
        }}>
          <div style={{
            width: 140, aspectRatio: '3 / 4', borderRadius: 20,
            background: '#000', overflow: 'hidden', position: 'relative',
          }}>
            <img src={previewUrl} alt="preview" style={{
              width: '100%', height: '100%', objectFit: 'cover',
            }}/>
          </div>
        </div>

        {/* STEP 1: Ángulo */}
        {step === 1 && (
          <div style={{ padding: '20px 22px' }}>
            <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 6 }}>Paso 1</div>
            <div className="pf-display" style={{ fontSize: 20, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Qué ángulo es esta foto?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Esto te ayuda a comparar después contra fotos del mismo ángulo.
            </div>
            <div style={{
              marginTop: 18, display: 'flex', flexWrap: 'wrap', gap: 8,
            }}>
              {chip('🚶‍♀️ Frente', 'frente', angle === 'frente', () => setAngle('frente'))}
              {chip('↔️ Lateral', 'lateral', angle === 'lateral', () => setAngle('lateral'))}
              {chip('🔙 Atrás', 'atras', angle === 'atras', () => setAngle('atras'))}
            </div>
            <button onClick={() => setStep(2)} style={{
              marginTop: 24, width: '100%', padding: '14px',
              border: 'none', borderRadius: 20, cursor: 'pointer',
              background: T.accent, color: '#fff',
              fontFamily: 'Poppins', fontSize: 14, fontWeight: 600,
              boxShadow: `0 6px 18px ${T.accent}40`,
            }}>Continuar →</button>
          </div>
        )}

        {/* STEP 2: Privacidad */}
        {step === 2 && (
          <div style={{ padding: '20px 22px' }}>
            <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 6 }}>Paso 2</div>
            <div className="pf-display" style={{ fontSize: 20, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Privada o pública?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Pública aparece en tu perfil de la comunidad. Privada solo la ves tú.
            </div>
            <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 10 }}>
              <button onClick={() => setIsPublic(false)} style={{
                padding: '16px', borderRadius: 20, cursor: 'pointer',
                border: !isPublic ? `1.5px solid ${T.accent}` : T.border,
                background: !isPublic ? `${T.accent}15` : T.card,
                color: T.text, textAlign: 'left', fontFamily: 'inherit',
                display: 'flex', alignItems: 'center', gap: 12,
              }}>
                <span style={{ fontSize: 24 }}>🔒</span>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 14, fontWeight: 700 }}>Privada</div>
                  <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>Solo tú la ves</div>
                </div>
                {!isPublic && <span style={{ color: T.accent, fontSize: 18 }}>✓</span>}
              </button>
              <button onClick={() => setIsPublic(true)} style={{
                padding: '16px', borderRadius: 20, cursor: 'pointer',
                border: isPublic ? `1.5px solid ${T.accent}` : T.border,
                background: isPublic ? `${T.accent}15` : T.card,
                color: T.text, textAlign: 'left', fontFamily: 'inherit',
                display: 'flex', alignItems: 'center', gap: 12,
              }}>
                <span style={{ fontSize: 24 }}>👥</span>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 14, fontWeight: 700 }}>Pública</div>
                  <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>Aparece en tu perfil comunidad</div>
                </div>
                {isPublic && <span style={{ color: T.accent, fontSize: 18 }}>✓</span>}
              </button>
            </div>
            <button onClick={() => setStep(3)} style={{
              marginTop: 24, width: '100%', padding: '14px',
              border: 'none', borderRadius: 20, cursor: 'pointer',
              background: T.accent, color: '#fff',
              fontFamily: 'Poppins', fontSize: 14, fontWeight: 600,
              boxShadow: `0 6px 18px ${T.accent}40`,
            }}>Continuar →</button>
          </div>
        )}

        {/* STEP 3: Medidas + notas (opcional) */}
        {step === 3 && (
          <div style={{ padding: '20px 22px' }}>
            <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 6 }}>Paso 3 · opcional</div>
            <div className="pf-display" style={{ fontSize: 20, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Añadir medidas?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Útil para ver tu progreso con números. Puedes saltar todos.
            </div>
            <div style={{ marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              {[
                { l: 'Peso', u: 'kg', v: weight, set: setWeight },
                { l: 'Cintura', u: 'cm', v: waist, set: setWaist },
                { l: 'Cadera', u: 'cm', v: hip, set: setHip },
                { l: 'Pecho', u: 'cm', v: chest, set: setChest },
              ].map((f, i) => (
                <div key={i} style={{
                  padding: '12px 14px', borderRadius: 14,
                  background: T.subtle,
                }}>
                  <div style={{ fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.3,  }}>
                    {f.l} ({f.u})
                  </div>
                  <input
                    type="number" step="0.1" placeholder="—"
                    value={f.v} onChange={(e) => f.set(e.target.value)}
                    style={{
                      width: '100%', border: 'none', background: 'transparent', outline: 'none',
                      fontFamily: 'Poppins', fontSize: 18, fontWeight: 700,
                      color: T.text, padding: '6px 0 0',
                    }}
                  />
                </div>
              ))}
            </div>
            <div style={{ marginTop: 14 }}>
              <div style={{ fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.3,  marginBottom: 6 }}>
                Nota (opcional)
              </div>
              <textarea
                value={notes} onChange={(e) => setNotes(e.target.value)}
                placeholder="¿Cómo te sientes? 💗"
                rows={2}
                style={{
                  width: '100%', padding: '10px 12px', borderRadius: 14,
                  border: T.border, background: T.subtle, color: T.text,
                  fontFamily: 'Poppins', fontSize: 13, resize: 'none', outline: 'none',
                }}
              />
            </div>
            <button onClick={save} disabled={saving} style={{
              marginTop: 20, width: '100%', padding: '14px',
              border: 'none', borderRadius: 20, cursor: saving ? 'wait' : 'pointer',
              background: T.accent, color: '#fff',
              fontFamily: 'Poppins', fontSize: 14, fontWeight: 600,
              boxShadow: `0 6px 18px ${T.accent}40`,
              opacity: saving ? 0.7 : 1,
            }}>{saving ? 'Subiendo…' : 'Guardar foto 📸'}</button>
            <button onClick={() => { setWeight(''); setWaist(''); setHip(''); setChest(''); setNotes(''); save(); }} disabled={saving} style={{
              marginTop: 8, width: '100%', padding: '10px',
              border: 'none', borderRadius: 14, cursor: saving ? 'wait' : 'pointer',
              background: 'transparent', color: T.muted,
              fontFamily: 'Poppins', fontSize: 12, fontWeight: 600,
            }}>Saltar medidas y guardar</button>
          </div>
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// MultiPhotoWizard — pide las 3 fotos (frente / lado / atrás)
// Cada una opcional (puedes saltarla). Después privacidad + medidas.
// Sube todo en bloque al guardar.
// ─────────────────────────────────────────────────────────────
function MultiPhotoWizard({ T, onClose, onSaved }) {
  // step: 'photos' | 'privacy' | 'measures'
  const [step, setStep] = useS3('photos');
  // files: { frente: File | null, lateral: File | null, atras: File | null }
  const [files, setFiles] = useS3({ frente: null, lateral: null, atras: null });
  const [isPublic, setIsPublic] = useS3(false);
  const [weight, setWeight] = useS3('');
  const [waist, setWaist] = useS3('');
  const [hip, setHip] = useS3('');
  const [chest, setChest] = useS3('');
  const [notes, setNotes] = useS3('');
  const [saving, setSaving] = useS3(false);
  const inputsRef = React.useRef({});

  // Object URLs para preview (se revocan al cerrar)
  const previews = React.useMemo(() => {
    const out = {};
    Object.entries(files).forEach(([k, f]) => {
      if (f) out[k] = URL.createObjectURL(f);
    });
    return out;
  }, [files]);
  React.useEffect(() => {
    return () => {
      Object.values(previews).forEach(url => { try { URL.revokeObjectURL(url); } catch {} });
    };
  }, [previews]);

  function pick(angle) {
    const input = inputsRef.current[angle];
    if (input) input.click();
  }
  function onFile(angle, e) {
    const f = e.target.files?.[0];
    e.target.value = '';
    if (!f) return;
    setFiles(prev => ({ ...prev, [angle]: f }));
  }
  function removeFile(angle) {
    setFiles(prev => ({ ...prev, [angle]: null }));
  }

  const anyFile = Object.values(files).some(Boolean);

  async function saveAll() {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { window.__pauNav?.toast('Sin conexión', { icon: '⚠️' }); return; }
    if (!anyFile) {
      window.__pauNav?.toast('Sube al menos una foto', { icon: '⚠️' });
      setStep('photos');
      return;
    }
    setSaving(true);
    try {
      const { data: { user } } = await sb.auth.getUser();
      if (!user) throw new Error('Sin sesión');
      const ts = Date.now();
      const ok = [];
      const fail = [];
      // Subir cada foto que esté presente
      for (const [angle, file] of Object.entries(files)) {
        if (!file) continue;
        const ext = (file.name.split('.').pop() || 'jpg').toLowerCase();
        const fname = `${angle}-${isPublic ? 'pub' : 'priv'}-${ts}-${Math.random().toString(36).slice(2, 6)}.${ext}`;
        const path = `${user.id}/${fname}`;
        const { error: upErr } = await sb.storage.from('progress-photos').upload(path, file, {
          cacheControl: '3600', upsert: false, contentType: file.type,
        });
        if (upErr) {
          console.warn('[multi-photo]', angle, upErr.message);
          fail.push(angle);
        } else {
          ok.push(angle);
        }
      }
      // Guardar medidas si las añadió
      const measurements = {};
      if (weight) measurements.weight = parseFloat(weight);
      if (waist) measurements.waist = parseFloat(waist);
      if (hip) measurements.hip = parseFloat(hip);
      if (chest) measurements.chest_cm = parseFloat(chest);
      if (Object.keys(measurements).length > 0) {
        try {
          await sb.from('user_measurements').insert({
            user_id: user.id,
            ...measurements,
            notes: notes || null,
            taken_at: new Date().toISOString(),
          });
          window.PauUserRetos?.awardPoints?.(15, 'measurement');
        } catch (mErr) {
          console.warn('[measurements]', mErr?.message);
        }
      }
      // Puntos por fotos
      try { window.PauUserRetos?.awardPoints?.(10 * ok.length, 'photo'); } catch {}
      if (fail.length > 0 && ok.length === 0) {
        window.__pauNav?.toast('Error al subir las fotos', { icon: '⚠️' });
        setSaving(false);
        return;
      }
      onSaved && onSaved();
    } catch (err) {
      console.warn('[wizard-upload]', err);
      window.__pauNav?.toast('Error: ' + (err?.message || 'no se pudo subir'), { icon: '⚠️' });
      setSaving(false);
    }
  }

  const angles = [
    { id: 'frente', label: 'Frente', emoji: '🚶‍♀️', desc: 'De cara, brazos relajados' },
    { id: 'lateral', label: 'De lado', emoji: '↔️', desc: 'Mostrando el perfil' },
    { id: 'atras', label: 'Atrás', emoji: '🔙', desc: 'De espaldas' },
  ];

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

        {/* Header con pasos */}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
          <button onClick={onClose} style={{
            border: 'none', background: 'transparent', cursor: 'pointer',
            fontSize: 18, color: T.muted, padding: 4,
          }}>×</button>
          <div style={{ fontFamily: 'Poppins', fontSize: 12, fontWeight: 700, color: T.text }}>
            {step === 'photos' ? '1/3 · Fotos' : step === 'privacy' ? '2/3 · Privacidad' : '3/3 · Medidas (opcional)'}
          </div>
          <span style={{ width: 20 }}/>
        </div>

        {/* STEP 1: Foto frente/lado/atrás (las 3, cualquiera opcional) */}
        {step === 'photos' && (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              Sube tus 3 fotos
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Frente · Lado · Atrás. Puedes saltar las que quieras y subir solo las que tengas.
            </div>

            <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 10 }}>
              {angles.map(a => {
                const file = files[a.id];
                const preview = previews[a.id];
                return (
                  <div key={a.id} style={{
                    padding: 12, borderRadius: 20,
                    background: file ? `${T.accent}10` : T.subtle,
                    border: file ? `1.5px solid ${T.accent}40` : 'none',
                    display: 'flex', alignItems: 'center', gap: 12,
                  }}>
                    {/* Thumbnail */}
                    {preview ? (
                      <div style={{
                        width: 62, height: 80, borderRadius: 10,
                        background: '#000', overflow: 'hidden', flexShrink: 0,
                        position: 'relative',
                      }}>
                        <img src={preview} alt={a.label} style={{
                          width: '100%', height: '100%', objectFit: 'cover',
                        }}/>
                      </div>
                    ) : (
                      <div style={{
                        width: 62, height: 80, borderRadius: 10,
                        background: T.card, flexShrink: 0,
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontSize: 26,
                      }}>{a.emoji}</div>
                    )}
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 14, fontWeight: 700, color: T.text }}>{a.label}</div>
                      <div style={{ fontSize: 11, color: T.muted, marginTop: 2, lineHeight: 1.4 }}>
                        {file ? '✓ Lista para subir' : a.desc}
                      </div>
                      <div style={{ marginTop: 8, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                        <button onClick={() => pick(a.id)} style={{
                          padding: '6px 12px', borderRadius: 999, border: 'none', cursor: 'pointer',
                          background: T.accent, color: '#fff',
                          fontFamily: 'Poppins', fontSize: 11, fontWeight: 700,
                        }}>📷 {file ? 'Cambiar' : 'Tomar / Subir'}</button>
                        {file && (
                          <button onClick={() => removeFile(a.id)} style={{
                            padding: '6px 12px', borderRadius: 999, border: T.border, cursor: 'pointer',
                            background: 'transparent', color: T.muted,
                            fontFamily: 'Poppins', fontSize: 11, fontWeight: 600,
                          }}>Quitar</button>
                        )}
                      </div>
                    </div>
                    <input
                      ref={(el) => { inputsRef.current[a.id] = el; }}
                      type="file" accept="image/*"
                      style={{ display: 'none' }}
                      onChange={(e) => onFile(a.id, e)}
                    />
                  </div>
                );
              })}
            </div>

            <div style={{ marginTop: 20, display: 'flex', gap: 8 }}>
              <button onClick={onClose} style={{
                flex: 1, padding: '14px',
                border: T.border, borderRadius: 14, cursor: 'pointer',
                background: T.card, color: T.text,
                fontFamily: 'Poppins', fontSize: 13, fontWeight: 600,
              }}>Cancelar</button>
              <button onClick={() => setStep('privacy')} disabled={!anyFile} style={{
                flex: 2, padding: '14px',
                border: 'none', borderRadius: 14, cursor: anyFile ? 'pointer' : 'default',
                background: anyFile ? T.accent : T.subtle,
                color: anyFile ? '#fff' : T.muted,
                fontFamily: 'Poppins', fontSize: 14, fontWeight: 700,
                boxShadow: anyFile ? `0 6px 18px ${T.accent}40` : 'none',
              }}>Continuar →</button>
            </div>
          </div>
        )}

        {/* STEP 2: Privacidad */}
        {step === 'privacy' && (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Privadas o públicas?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Aplica a las {Object.values(files).filter(Boolean).length} fotos que subes ahora.
            </div>
            <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 10 }}>
              <button onClick={() => setIsPublic(false)} style={{
                padding: '16px', borderRadius: 20, cursor: 'pointer',
                border: !isPublic ? `1.5px solid ${T.accent}` : T.border,
                background: !isPublic ? `${T.accent}15` : T.card,
                color: T.text, textAlign: 'left', fontFamily: 'inherit',
                display: 'flex', alignItems: 'center', gap: 12,
              }}>
                <span style={{ fontSize: 24 }}>🔒</span>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 14, fontWeight: 700 }}>Privadas</div>
                  <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>Solo tú las ves</div>
                </div>
                {!isPublic && <span style={{ color: T.accent, fontSize: 18 }}>✓</span>}
              </button>
              <button onClick={() => setIsPublic(true)} style={{
                padding: '16px', borderRadius: 20, cursor: 'pointer',
                border: isPublic ? `1.5px solid ${T.accent}` : T.border,
                background: isPublic ? `${T.accent}15` : T.card,
                color: T.text, textAlign: 'left', fontFamily: 'inherit',
                display: 'flex', alignItems: 'center', gap: 12,
              }}>
                <span style={{ fontSize: 24 }}>👥</span>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 14, fontWeight: 700 }}>Públicas</div>
                  <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>Aparecen en tu perfil comunidad</div>
                </div>
                {isPublic && <span style={{ color: T.accent, fontSize: 18 }}>✓</span>}
              </button>
            </div>
            <div style={{ marginTop: 20, display: 'flex', gap: 8 }}>
              <button onClick={() => setStep('photos')} style={{
                flex: 1, padding: '14px',
                border: T.border, borderRadius: 14, cursor: 'pointer',
                background: T.card, color: T.text,
                fontFamily: 'Poppins', fontSize: 13, fontWeight: 600,
              }}>Atrás</button>
              <button onClick={() => setStep('measures')} style={{
                flex: 2, padding: '14px',
                border: 'none', borderRadius: 14, cursor: 'pointer',
                background: T.accent, color: '#fff',
                fontFamily: 'Poppins', fontSize: 14, fontWeight: 700,
                boxShadow: `0 6px 18px ${T.accent}40`,
              }}>Continuar →</button>
            </div>
          </div>
        )}

        {/* STEP 3: Medidas opcionales + guardar */}
        {step === 'measures' && (
          <div>
            <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.3 }}>
              ¿Añadir medidas?
            </div>
            <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.4 }}>
              Opcional. Útil para ver tu progreso con números.
            </div>
            <div style={{ marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              {[
                { l: 'Peso', u: 'kg', v: weight, set: setWeight },
                { l: 'Cintura', u: 'cm', v: waist, set: setWaist },
                { l: 'Cadera', u: 'cm', v: hip, set: setHip },
                { l: 'Pecho', u: 'cm', v: chest, set: setChest },
              ].map((f, i) => (
                <div key={i} style={{
                  padding: '12px 14px', borderRadius: 14,
                  background: T.subtle,
                }}>
                  <div style={{ fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.3,  }}>
                    {f.l} ({f.u})
                  </div>
                  <input
                    type="number" step="0.1" placeholder="—"
                    value={f.v} onChange={(e) => f.set(e.target.value)}
                    style={{
                      width: '100%', border: 'none', background: 'transparent', outline: 'none',
                      fontFamily: 'Poppins', fontSize: 18, fontWeight: 700,
                      color: T.text, padding: '6px 0 0',
                    }}
                  />
                </div>
              ))}
            </div>
            <div style={{ marginTop: 14 }}>
              <div style={{ fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.3,  marginBottom: 6 }}>
                Nota (opcional)
              </div>
              <textarea
                value={notes} onChange={(e) => setNotes(e.target.value)}
                placeholder="¿Cómo te sientes? 💗"
                rows={2}
                style={{
                  width: '100%', padding: '10px 12px', borderRadius: 14,
                  border: T.border, background: T.subtle, color: T.text,
                  fontFamily: 'Poppins', fontSize: 13, resize: 'none', outline: 'none',
                }}
              />
            </div>
            <div style={{ marginTop: 20, display: 'flex', gap: 8 }}>
              <button onClick={() => setStep('privacy')} disabled={saving} style={{
                flex: 1, padding: '14px',
                border: T.border, borderRadius: 14, cursor: saving ? 'wait' : 'pointer',
                background: T.card, color: T.text,
                fontFamily: 'Poppins', fontSize: 13, fontWeight: 600,
              }}>Atrás</button>
              <button onClick={saveAll} disabled={saving} style={{
                flex: 2, padding: '14px',
                border: 'none', borderRadius: 14, cursor: saving ? 'wait' : 'pointer',
                background: T.accent, color: '#fff',
                fontFamily: 'Poppins', fontSize: 14, fontWeight: 700,
                boxShadow: `0 6px 18px ${T.accent}40`,
                opacity: saving ? 0.7 : 1,
                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
              }}>
                {saving && (
                  <span style={{
                    width: 16, height: 16, border: '2px solid rgba(255,255,255,0.3)',
                    borderTopColor: '#fff', borderRadius: 50,
                    animation: 'spin 700ms linear infinite',
                  }}/>
                )}
                {saving ? 'Subiendo…' : 'Guardar fotos 📸'}
              </button>
            </div>
            <button onClick={() => { setWeight(''); setWaist(''); setHip(''); setChest(''); setNotes(''); saveAll(); }} disabled={saving} style={{
              marginTop: 8, width: '100%', padding: '10px',
              border: 'none', borderRadius: 14, cursor: saving ? 'wait' : 'pointer',
              background: 'transparent', color: T.muted,
              fontFamily: 'Poppins', fontSize: 12, fontWeight: 600,
            }}>Saltar medidas y guardar</button>
          </div>
        )}
      </div>
    </div>
  );
}

function MedidasTab({ T, latest, prev }) {
  // Medidas REALES desde Supabase (tabla measurements)
  const [rows, setRows] = useS3(null);
  const [showForm, setShowForm] = useS3(false);
  const fields = [
    { k: 'weight',  l: 'Peso',     u: 'kg' },
    { k: 'waist',   l: 'Cintura',  u: 'cm' },
    { k: 'hip',     l: 'Cadera',   u: 'cm' },
    { k: 'arm',     l: 'Brazo',    u: 'cm' },
    { k: 'thigh',   l: 'Muslo',    u: 'cm' },
  ];

  async function load() {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setRows([]); return; }
    const { data: { user } } = await sb.auth.getUser();
    if (!user) { setRows([]); return; }
    try {
      const { data } = await sb.from('user_measurements')
        .select('*')
        .eq('user_id', user.id)
        .order('taken_at', { ascending: false })
        .limit(60);
      setRows(data || []);
    } catch {
      setRows([]);
    }
  }
  React.useEffect(() => { load(); }, []);

  const cur = rows && rows[0];
  const before = rows && rows[rows.length - 1];
  const hasData = rows && rows.length > 0;

  return (
    <div>
      {/* Evolución del peso — fase 3 del roadmap */}
      <WeightChart T={T} rows={rows}/>
      <div style={{ padding: '18px 20px 4px', display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
        <span style={{ fontFamily: 'Poppins', fontSize: 11, color: T.muted, fontWeight: 600, letterSpacing: 0.5,  }}>
          {hasData ? `Última · ${new Date(cur.taken_at).toLocaleDateString('es', { day: 'numeric', month: 'short' })}` : 'Aún no hay medidas'}
        </span>
        <button onClick={() => setShowForm(true)} style={{
          padding: '6px 12px', border: 'none', borderRadius: 999, cursor: 'pointer',
          background: T.accent, color: '#fff',
          fontFamily: 'Poppins', fontSize: 11, fontWeight: 600,
        }}>＋ Medir hoy</button>
      </div>

      {rows === null && (
        <div style={{ padding: 30, textAlign: 'center', color: T.muted, fontSize: 13 }}>
          <div style={{
            width: 24, height: 24, borderRadius: 50, margin: '0 auto 10px',
            border: '2px solid ' + T.subtle, borderTopColor: T.accent,
            animation: 'spin 800ms linear infinite',
          }}/>
          Cargando…
        </div>
      )}

      {/* Estado vacío */}
      {hasData === false && (
        <div style={{ padding: '24px 20px 0', textAlign: 'center' }}>
          <div style={{ fontSize: 40, marginBottom: 10 }}>📏</div>
          <div style={{ fontSize: 13, fontWeight: 600, color: T.text }}>Empieza a llevar tu progreso</div>
          <div style={{ fontSize: 11, color: T.muted, marginTop: 4, lineHeight: 1.5 }}>
            Toma tu primera medición para comparar después.
          </div>
        </div>
      )}

      {/* Datos */}
      {hasData && (
        <div style={{ padding: '14px 20px 0', display: 'flex', flexDirection: 'column', gap: 10 }}>
          {fields.map(f => {
            const v = cur?.[f.k];
            const vBefore = before?.[f.k];
            if (v == null) return null;
            const delta = vBefore != null ? (v - vBefore) : 0;
            const better = delta < 0;
            return (
              <div key={f.k} style={{
                padding: '14px 20px', background: T.card, borderRadius: 20, border: T.border,
                display: 'flex', alignItems: 'center', gap: 14,
              }}>
                <div style={{ flex: 1 }}>
                  <div style={{ fontFamily: 'Poppins', fontSize: 12, color: T.muted, fontWeight: 500 }}>{f.l}</div>
                  <div style={{ display: 'flex', alignItems: 'baseline', gap: 4, marginTop: 2 }}>
                    <span style={{ fontFamily: 'Poppins', fontSize: 22, fontWeight: 700, color: T.text, letterSpacing: -0.5 }}>{v}</span>
                    <span style={{ fontFamily: 'Poppins', fontSize: 12, color: T.muted, fontWeight: 500 }}>{f.u}</span>
                  </div>
                </div>
                {rows.length > 1 && delta !== 0 && (
                  <div style={{
                    padding: '6px 10px', borderRadius: 999,
                    background: better ? 'rgba(168,197,160,0.18)' : T.subtle,
                    color: better ? '#5C7A56' : T.muted,
                    fontFamily: 'Poppins', fontSize: 11, fontWeight: 600,
                  }}>{delta > 0 ? '+' : ''}{delta.toFixed(1)} {f.u}</div>
                )}
              </div>
            );
          })}

          {/* Histórico */}
          {rows.length > 1 && (
            <>
              <div style={{ padding: '14px 0 4px', fontFamily: 'Poppins', fontSize: 11, color: T.muted, fontWeight: 600, letterSpacing: 0.5,  }}>Histórico</div>
              <div style={{ background: T.card, borderRadius: 20, overflow: 'hidden', border: T.border }}>
                {rows.map((m, i) => (
                  <div key={m.id || i} style={{
                    padding: '12px 16px',
                    display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                    borderBottom: i < rows.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={{ fontFamily: 'Poppins', fontSize: 12, fontWeight: 500, color: T.text }}>{new Date(m.taken_at).toLocaleDateString('es', { day: 'numeric', month: 'short', year: 'numeric' })}</span>
                    <span style={{ fontFamily: 'Poppins', fontSize: 11, color: T.muted }}>
                      {m.weight ? `${m.weight}kg` : ''}
                      {m.weight && m.waist ? ' · ' : ''}
                      {m.waist ? `${m.waist}cm cintura` : ''}
                    </span>
                  </div>
                ))}
              </div>
            </>
          )}
        </div>
      )}

      {showForm && <MeasurementForm T={T} onClose={() => setShowForm(false)} onSaved={() => { setShowForm(false); load(); }}/>}
    </div>
  );
}

function MeasurementForm({ T, onClose, onSaved }) {
  const [vals, setVals] = useS3({ weight: '', waist: '', hip: '', arm: '', thigh: '' });
  const [saving, setSaving] = useS3(false);
  const fields = [
    { k: 'weight', l: 'Peso',    u: 'kg' },
    { k: 'waist',  l: 'Cintura', u: 'cm' },
    { k: 'hip',    l: 'Cadera',  u: 'cm' },
    { k: 'arm',    l: 'Brazo',   u: 'cm' },
    { k: 'thigh',  l: 'Muslo',   u: 'cm' },
  ];

  async function save() {
    const sb = window.__PAU_SUPABASE;
    if (!sb) return;
    setSaving(true);
    try {
      const { data: { user } } = await sb.auth.getUser();
      if (!user) throw new Error('Sin sesión');
      const row = { user_id: user.id, taken_at: new Date().toISOString() };
      Object.entries(vals).forEach(([k, v]) => {
        const num = parseFloat(v);
        if (!isNaN(num)) row[k] = num;
      });
      const { error } = await sb.from('user_measurements').insert(row);
      if (error) throw error;
      try { window.PauUserRetos?.awardPoints?.(15, 'measurement'); } catch {}
      window.__pauNav?.toast('Medidas guardadas 💗', { icon: '✓' });
      onSaved && onSaved();
    } catch (e) {
      window.__pauNav?.toast('Error: ' + (e.message || e), { icon: '⚠️' });
    } finally {
      setSaving(false);
    }
  }

  return (
    <div onClick={onClose} style={{
      position: 'absolute', inset: 0, zIndex: 310,
      background: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'flex-end',
      animation: 'fade-in 200ms ease',
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', background: T.bg,
        borderTopLeftRadius: 28, borderTopRightRadius: 28,
        padding: '14px 22px 32px',
        animation: 'slide-up 300ms cubic-bezier(.2,.8,.2,1)',
        maxHeight: '92%', overflowY: 'auto',
      }}>
        <div style={{ width: 40, height: 4, borderRadius: 999, background: 'rgba(0,0,0,0.15)', margin: '4px auto 18px' }}/>
        <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.4 }}>Nueva medición</div>
        <div style={{ fontSize: 12, color: T.muted, marginTop: 4 }}>Deja en blanco lo que no quieras medir hoy</div>

        <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 8 }}>
          {fields.map(f => (
            <div key={f.k} style={{
              padding: '12px 14px', background: T.card, borderRadius: 20, border: T.border,
              display: 'flex', alignItems: 'center', gap: 10,
            }}>
              <span style={{ flex: 1, fontSize: 13, fontWeight: 600, color: T.text }}>{f.l}</span>
              <input
                type="number"
                inputMode="decimal"
                step="0.1"
                value={vals[f.k]}
                onChange={(e) => setVals(v => ({ ...v, [f.k]: e.target.value }))}
                placeholder="—"
                style={{
                  width: 90, padding: '8px 10px', textAlign: 'right',
                  border: T.border, borderRadius: 10, background: T.subtle,
                  fontSize: 14, fontWeight: 700, color: T.text, outline: 'none',
                }}
              />
              <span style={{ width: 24, fontSize: 11, color: T.muted, fontWeight: 600 }}>{f.u}</span>
            </div>
          ))}
        </div>

        <button onClick={save} disabled={saving} style={{
          marginTop: 16, width: '100%', padding: '16px',
          border: 'none', borderRadius: 20, cursor: saving ? 'wait' : 'pointer',
          background: T.accent, color: '#fff',
          fontSize: 14, fontWeight: 700, letterSpacing: 0.2,
          opacity: saving ? 0.7 : 1,
        }}>{saving ? 'Guardando…' : 'Guardar medición'}</button>
        <button onClick={onClose} style={{
          marginTop: 8, width: '100%', padding: '12px',
          border: 'none', background: 'transparent', cursor: 'pointer',
          fontSize: 12, fontWeight: 600, color: T.muted,
        }}>Cancelar</button>
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// HISTORIAL
// ═════════════════════════════════════════════════════════════
function HistorialScreen({ theme, nav }) {
  const T = theme;
  const [filter, setFilter] = useS3('todo');
  const [items, setItems] = useS3(null); // null=loading, []=vacío, [...]=con datos
  const [error, setError] = useS3(null);

  // Carga historial REAL desde Supabase
  React.useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setItems([]); return; }
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) { setItems([]); return; }

        // 1) Días de retos completados (con timestamp real)
        const { data: dayCompletions } = await sb
          .from('day_completions')
          .select('reto_id, day_number, completed_at')
          .eq('user_id', user.id)
          .order('completed_at', { ascending: false })
          .limit(100);

        // 2) Retos completados al 100% (completed_at no null en user_retos)
        const { data: retosFinished } = await sb
          .from('user_retos')
          .select('reto_id, completed_at, completed_days')
          .eq('user_id', user.id)
          .not('completed_at', 'is', null)
          .order('completed_at', { ascending: false })
          .limit(50);

        // 3) Recetas marcadas / comidas registradas (si existe meal_log)
        let mealLogs = [];
        try {
          const { data } = await sb
            .from('meal_logs')
            .select('id, meal_name, logged_at')
            .eq('user_id', user.id)
            .order('logged_at', { ascending: false })
            .limit(50);
          mealLogs = data || [];
        } catch {}

        // Normalizar a items
        const normalized = [];
        (dayCompletions || []).forEach(d => {
          const reto = window.pauRetoById ? window.pauRetoById(d.reto_id) : null;
          normalized.push({
            kind: 'reto',
            emoji: reto?.emoji || '💪',
            title: reto ? `${reto.title} · día ${d.day_number}` : `Día ${d.day_number}`,
            meta: 'Día completado',
            when: timeAgo(d.completed_at),
            ts: d.completed_at,
          });
        });
        (retosFinished || []).forEach(r => {
          const reto = window.pauRetoById ? window.pauRetoById(r.reto_id) : null;
          const doneDays = (r.completed_days || []).length;
          const totalDays = reto?.days || 0;
          // Distinguir: completado 100% vs abandonado
          const fullyCompleted = totalDays > 0 && doneDays >= totalDays;
          normalized.push({
            kind: 'reto',
            emoji: fullyCompleted ? '🏆' : '👋',
            title: reto
              ? (fullyCompleted ? `${reto.title} · completado` : `${reto.title} · abandonado`)
              : (fullyCompleted ? 'Reto completado' : 'Reto abandonado'),
            meta: fullyCompleted
              ? `${doneDays}/${totalDays} días · ¡todo!`
              : `${doneDays}/${totalDays || '?'} días hechos`,
            when: timeAgo(r.completed_at),
            ts: r.completed_at,
          });
        });
        (mealLogs || []).forEach(m => {
          normalized.push({
            kind: 'receta',
            emoji: '🍓',
            title: m.meal_name || 'Comida',
            meta: 'Anotada en diario',
            when: timeAgo(m.logged_at),
            ts: m.logged_at,
          });
        });

        // Ordenar por fecha desc
        normalized.sort((a, b) => new Date(b.ts) - new Date(a.ts));
        setItems(normalized);
      } catch (e) {
        setError(e.message || 'Error cargando historial');
        setItems([]);
      }
    })();
  }, []);

  function timeAgo(iso) {
    if (!iso) return '';
    const d = new Date(iso);
    const now = new Date();
    const diffMs = now - d;
    const diffMin = Math.floor(diffMs / 60000);
    if (diffMin < 1) return 'ahora';
    if (diffMin < 60) return `hace ${diffMin} min`;
    const diffHr = Math.floor(diffMin / 60);
    if (diffHr < 24) return `hace ${diffHr} h`;
    const diffDay = Math.floor(diffHr / 24);
    if (diffDay === 1) return 'ayer';
    if (diffDay < 7) return `hace ${diffDay} días`;
    return d.toLocaleDateString('es', { day: 'numeric', month: 'short' });
  }

  const filtered = (items || []).filter(h => filter === 'todo' || h.kind === filter);
  const kindColor = { rutina: T.accent, reto: '#C9956B', receta: '#A8C5A0' };
  const kindLabel = { rutina: 'Rutina', reto: 'Reto', receta: 'Receta' };

  return (
    <div style={{ paddingBottom: 110 }}>
      <div style={{ padding: '12px 20px 4px' }}>
        <BackBtn onClick={() => nav.back()}/>
        <div style={{ marginTop: 14 }}>
          <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95 }}>
            Historial
          </div>
        </div>
      </div>

      <div style={{ display: 'flex', gap: 8, padding: '18px 20px 4px', overflowX: 'auto' }} className="hide-scroll">
        {['todo', 'reto', 'receta'].map(f => (
          <Pill key={f} active={filter===f} onClick={() => setFilter(f)} dark={T.dark} color={T.accent}>
            {f === 'todo' ? 'Todo' : kindLabel[f] + 's'}
          </Pill>
        ))}
      </div>

      {/* Loading — Skeleton */}
      {items === null && !error && <SkeletonList count={4} T={T}/>}

      {/* Vacío con CTA */}
      {items && filtered.length === 0 && !error && (
        <EmptyState
          emoji="📋" T={T}
          title="Tu historial está vacío"
          description="Cuando completes días, recetas o rutinas, aparecerán aquí ordenados por fecha."
          ctaLabel="Ver retos"
          onCta={() => nav.tab('retos')}
        />
      )}

      {error && (
        <div style={{ padding: 30, textAlign: 'center', color: T.muted, fontSize: 13 }}>{error}</div>
      )}

      {/* Lista REAL */}
      <div style={{ margin: '14px 20px 0', background: T.card, borderRadius: 20, border: T.border, overflow: 'hidden' }}>
        {filtered.map((h, i) => (
          <div key={i} style={{
            padding: '14px 16px',
            display: 'flex', alignItems: 'center', gap: 12,
            borderBottom: i < filtered.length - 1 ? (T.dark ? '0.5px solid rgba(255,255,255,0.06)' : '0.5px solid rgba(0,0,0,0.05)') : 'none',
          }}>
            <div style={{
              width: 44, height: 44, borderRadius: 14, flexShrink: 0,
              background: `${kindColor[h.kind]}18`,
              display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 22,
            }}>{h.emoji}</div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{
                  fontFamily: 'Poppins', fontSize: 9, fontWeight: 700, letterSpacing: 0.6, 
                  color: kindColor[h.kind],
                }}>{kindLabel[h.kind]}</span>
                <span style={{ fontFamily: 'Poppins', fontSize: 10, color: T.muted }}>· {h.when}</span>
              </div>
              <div style={{ fontFamily: 'Poppins', fontSize: 14, fontWeight: 600, color: T.text, marginTop: 2, letterSpacing: -0.2, lineHeight: 1.2 }}>{h.title}</div>
              <div style={{ fontFamily: 'Poppins', fontSize: 11, color: T.muted, marginTop: 2 }}>{h.meta}</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// RUTINA DETAIL — rutina suelta
// ═════════════════════════════════════════════════════════════
function RutinaDetailScreen({ rutinaId, theme, isPremium, nav }) {
  const T = theme;
  // Buscar primero en cache Supabase, luego en mock
  const supabaseRutina = (window.__pauCache?.rutinas || []).find(x => x.id === rutinaId);
  const mockRutina = RUTINAS.find(x => x.id === rutinaId);
  const r = supabaseRutina || mockRutina;
  if (!r) return null;

  const hasBunny = Boolean(supabaseRutina?.bunny_video_id);
  const hasBunnyVoice = Boolean(supabaseRutina?.bunny_video_id_voice);
  const isDemo = supabaseRutina?.is_demo || r.tag === 'DEMO';
  const isLocked = !isDemo && r.tag === 'PRO' && !isPremium;

  // URL de la portada
  const posterPath = supabaseRutina?.poster_path;
  const posterUrl = posterPath
    ? `${window.__PAU_SUPABASE_URL || ''}/storage/v1/object/public/rutinas-posters/${posterPath}`
    : null;

  function openPlayer() {
    if (isLocked) {
      nav.go('paywall');
      return;
    }
    if (hasBunny) {
      nav.openModal(<BunnyPlayerModal
        guid={supabaseRutina.bunny_video_id}
        guidVoice={supabaseRutina.bunny_video_id_voice}
        title={r.title}
        T={T}
        onClose={() => { nav.closeModal(); openRate(); }}
      />);
    } else {
      nav.toast('Esta rutina aún no tiene video subido', { icon: '🎬' });
    }
  }

  function openRate() {
    nav.openModal(<RateWorkoutModal
      open target={`${r.title} · ${r.minutes} min`}
      T={T} accent={T.accent} retoColor={T.accent}
      onClose={() => nav.closeModal()}
      onSave={async () => {
        nav.closeModal();
        const sb = window.__PAU_SUPABASE;
        try {
          // 1) +30 puntos por completar rutina suelta
          if (window.PauUserRetos?.awardPoints) {
            await window.PauUserRetos.awardPoints(30, 'rutina_completed', { rutina_id: r.id });
          }
          // 2) Insertar day_completion para que CUENTE PARA LA RACHA
          // reto_id=null porque es rutina libre (no de reto). day_number=0 = libre.
          // La racha cuenta fechas únicas, así que si hace varias rutinas el mismo
          // día no se duplica (Set de dates en getStats).
          if (sb) {
            const { data: { user } } = await sb.auth.getUser();
            if (user) {
              try {
                await sb.from('day_completions').insert({
                  user_id: user.id,
                  reto_id: null,
                  day_number: 0,
                  completed_at: new Date().toISOString(),
                });
              } catch (dcErr) {
                // Si falla por unique constraint (ya hubo rutina libre hoy), está OK
                console.warn('[rutina-libre dc]', dcErr?.message);
              }
              // 3) Refrescar profile + stats en cache
              const { data: p } = await sb.from('profiles')
                .select('id, full_name, username, avatar_url, bio, is_premium, premium_until, trial_ends_at, points, level, goal, target_days_per_week, height_cm, weight, birth_date, gender, phone, is_public')
                .eq('id', user.id).maybeSingle();
              if (p) {
                window.__pauCache.profile = { ...p, email: user.email || '' };
              }
              if (window.PauUserRetos?.getStats) {
                const s = await window.PauUserRetos.getStats();
                if (s) window.__pauCache.stats = s;
              }
              window.dispatchEvent(new CustomEvent('pau:cache-updated'));
            }
          }
        } catch (e) { console.warn('[points rutina]', e); }
        nav.toast('Rutina completada · +30 ⭐', { icon: '💪' });
      }}
    />);
  }

  return (
    <div style={{ paddingBottom: 110 }}>
      {/* PORTADA estilo Silbe: foto vertical 3:4 con gradient + texto blanco */}
      <div style={{
        position: 'relative', aspectRatio: '3 / 4',
        background: posterUrl
          ? `url(${posterUrl}) center/cover no-repeat`
          : `linear-gradient(160deg, ${T.subtle}, ${T.accent}50)`,
        overflow: 'hidden',
      }}>
        {/* Emoji centrado si no hay foto */}
        {!posterUrl && (
          <div style={{
            position: 'absolute', top: '38%', left: 0, right: 0, textAlign: 'center',
            fontSize: 90, opacity: 0.7,
          }}>{r.emoji}</div>
        )}
        {/* Gradient negro abajo */}
        <div style={{
          position: 'absolute', bottom: 0, left: 0, right: 0, height: '55%',
          background: 'linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.6) 70%, rgba(0,0,0,0.85) 100%)',
          pointerEvents: 'none',
        }}/>
        {/* Botones top */}
        <div style={{
          position: 'absolute', top: 'calc(env(safe-area-inset-top, 0px) + 16px)', left: 16, right: 16,
          display: 'flex', justifyContent: 'space-between', zIndex: 5,
        }}>
          <button onClick={() => nav.back()} style={{
            width: 36, height: 36, borderRadius: 999, border: 'none',
            background: 'rgba(255,255,255,0.95)', cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
          }}>{Icon.back('#2D2A26')}</button>
          {isDemo && (
            <span style={{
              padding: '5px 10px', borderRadius: 999,
              background: '#5C7A56', color: '#fff',
              fontSize: 9, fontWeight: 700, letterSpacing: 0.6,
              boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
            }}>PRUEBA GRATIS</span>
          )}
        </div>
        {/* Texto sobre gradient */}
        <div style={{
          position: 'absolute', bottom: 24, left: 22, right: 22, color: '#fff',
        }}>
          <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: 1.2,  opacity: 0.85 }}>
            {r.muscle}
          </div>
          <div style={{ fontSize: 28, fontWeight: 700, marginTop: 6, lineHeight: 1.1, letterSpacing: -0.6, textShadow: '0 2px 8px rgba(0,0,0,0.5)' }}>
            {r.title}
          </div>
          <div style={{ fontSize: 12, marginTop: 6, opacity: 0.9, display: 'flex', gap: 12 }}>
            <span>{r.minutes} min</span>
            <span>· {r.level || 'Medio'}</span>
            <span>· {r.equipment || 'Sin equipo'}</span>
          </div>
        </div>
      </div>

      {/* CTA */}
      <div style={{ padding: '20px 20px 0' }}>
        <button onClick={openPlayer} style={{
          width: '100%', padding: '18px',
          border: 'none', borderRadius: 20, cursor: 'pointer',
          background: isLocked ? T.subtle : T.accent,
          color: isLocked ? T.text : '#fff',
          fontFamily: 'Poppins', fontSize: 15, fontWeight: 600,
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
          boxShadow: isLocked ? 'none' : `0 6px 18px ${T.accent}55`,
        }}>
          {isLocked ? '🔒 Premium · 7 días gratis' : `▶ Empezar rutina · ${r.minutes} min`}
        </button>
      </div>

      {/* Descripción + info audio */}
      <div style={{ padding: '20px 22px 0' }}>
        {r.description && (
          <div style={{ fontFamily: 'Poppins', fontSize: 13, color: T.text, lineHeight: 1.6 }}>
            {r.description}
          </div>
        )}
        {hasBunnyVoice && !isLocked && (
          <div style={{
            marginTop: 14, padding: '10px 14px',
            background: T.subtle, borderRadius: 14,
            fontSize: 12, color: T.text, display: 'flex', alignItems: 'center', gap: 8,
          }}>
            🎵 Puedes elegir <b>con música</b> o <b>solo voz</b> cuando empieces.
          </div>
        )}
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// BUNNY PLAYER MODAL — con toggle música / solo voz
// ═════════════════════════════════════════════════════════════
function BunnyPlayerModal({ guid, guidVoice, title, T, onClose }) {
  const [mode, setMode] = React.useState('music'); // 'music' | 'voice'
  const [data, setData] = React.useState(null);
  const [error, setError] = React.useState(null);

  const activeGuid = mode === 'voice' && guidVoice ? guidVoice : guid;

  React.useEffect(() => {
    let abort = false;
    setData(null);
    (async () => {
      try {
        const res = await fetch(`/api/bunny/playback/${activeGuid}`);
        const json = await res.json();
        if (!res.ok) {
          if (json.paywall) {
            onClose && onClose();
            window.__pauNav && window.__pauNav.go('paywall');
            return;
          }
          throw new Error(json.error || 'No se pudo cargar');
        }
        if (!abort) setData(json);
      } catch (e) {
        if (!abort) setError(e.message || 'Error');
      }
    })();
    return () => { abort = true; };
  }, [activeGuid]);

  return (
    <div style={{
      position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.04)', zIndex: 200,
      display: 'flex', flexDirection: 'column',
      animation: 'fade-in 240ms ease',
    }}>
      {/* Top bar */}
      <div style={{
        padding: 'calc(env(safe-area-inset-top, 0px) + 16px) 18px 12px',
        display: 'flex', alignItems: 'center', gap: 12,
        background: 'linear-gradient(180deg, rgba(0,0,0,0.6), transparent)',
      }}>
        <button onClick={onClose} style={{
          width: 36, height: 36, borderRadius: 999, border: 'none',
          background: 'rgba(255,255,255,0.15)', cursor: 'pointer', color: '#fff', fontSize: 18,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>✕</button>
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: 'Poppins', fontSize: 10, color: 'rgba(255,255,255,0.5)', fontWeight: 600, letterSpacing: 0.6,  }}>Rutina</div>
          <div style={{ fontFamily: 'Poppins', fontSize: 14, fontWeight: 600, color: '#fff', marginTop: 2 }}>{title}</div>
        </div>
        {/* Toggle música/voz */}
        {guidVoice && (
          <div style={{
            display: 'flex', background: 'rgba(255,255,255,0.12)',
            borderRadius: 999, padding: 3,
          }}>
            <button onClick={() => setMode('music')} style={{
              padding: '6px 12px', border: 'none', borderRadius: 999, cursor: 'pointer',
              background: mode === 'music' ? '#fff' : 'transparent',
              color: mode === 'music' ? '#000' : '#fff',
              fontFamily: 'Poppins', fontSize: 11, fontWeight: 600,
            }}>🎵 Música</button>
            <button onClick={() => setMode('voice')} style={{
              padding: '6px 12px', border: 'none', borderRadius: 999, cursor: 'pointer',
              background: mode === 'voice' ? '#fff' : 'transparent',
              color: mode === 'voice' ? '#000' : '#fff',
              fontFamily: 'Poppins', fontSize: 11, fontWeight: 600,
            }}>🎙️ Solo voz</button>
          </div>
        )}
      </div>

      {/* Video */}
      <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        {!data && !error && (
          <div style={{ color: '#fff', textAlign: 'center' }}>
            <div style={{
              width: 32, height: 32, borderRadius: 50, margin: '0 auto 12px',
              border: '2.5px solid rgba(255,255,255,0.2)', borderTopColor: '#fff',
              animation: 'spin 800ms linear infinite',
            }}/>
            <div style={{ fontFamily: 'Poppins', fontSize: 13 }}>Cargando…</div>
          </div>
        )}
        {error && (
          <div style={{ color: '#fff', textAlign: 'center', padding: 20 }}>
            <div style={{ fontSize: 32, marginBottom: 8 }}>😕</div>
            <div style={{ fontFamily: 'Poppins', fontSize: 13 }}>{error}</div>
          </div>
        )}
        {data && (
          <iframe
            key={activeGuid}
            src={data.iframe}
            allow="accelerometer; gyroscope; autoplay; encrypted-media; picture-in-picture"
            allowFullScreen
            style={{ border: 0, width: '100%', height: '100%' }}
            title={data.title}
          />
        )}
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// CALENDARIO DEL DÍA — rutina del reto + comidas del plan + comidas registradas
// Muestra día actual + pasados + próximos (lista scroll)
// Datos REALES de Supabase: user_retos, day_completions, meal_logs, planes
// ═════════════════════════════════════════════════════════════
function CalendarioScreen({ retoId, theme, nav }) {
  const T = theme;
  const [data, setData] = useS3(null); // { days: [{ dayNumber, date, isToday, isPast, completed, videos, plannedMeals, loggedMeals }] }
  const [error, setError] = useS3(null);
  const [selectedIdx, setSelectedIdx] = useS3(0);
  const [view, setView] = useS3('dia'); // 'dia' | 'semana' | 'mes'

  React.useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setError('Sin conexión'); return; }
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) { setError('Inicia sesión'); return; }

        // 1) Reto activo (o el que se pasó por param)
        const cache = window.__pauCache || {};
        const activeUserReto = cache.activeReto;
        const targetRetoId = retoId || (activeUserReto && activeUserReto.reto_id);

        // Si NO hay reto pero SÍ hay plan activo, generamos calendario solo-plan
        if (!targetRetoId) {
          try {
            const { data: planEnrolls } = 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 planEnroll = planEnrolls?.[0];
            if (planEnroll?.plan_id) {
              const { data: planDays } = await sb
                .from('plan_days')
                .select('day_number, meals')
                .eq('plan_id', planEnroll.plan_id)
                .order('day_number');
              const planStart = planEnroll.enrolled_at ? new Date(planEnroll.enrolled_at) : new Date();
              const planTotalDays = (planDays || []).length || 7;
              // Comidas registradas para mostrar "ya hecha"
              let mealLogsByDay = {};
              try {
                const fromDate = new Date(planStart.getTime() - 86400000 * 2).toISOString();
                const { data: logs } = await sb.from('meal_logs')
                  .select('id, meal_name, meal_type, logged_at, photo_url')
                  .eq('user_id', user.id)
                  .gte('logged_at', fromDate)
                  .order('logged_at', { ascending: false });
                (logs || []).forEach(l => {
                  const day = Math.floor((new Date(l.logged_at) - planStart) / 86400000) + 1;
                  if (day >= 1 && day <= planTotalDays) {
                    mealLogsByDay[day] = mealLogsByDay[day] || [];
                    mealLogsByDay[day].push(l);
                  }
                });
              } catch {}
              const today = new Date();
              const todayDayNum = Math.floor((today - planStart) / 86400000) + 1;
              const days = (planDays || []).map(pd => {
                const date = new Date(planStart.getTime() + (pd.day_number - 1) * 86400000);
                return {
                  dayNumber: pd.day_number,
                  date,
                  isToday: pd.day_number === todayDayNum,
                  isPast: pd.day_number < todayDayNum,
                  isFuture: pd.day_number > todayDayNum,
                  completed: false,
                  videos: [],
                  plannedMeals: pd.meals || [],
                  loggedMeals: mealLogsByDay[pd.day_number] || [],
                };
              });
              const tIdx = days.findIndex(d => d.isToday);
              setSelectedIdx(tIdx >= 0 ? tIdx : 0);
              setData({ days, reto: null, currentDay: Math.max(1, todayDayNum), totalDays: planTotalDays, planOnly: true });
              return;
            }
          } catch (e) {
            console.warn('[calendar] plan load:', e?.message);
          }
          setData({ days: [], reto: null, message: 'No tienes ningún reto ni plan activo. Únete a uno o genera tu plan IA para ver tu calendario.' });
          return;
        }
        const reto = window.pauRetoById ? window.pauRetoById(targetRetoId) : null;

        // 2) Enrollment (para saber fecha de inicio y días completados)
        const { data: enrollment } = await sb
          .from('user_retos')
          .select('current_day, completed_days, enrolled_at')
          .eq('user_id', user.id)
          .eq('reto_id', targetRetoId)
          .order('enrolled_at', { ascending: false })
          .limit(1)
          .maybeSingle();

        const startDate = enrollment?.enrolled_at ? new Date(enrollment.enrolled_at) : new Date();
        const completedDays = enrollment?.completed_days || [];
        const currentDay = enrollment?.current_day || 1;
        const totalDays = reto?.days || 14;

        // 3) Día de completación por día (timestamps reales)
        const { data: dayCompletions } = await sb
          .from('day_completions')
          .select('day_number, completed_at')
          .eq('user_id', user.id)
          .eq('reto_id', targetRetoId);
        const completionByDay = {};
        // Solo la pasada ACTUAL: completions anteriores a la inscripción
        // vigente son de una pasada vieja → NO se pintan (bug días fantasma)
        const enrolledAtMs = enrollment?.enrolled_at ? new Date(enrollment.enrolled_at).getTime() : 0;
        (dayCompletions || []).forEach(d => {
          if (new Date(d.completed_at).getTime() >= enrolledAtMs) {
            completionByDay[d.day_number] = d.completed_at;
          }
        });

        // 3b) Días de descanso marcados por la usuaria
        const restByDay = {};
        try {
          const { data: rests } = await sb
            .from('rest_days')
            .select('day_number, rest_date, note')
            .eq('user_id', user.id)
            .eq('reto_id', targetRetoId);
          (rests || []).forEach(r => {
            if (r.day_number) restByDay[r.day_number] = r;
          });
        } catch {}

        // 4) Plan activo del usuario (para comidas planificadas) — con fotos enriquecidas
        let planMealsByDay = {};
        try {
          const activePlan = await window.PauUserRetos?.getActivePlan?.();
          if (activePlan?.plan_id) {
            const { data: planDays } = await sb.from('plan_days').select('day_number, meals').eq('plan_id', activePlan.plan_id);
            (planDays || []).forEach(d => { planMealsByDay[d.day_number] = d.meals || []; });
            // Enriquecer con image_url de cache
            const allKeys = new Set();
            Object.values(planMealsByDay).forEach(arr => {
              arr.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; });
              Object.values(planMealsByDay).forEach(arr => {
                arr.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];
                });
              });
            }
          }
        } catch {}

        // 5) Comidas REALES registradas (meal_logs)
        let mealLogsByDay = {};
        try {
          const fromDate = new Date(startDate.getTime() - 86400000 * 2).toISOString();
          const { data: logs } = await sb
            .from('meal_logs')
            .select('id, meal_name, meal_type, logged_at, photo_url')
            .eq('user_id', user.id)
            .gte('logged_at', fromDate)
            .order('logged_at', { ascending: false });
          (logs || []).forEach(l => {
            const day = Math.floor((new Date(l.logged_at) - startDate) / 86400000) + 1;
            if (day >= 1 && day <= totalDays) {
              mealLogsByDay[day] = mealLogsByDay[day] || [];
              mealLogsByDay[day].push(l);
            }
          });
        } catch {}

        // 6) Construir lista de días
        const today = new Date();
        const todayDay = Math.floor((today - startDate) / 86400000) + 1;
        const days = [];
        for (let d = 1; d <= totalDays; d++) {
          const date = new Date(startDate.getTime() + (d - 1) * 86400000);
          const isRest = !!restByDay[d];
          const realCompleted = !!completionByDay[d]; // timestamps reales de ESTA pasada
          days.push({
            dayNumber: d,
            date,
            isToday: d === todayDay,
            isPast: d < todayDay,
            isFuture: d > todayDay,
            completed: realCompleted || isRest, // rest cuenta como completado
            isRest,
            restNote: restByDay[d]?.note || null,
            completedAt: completionByDay[d] || null,
            videos: (typeof VIDEOS_BY_DAY !== 'undefined' && VIDEOS_BY_DAY[d]) ? VIDEOS_BY_DAY[d] : [],
            plannedMeals: planMealsByDay[d] || [],
            loggedMeals: mealLogsByDay[d] || [],
          });
        }

        // Por defecto seleccionar el día de hoy (o el siguiente pendiente)
        const todayIdx = days.findIndex(x => x.isToday);
        setSelectedIdx(todayIdx >= 0 ? todayIdx : 0);
        setData({ days, reto, currentDay, totalDays, retoId: targetRetoId });
      } catch (e) {
        setError(e.message || 'Error cargando calendario');
      }
    })();
  }, [retoId]);

  function formatDate(d) {
    return d.toLocaleDateString('es', { weekday: 'short', day: 'numeric', month: 'short' });
  }

  return (
    <div style={{ paddingBottom: 110 }}>
      <div style={{ padding: '12px 20px 0' }}>
        <button onClick={() => nav.back()} style={{
          width: 36, height: 36, borderRadius: 999, border: 'none',
          background: T.subtle, cursor: 'pointer', marginBottom: 14,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>{Icon.back(T.text)}</button>
        <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95 }}>
          Calendario
        </div>
        {(data?.reto || data?.planOnly) && (
          <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 8 }}>
            Día {data.currentDay}/{data.totalDays}
          </div>
        )}
      </div>

      {!data && !error && (
        <div style={{ padding: 40, textAlign: 'center', color: T.muted, fontSize: 13 }}>
          <div style={{
            width: 28, height: 28, borderRadius: 50, margin: '0 auto 12px',
            border: '2.5px solid ' + T.subtle, borderTopColor: T.accent,
            animation: 'spin 800ms linear infinite',
          }}/>
          Cargando…
        </div>
      )}

      {error && (
        <div style={{ padding: 30, textAlign: 'center', color: T.muted, fontSize: 13 }}>{error}</div>
      )}

      {data?.message && (
        <EmptyState
          emoji="📅" T={T}
          title="Aún no hay nada en tu calendario"
          description="Únete a un reto, explora rutinas o genera tu plan de comidas IA para llenar tu calendario."
          ctaLabel="Empezar"
          onCta={() => nav.tab('retos')}
        />
      )}

      {data?.days && data.days.length > 0 && (
        <>
          {/* Segmented Día / Semana / Mes */}
          <div style={{
            margin: '16px 20px 0', padding: 4,
            background: T.subtle, borderRadius: 14, display: 'flex', position: 'relative',
          }}>
            <div style={{
              position: 'absolute', top: 4, bottom: 4,
              left: view === 'dia' ? 4 : view === 'semana' ? 'calc(33.33% + 1px)' : 'calc(66.66% - 2px)',
              width: 'calc(33.33% - 3px)', background: T.card, borderRadius: 9,
              boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
              transition: 'left 240ms cubic-bezier(.2,.8,.2,1)',
            }}/>
            {[
              { id: 'dia', label: 'Día' },
              { id: 'semana', label: 'Semana' },
              { id: 'mes', label: 'Mes' },
            ].map(s => (
              <button key={s.id} onClick={() => setView(s.id)} style={{
                flex: 1, padding: '8px', border: 'none', background: 'transparent', cursor: 'pointer',
                position: 'relative', zIndex: 1,
                fontFamily: 'Poppins', fontSize: 12, fontWeight: 600,
                color: view === s.id ? T.text : T.muted, letterSpacing: -0.1,
              }}>{s.label}</button>
            ))}
          </div>

          {/* Pills horizontales — solo en vista Día */}
          {view === 'dia' && (
            <div style={{
              margin: '16px 0 0', padding: '4px 22px', overflowX: 'auto',
              display: 'flex', gap: 8,
            }} className="hide-scroll">
              {data.days.map((d, i) => {
                const sel = i === selectedIdx;
                const dotColor = d.completed ? T.accent : (d.isToday ? T.accent : T.muted);
                return (
                  <button key={d.dayNumber} onClick={() => setSelectedIdx(i)} style={{
                    flexShrink: 0, padding: '10px 14px', borderRadius: 20,
                    border: sel ? `1.5px solid ${T.accent}` : T.border,
                    background: sel ? T.accent : (d.isToday ? T.subtle : T.card),
                    color: sel ? '#fff' : T.text, cursor: 'pointer',
                    display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
                    minWidth: 64,
                    opacity: d.isFuture && !sel ? 0.7 : 1,
                  }}>
                    <span style={{ fontSize: 9, fontWeight: 700, letterSpacing: 0.5, 
                      color: sel ? 'rgba(255,255,255,0.85)' : T.muted }}>
                      Día {d.dayNumber}
                    </span>
                    <span style={{ fontSize: 14, fontWeight: 700 }}>
                      {d.date.getDate()}
                    </span>
                    <span style={{
                      width: 5, height: 5, borderRadius: 999,
                      background: sel ? '#fff' : dotColor,
                      opacity: d.completed || d.isToday ? 1 : 0.3,
                    }}/>
                  </button>
                );
              })}
            </div>
          )}

          {/* VISTA SEMANA — grid 7 días */}
          {view === 'semana' && (() => {
            // Encuentra la semana del día seleccionado o de hoy
            const todayIdx = data.days.findIndex(x => x.isToday);
            const anchorIdx = selectedIdx >= 0 ? selectedIdx : (todayIdx >= 0 ? todayIdx : 0);
            const anchorDate = data.days[anchorIdx]?.date || new Date();
            const dow = (anchorDate.getDay() + 6) % 7; // Lun=0
            const lunesDate = new Date(anchorDate); lunesDate.setDate(anchorDate.getDate() - dow);
            const weekDays = ['L', 'M', 'X', 'J', 'V', 'S', 'D'].map((letter, i) => {
              const date = new Date(lunesDate); date.setDate(lunesDate.getDate() + i);
              // Match con data.days por toLocaleDateString
              const ds = date.toLocaleDateString('en-CA');
              const matched = data.days.find(d => d.date.toLocaleDateString('en-CA') === ds);
              return { letter, date, matched };
            });
            return (
              <div style={{ margin: '20px 18px 0' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10 }}>
                  <div className="pf-eyebrow" style={{ color: T.muted }}>
                    Semana del {lunesDate.getDate()} {lunesDate.toLocaleDateString('es', { month: 'short' })}
                  </div>
                  <div style={{ fontSize: 11, fontWeight: 700, color: T.text }}>
                    {weekDays.filter(d => d.matched?.completed).length}/7 ✓
                  </div>
                </div>
                <div style={{
                  padding: '14px 8px', background: T.card, borderRadius: 20, border: T.border,
                  display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 4,
                }}>
                  {weekDays.map((d, i) => {
                    const m = d.matched;
                    const isToday = m?.isToday;
                    const isDone = m?.completed;
                    const noReto = !m;
                    return (
                      <button key={i} onClick={() => {
                        if (m) {
                          const idx = data.days.findIndex(x => x === m);
                          if (idx >= 0) { setSelectedIdx(idx); setView('dia'); }
                        }
                      }} disabled={noReto} style={{
                        padding: '10px 0 8px', cursor: noReto ? 'default' : 'pointer',
                        border: isToday ? `1.5px solid ${T.accent}` : 'none',
                        background: isDone ? T.accent : isToday ? T.subtle : 'transparent',
                        color: isDone ? '#fff' : T.text, borderRadius: 14,
                        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
                        opacity: noReto ? 0.35 : 1,
                      }}>
                        <span style={{ fontSize: 9, fontWeight: 700, letterSpacing: 0.4,
                          color: isDone ? 'rgba(255,255,255,0.85)' : T.muted }}>{d.letter}</span>
                        <span style={{ fontSize: 16, fontWeight: 700, letterSpacing: -0.3 }}>{d.date.getDate()}</span>
                        {m && (
                          <span style={{ fontSize: 8, fontWeight: 700,
                            color: isDone ? 'rgba(255,255,255,0.85)' : T.muted,
                            opacity: isDone ? 1 : 0.7,
                          }}>D{m.dayNumber}</span>
                        )}
                      </button>
                    );
                  })}
                </div>
                {/* Resumen semana */}
                <div style={{
                  marginTop: 14, padding: '14px 16px',
                  background: T.card, borderRadius: 20, border: T.border,
                }}>
                  <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 8 }}>Esta semana</div>
                  {(() => {
                    const matched = weekDays.filter(d => d.matched);
                    const completed = matched.filter(d => d.matched.completed).length;
                    const total = matched.length;
                    const pct = total > 0 ? completed / total : 0;
                    return (
                      <>
                        <div style={{ fontSize: 22, fontWeight: 800, color: T.text, letterSpacing: -0.4 }}>
                          {completed} de {total} entrenamientos
                        </div>
                        <div style={{ marginTop: 10, height: 6, background: T.subtle, borderRadius: 999, overflow: 'hidden' }}>
                          <div style={{ width: `${pct * 100}%`, height: '100%', background: T.accent, borderRadius: 999, transition: 'width 600ms ease' }}/>
                        </div>
                      </>
                    );
                  })()}
                </div>
              </div>
            );
          })()}

          {/* VISTA MES — grid completo del mes */}
          {view === 'mes' && (() => {
            const anchorIdx = selectedIdx >= 0 ? selectedIdx : 0;
            const anchorDate = data.days[anchorIdx]?.date || new Date();
            const year = anchorDate.getFullYear();
            const month = anchorDate.getMonth();
            const firstDay = new Date(year, month, 1);
            const lastDay = new Date(year, month + 1, 0);
            const startDow = (firstDay.getDay() + 6) % 7; // Lun=0
            const cells = [];
            // Padding al inicio
            for (let i = 0; i < startDow; i++) cells.push(null);
            for (let d = 1; d <= lastDay.getDate(); d++) {
              const date = new Date(year, month, d);
              const ds = date.toLocaleDateString('en-CA');
              const matched = data.days.find(x => x.date.toLocaleDateString('en-CA') === ds);
              cells.push({ date, matched });
            }
            const meses = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre'];
            const completedThisMonth = cells.filter(c => c?.matched?.completed).length;
            const plannedThisMonth = cells.filter(c => c?.matched).length;
            return (
              <div style={{ margin: '20px 18px 0' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10 }}>
                  <div className="pf-eyebrow" style={{ color: T.muted, textTransform: 'capitalize' }}>
                    {meses[month]} {year}
                  </div>
                  <div style={{ fontSize: 11, fontWeight: 700, color: T.text }}>
                    {completedThisMonth} / {plannedThisMonth} ✓
                  </div>
                </div>
                {/* Header dias */}
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 2, marginBottom: 6 }}>
                  {['L', 'M', 'X', 'J', 'V', 'S', 'D'].map((l, i) => (
                    <div key={i} style={{
                      textAlign: 'center', fontSize: 9, fontWeight: 700,
                      color: T.muted, letterSpacing: 0.5,
                    }}>{l}</div>
                  ))}
                </div>
                <div style={{
                  padding: 8, background: T.card, borderRadius: 20, border: T.border,
                  display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 4,
                }}>
                  {cells.map((c, i) => {
                    if (!c) return <div key={i}/>;
                    const m = c.matched;
                    const isToday = m?.isToday;
                    const isDone = m?.completed;
                    const noReto = !m;
                    return (
                      <button key={i} onClick={() => {
                        if (m) {
                          const idx = data.days.findIndex(x => x === m);
                          if (idx >= 0) { setSelectedIdx(idx); setView('dia'); }
                        }
                      }} disabled={noReto} style={{
                        aspectRatio: '1 / 1', padding: 0,
                        cursor: noReto ? 'default' : 'pointer',
                        border: isToday ? `1.5px solid ${T.accent}` : 'none',
                        background: isDone ? T.accent : isToday ? T.subtle : 'transparent',
                        color: isDone ? '#fff' : T.text, borderRadius: 8,
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontSize: 12, fontWeight: 700,
                        opacity: noReto ? 0.25 : 1,
                        position: 'relative',
                      }}>
                        {c.date.getDate()}
                        {isDone && (
                          <span style={{
                            position: 'absolute', bottom: 3, right: 3,
                            fontSize: 7, color: '#fff', opacity: 0.7,
                          }}>✓</span>
                        )}
                      </button>
                    );
                  })}
                </div>
                {/* Leyenda */}
                <div style={{
                  marginTop: 14, display: 'flex', gap: 16, padding: '0 4px',
                  fontSize: 11, color: T.muted,
                }}>
                  <span style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
                    <span style={{ width: 10, height: 10, borderRadius: 4, background: T.accent }}/>
                    Completado
                  </span>
                  <span style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
                    <span style={{ width: 10, height: 10, borderRadius: 4, border: `1.5px solid ${T.accent}` }}/>
                    Hoy
                  </span>
                </div>
              </div>
            );
          })()}

          {/* Detalle del día seleccionado — solo en vista Día */}
          {view === 'dia' && (() => {
            const day = data.days[selectedIdx];
            if (!day) return null;
            return (
              <div style={{ padding: '20px 22px 0' }}>
                <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
                  <div>
                    <div className="pf-eyebrow" style={{ color: T.muted }}>
                      {day.isToday ? 'Hoy' : day.isPast ? 'Pasado' : 'Próximo'}
                    </div>
                    <div className="pf-display" style={{ fontSize: 22, fontWeight: 600, color: T.text, letterSpacing: -0.4, marginTop: 2 }}>
                      {formatDate(day.date)}
                    </div>
                  </div>
                  {day.completed && (
                    <span style={{
                      padding: '5px 10px', borderRadius: 999,
                      background: day.isRest ? '#9AB39430' : T.accent + '20',
                      color: day.isRest ? '#5C7755' : T.accent,
                      fontSize: 11, fontWeight: 700, letterSpacing: 0.4,
                    }}>{day.isRest ? '🌴 Descanso' : '✓ Completado'}</span>
                  )}
                </div>

                {/* DÍA DE DESCANSO — botón para marcar/desmarcar */}
                {!day.isFuture && !day.completed && data.retoId && (
                  <button onClick={async () => {
                    try {
                      const { data: { user } } = await sb.auth.getUser();
                      if (!user) return;
                      const note = window.prompt('Día de descanso (opcional, nota):', '');
                      if (note === null) return; // cancel
                      const restDate = day.date.toISOString().slice(0, 10);
                      const { error } = await sb.from('rest_days').insert({
                        user_id: user.id,
                        reto_id: data.retoId,
                        day_number: day.dayNumber,
                        rest_date: restDate,
                        note: note || null,
                      });
                      if (error) {
                        nav.toast('Error: ' + error.message, { icon: '⚠️' });
                        return;
                      }
                      nav.toast('Día de descanso registrado 🌴', { icon: '✓' });
                      // Recargar
                      window.dispatchEvent(new CustomEvent('pau:cache-updated'));
                      setData(null); // forzar re-fetch del calendario
                      setTimeout(() => setSelectedIdx(selectedIdx), 100);
                    } catch (e) {
                      nav.toast('Error: ' + e.message, { icon: '⚠️' });
                    }
                  }} style={{
                    marginTop: 12, padding: '10px 14px', borderRadius: 14,
                    border: T.border, background: T.card, color: T.text,
                    cursor: 'pointer', fontSize: 12, fontWeight: 600,
                    display: 'flex', alignItems: 'center', gap: 8, fontFamily: 'inherit',
                  }}>
                    <span style={{ fontSize: 16 }}>🌴</span>
                    <span>Marcar como día de descanso</span>
                  </button>
                )}
                {day.isRest && day.restNote && (
                  <div style={{
                    marginTop: 10, padding: '10px 14px', borderRadius: 14,
                    background: '#9AB39415', color: T.text, fontSize: 12, lineHeight: 1.4,
                  }}>
                    🌴 {day.restNote}
                  </div>
                )}

                {/* RUTINA del día */}
                <div style={{ marginTop: 22 }}>
                  <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 10 }}>Rutina</div>
                  {day.videos.length === 0 ? (
                    <div style={{
                      padding: '20px', background: T.card, borderRadius: 20, border: T.border,
                      fontSize: 12, color: T.muted, textAlign: 'center',
                    }}>Sin rutina asignada para este día</div>
                  ) : (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                      {day.videos.map((v, i) => (
                        <div key={i} onClick={() => nav.go('reto', retoId || data.reto?.id)} style={{
                          padding: '14px 16px', background: T.card, borderRadius: 20, border: T.border,
                          display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer',
                        }}>
                          <div style={{
                            width: 42, height: 42, borderRadius: 14, flexShrink: 0,
                            background: `linear-gradient(135deg, ${T.accent}, ${T.deep})`,
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                            color: '#fff', fontSize: 18,
                          }}>▶</div>
                          <div style={{ flex: 1, minWidth: 0 }}>
                            <div style={{ fontSize: 13, fontWeight: 600, color: T.text, letterSpacing: -0.2 }}>{v.title}</div>
                            <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>{v.minutes} min · {v.muscle || 'Cuerpo completo'}</div>
                          </div>
                        </div>
                      ))}
                    </div>
                  )}
                </div>

                {/* COMIDAS REGISTRADAS por el usuario hoy */}
                <div style={{ marginTop: 22 }}>
                  <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10 }}>
                    <div className="pf-eyebrow" style={{ color: T.muted }}>Comidas registradas</div>
                    {day.isToday && (
                      <button onClick={() => {
                        nav.openModal && nav.openModal(React.createElement(window.AddMealModal, {
                          open: true, T, accent: T.accent,
                          onClose: () => nav.closeModal(),
                          onSave: () => { nav.closeModal(); nav.toast('Comida añadida 💗', { icon: '🍓' }); },
                        }));
                      }} style={{
                        padding: '4px 10px', borderRadius: 999, border: 'none', cursor: 'pointer',
                        background: T.accent, color: '#fff', fontSize: 11, fontWeight: 700,
                      }}>+ Añadir</button>
                    )}
                  </div>
                  {day.loggedMeals.length === 0 ? (
                    <div style={{
                      padding: '16px', background: T.card, borderRadius: 20, border: T.border,
                      fontSize: 12, color: T.muted, textAlign: 'center',
                    }}>
                      {day.isPast ? 'No registraste comidas este día' : day.isFuture ? 'Aún no toca' : 'Aún no has registrado comidas hoy'}
                    </div>
                  ) : (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                      {day.loggedMeals.map(m => (
                        <div key={m.id} style={{
                          padding: '12px 14px', background: T.card, borderRadius: 20, border: T.border,
                          display: 'flex', alignItems: 'center', gap: 12,
                        }}>
                          <div style={{
                            width: 36, height: 36, borderRadius: 10, flexShrink: 0,
                            background: T.subtle, fontSize: 18,
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                          }}>{m.meal_type === 'desayuno' ? '🌅' : m.meal_type === 'almuerzo' ? '🥗' : m.meal_type === 'cena' ? '🌙' : '🍓'}</div>
                          <div style={{ flex: 1 }}>
                            <div style={{ fontSize: 12, fontWeight: 600, color: T.text }}>{m.meal_name}</div>
                            <div style={{ fontSize: 10, color: T.muted, marginTop: 2 }}>
                              {new Date(m.logged_at).toLocaleTimeString('es', { hour: '2-digit', minute: '2-digit' })}
                              {m.meal_type && ` · ${m.meal_type}`}
                            </div>
                          </div>
                          <button onClick={async () => {
                            if (!window.confirm('¿Borrar esta comida del registro?')) return;
                            try {
                              const sb = window.__PAU_SUPABASE;
                              await sb.from('meal_logs').delete().eq('id', m.id);
                              nav.toast('Borrada', { icon: '🗑️' });
                              window.dispatchEvent(new CustomEvent('pau:cache-updated'));
                            } catch (err) {
                              nav.toast('Error: ' + (err?.message || err), { icon: '⚠️' });
                            }
                          }} aria-label="Borrar comida" style={{
                            width: 30, height: 30, borderRadius: 999,
                            border: 'none', background: 'transparent', cursor: 'pointer',
                            fontSize: 14, color: T.muted, flexShrink: 0,
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                          }}>×</button>
                        </div>
                      ))}
                    </div>
                  )}
                </div>

                {/* COMIDAS DE HOY del plan — clickeable + quick-register */}
                {day.plannedMeals.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 }}>
                        {day.isToday ? 'Comidas de hoy de tu plan' : day.isPast ? 'Comidas que tocaban' : 'Próximas comidas del plan'}
                      </div>
                      <span style={{ fontSize: 10, color: T.muted, fontWeight: 600 }}>{day.plannedMeals.length}</span>
                    </div>
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                      {day.plannedMeals.map((m, i) => {
                        const name = m.name || m.title || 'Comida';
                        // ¿ya registrada hoy? (match por nombre exacto)
                        const alreadyLogged = (day.loggedMeals || []).some(l =>
                          (l.meal_name || '').toLowerCase().trim() === name.toLowerCase().trim()
                        );
                        function quickRegister(e) {
                          e.stopPropagation();
                          // Abrir AddMealModal con datos PRE-LLENADOS de esta comida del plan
                          // Permite editar hora, foto, descripción antes de guardar.
                          const mt = (m.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';
                          if (nav.openModal && window.AddMealModal) {
                            nav.openModal(React.createElement(window.AddMealModal, {
                              open: true, T, accent: T.accent,
                              prefill: {
                                name: name,
                                mealType: mealKind,
                                kcal: m.kcal,
                                protein_g: m.protein_g,
                                carbs_g: m.carbs_g,
                                fat_g: m.fat_g,
                                imageUrl: m.image_url || null,
                                fromPlan: true,
                              },
                              onClose: () => nav.closeModal(),
                              onSave: () => {
                                nav.closeModal();
                                try { window.PauUserRetos?.awardPoints?.(5, 'meal'); } catch {}
                                nav.toast('¡Registrada! +5 puntos 💗', { icon: '✓' });
                                window.dispatchEvent(new CustomEvent('pau:cache-updated'));
                              },
                            }));
                          }
                        }
                        return (
                          <div key={i} style={{
                            padding: '12px 14px', background: T.card, borderRadius: 20, border: T.border,
                            display: 'flex', alignItems: 'center', gap: 12,
                            opacity: alreadyLogged ? 0.6 : 1,
                          }}>
                            <button onClick={() => {
                              if (nav?.openModal && window.MealRecipeModal) {
                                nav.openModal(React.createElement(window.MealRecipeModal, {
                                  T, meal: m, nav,
                                  onClose: () => nav.closeModal(),
                                }));
                              }
                            }} style={{
                              flex: 1, display: 'flex', alignItems: 'center', gap: 12,
                              border: 'none', background: 'transparent', padding: 0,
                              cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit',
                            }}>
                              {m.image_url ? (
                                <div style={{
                                  width: 44, height: 44, borderRadius: 10, flexShrink: 0,
                                  overflow: 'hidden', background: T.subtle,
                                }}>
                                  <img src={m.image_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
                                </div>
                              ) : (
                                <span style={{ fontSize: 22 }}>{m.emoji || '🍓'}</span>
                              )}
                              <div style={{ flex: 1, minWidth: 0 }}>
                                <div style={{
                                  fontSize: 12.5, fontWeight: 700, color: T.text, lineHeight: 1.25,
                                  display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
                                  overflow: 'hidden',
                                  textDecoration: alreadyLogged ? 'line-through' : 'none',
                                }}>{name}</div>
                                <div style={{ fontSize: 10, color: T.muted, marginTop: 3 }}>
                                  {m.mealType || ''}{m.kcal ? ` · ${m.kcal} kcal` : ''}
                                </div>
                              </div>
                            </button>
                            {/* Quick register button */}
                            {!alreadyLogged && day.isToday ? (
                              <button onClick={quickRegister} aria-label="Marcar como hecha" style={{
                                flexShrink: 0, padding: '7px 11px', borderRadius: 999,
                                border: `1.5px solid ${T.accent}`, background: T.card, color: T.accent,
                                cursor: 'pointer', fontFamily: 'inherit',
                                fontSize: 11, fontWeight: 800,
                              }}>✓ Hecha</button>
                            ) : alreadyLogged ? (
                              <span style={{
                                flexShrink: 0, padding: '7px 11px', borderRadius: 999,
                                background: T.accent, color: '#fff',
                                fontSize: 10, fontWeight: 800, letterSpacing: 0.3,
                              }}>HECHA</span>
                            ) : null}
                          </div>
                        );
                      })}
                    </div>
                    {/* Quick action: añadir otra comida fuera del plan */}
                    {day.isToday && (
                      <button onClick={() => {
                        nav.openModal && nav.openModal(React.createElement(window.AddMealModal, {
                          open: true, T, accent: T.accent,
                          onClose: () => nav.closeModal(),
                          onSave: () => { nav.closeModal(); nav.toast('Comida añadida 💗', { icon: '🍓' }); },
                        }));
                      }} style={{
                        marginTop: 10, width: '100%', padding: '11px',
                        border: T.border, borderRadius: 14, cursor: 'pointer',
                        background: T.subtle, color: T.text,
                        fontFamily: 'inherit', fontSize: 12, fontWeight: 700,
                      }}>+ Añadir otra comida</button>
                    )}
                  </div>
                )}
              </div>
            );
          })()}
        </>
      )}
    </div>
  );
}

function TiendaScreen({ theme, nav }) {
  const T = theme;
  React.useEffect(() => {
    // Abre la tienda externa (Shopify de Pau) y vuelve atrás
    const url = 'https://paufit.co';
    if (window.PauNative && window.PauNative.isNativeApp) {
      window.PauNative.openExternal(url);
    } else if (window.top && window.top !== window) {
      window.top.open(url, '_blank', 'noopener');
    } else {
      window.open(url, '_blank', 'noopener');
    }
    setTimeout(() => nav.back(), 300);
  }, []);
  return (
    <div style={{ padding: 22, paddingBottom: 110 }}>
      <div style={{ fontSize: 24, fontWeight: 700, color: T.text, letterSpacing: -0.6 }}>Tienda</div>
      <div style={{ marginTop: 12, fontSize: 13, color: T.muted }}>Abriendo tienda…</div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// LISTA DE COMPRA — estilo Silbe (items planos con foto + sheet duración)
// ═════════════════════════════════════════════════════════════

// Mapping ingrediente → emoji para el círculo de foto
const INGREDIENT_EMOJI = {
  pollo: '🍗', pavo: '🦃', carne: '🥩', ternera: '🥩', cerdo: '🥓', jamon: '🥓', jamón: '🥓',
  pescado: '🐟', salmon: '🐟', salmón: '🐟', atun: '🐟', atún: '🐟', merluza: '🐟', lubina: '🐟', sardina: '🐟', sardinas: '🐟',
  gambas: '🦐', camaron: '🦐', camarón: '🦐',
  huevo: '🥚', huevos: '🥚',
  queso: '🧀', yogur: '🥛', leche: '🥛',
  espinaca: '🥬', espinacas: '🥬', lechuga: '🥬', kale: '🥬', rúcula: '🥬', rucula: '🥬',
  brocoli: '🥦', brócoli: '🥦', coliflor: '🥦',
  tomate: '🍅', pepino: '🥒', zucchini: '🥒', calabacin: '🥒', calabacín: '🥒',
  cebolla: '🧅', ajo: '🧄', pimiento: '🫑', berenjena: '🍆',
  zanahoria: '🥕', patata: '🥔', papa: '🥔', batata: '🍠', camote: '🍠',
  champinon: '🍄', champiñon: '🍄', champiñón: '🍄', seta: '🍄',
  espárragos: '🌿', esparragos: '🌿',
  banana: '🍌', banano: '🍌', plátano: '🍌', platano: '🍌', macho: '🍌',
  manzana: '🍎', pera: '🍐', naranja: '🍊', mandarina: '🍊', limon: '🍋', limón: '🍋',
  fresa: '🍓', fresas: '🍓', frambuesa: '🫐', arandano: '🫐', arándano: '🫐',
  mango: '🥭', piña: '🍍', papaya: '🥭', sandia: '🍉', sandía: '🍉', melon: '🍈', melón: '🍈',
  aguacate: '🥑', dátil: '🌴', dátiles: '🌴', datil: '🌴', datiles: '🌴',
  aceite: '🫒', aove: '🫒', oliva: '🫒',
  almendra: '🌰', almendras: '🌰', nueces: '🌰', nuez: '🌰', cacahuete: '🥜', cacahuetes: '🥜', maní: '🥜', mani: '🥜',
  avena: '🌾', lenteja: '🫘', lentejas: '🫘', garbanzo: '🫘', garbanzos: '🫘', alubia: '🫘', alubias: '🫘',
  miel: '🍯', canela: '🍂', tahin: '🌰', tahín: '🌰', chia: '🌱', chía: '🌱',
  agua: '💧',
};

function emojiForIngredient(name) {
  const n = name.toLowerCase().trim();
  // Match palabra a palabra
  for (const word of n.split(/\s+/)) {
    const clean = word.replace(/[^a-záéíóúñü]/gi, '');
    if (INGREDIENT_EMOJI[clean]) return INGREDIENT_EMOJI[clean];
  }
  // Fallback por inclusión
  for (const key of Object.keys(INGREDIENT_EMOJI)) {
    if (n.includes(key)) return INGREDIENT_EMOJI[key];
  }
  return '🥗';
}

// Secciones del súper — para que la lista se lea como se compra.
// Orden = orden de pasillos típico. La primera categoría que matchee gana.
const SHOP_CATEGORIES = [
  { id: 'fruta', title: 'Fruta y verdura', emoji: '🥦', words: ['espinaca','lechuga','kale','rucula','rúcula','brocoli','brócoli','coliflor','tomate','pepino','zucchini','calabacin','calabacín','calabaza','cebolla','ajo','pimiento','berenjena','zanahoria','patata','papa','batata','camote','champinon','champiñon','champiñón','seta','esparrago','espárrago','apio','puerro','jengibre','cilantro','perejil','albahaca','menta','banana','plátano','platano','manzana','pera','naranja','mandarina','limon','limón','lima','fresa','frambuesa','arandano','arándano','mora','mango','piña','papaya','sandia','sandía','melon','melón','aguacate','datil','dátil','uva','kiwi','coco','melocoton','melocotón','granada','verdura','fruta'] },
  { id: 'prote', title: 'Carne y pescado', emoji: '🍗', words: ['pollo','pavo','carne','ternera','cerdo','jamon','jamón','pescado','salmon','salmón','atun','atún','merluza','lubina','sardina','gamba','camaron','camarón','marisco','tofu','seitan','seitán'] },
  { id: 'lacteo', title: 'Lácteos y huevos', emoji: '🥛', words: ['huevo','queso','yogur','leche','kefir','kéfir','requeson','requesón','mozzarella','feta','parmesano','mantequilla','nata'] },
  { id: 'despensa', title: 'Despensa', emoji: '🌾', words: ['avena','arroz','pasta','quinoa','quinua','cuscus','cuscús','pan','tortita','tortilla','lenteja','garbanzo','alubia','frijol','judia','judía','harina','aceite','aove','oliva','miel','canela','cacao','chocolate','tahin','tahín','chia','chía','semilla','almendra','nuez','nueces','cacahuete','maní','mani','anacardo','pistacho','crema','levadura','sal','pimienta','oregano','orégano','comino','pimenton','pimentón','cúrcuma','curcuma','curry','vinagre','soja','mostaza','caldo','proteina','proteína','whey','mermelada','sirope','stevia','edulcorante','vainilla'] },
];

function categoryForIngredient(name) {
  const n = String(name || '').toLowerCase();
  const words = n.split(/\s+/).map(w => w.replace(/[^a-záéíóúñü]/gi, ''));
  for (const cat of SHOP_CATEGORIES) {
    if (words.some(w => cat.words.includes(w))) return cat;
    if (cat.words.some(k => k.length > 3 && n.includes(k))) return cat;
  }
  return { id: 'otros', title: 'Otros', emoji: '🛒' };
}

function ListaCompraScreen({ theme, nav }) {
  const T = theme;
  const [checked, setChecked] = useS3(new Set(JSON.parse(localStorage.getItem('paufit-shopping-checked') || '[]')));
  const [planDays, setPlanDays] = useS3(null);
  const [planTitle, setPlanTitle] = useS3('');
  const [planStart, setPlanStart] = useS3(null); // fecha enrollment
  const [selectedDays, setSelectedDays] = useS3(new Set([1, 2, 3, 4, 5, 6, 7]));
  const [showDurationSheet, setShowDurationSheet] = useS3(false);
  const [durationLabel, setDurationLabel] = useS3('Esta semana');

  // Cargar plan activo + sus días
  useE3(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) return;
    (async () => {
      try {
        const { data: { user } } = await sb.auth.getUser();
        if (!user) 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 enroll = enrollments?.[0];
        if (!enroll?.plan_id) return;
        setPlanStart(enroll.enrolled_at ? new Date(enroll.enrolled_at) : new Date());
        const [{ data: planRow }, { data: days }] = await Promise.all([
          sb.from('planes').select('id, title').eq('id', enroll.plan_id).maybeSingle(),
          sb.from('plan_days').select('day_number, meals').eq('plan_id', enroll.plan_id).order('day_number', { ascending: true }),
        ]);
        setPlanTitle(planRow?.title || 'Mi plan');
        setPlanDays(days || []);
        setSelectedDays(new Set((days || []).map(d => d.day_number)));
      } catch (e) {
        console.warn('[lista-compra] load:', e?.message);
      }
    })();
  }, []);

  // Helper: extraer cantidad + unidad + nombre normalizado.
  // Quita SOLO adjetivos cosméticos (maduro, fresco, campero, orgánico)
  // pero RESPETA variedades reales que se compran distinto:
  //   "tomate cherry" ≠ "tomate" (son productos diferentes en el super)
  //   "pechuga pollo" ≠ "muslo pollo" (cortes diferentes)
  //   "queso feta" ≠ "queso fresco" (quesos diferentes)
  //   "atún en agua" ≠ "atún en aceite" (productos diferentes)
  function normalizeIngredientName(raw) {
    let n = String(raw || '').toLowerCase().trim();
    // Quitar "de " entre palabras (pechuga de pollo → pechuga pollo)
    n = n.replace(/\bde\s+/g, '');

    // ✅ Adjetivos COSMÉTICOS que SÍ se quitan (no cambian el producto en el super):
    const cosmeticAdjectives = [
      // Estado de maduración / frescura → da igual al comprar
      'maduro', 'madura', 'maduros', 'maduras',
      'fresco', 'fresca', 'frescos', 'frescas',
      'natural', 'naturales',
      // Procedencia / certificaciones → da igual al comprar
      'organico', 'orgánico', 'organica', 'orgánica',
      'campero', 'campera', 'camperos', 'camperas',
      'ecologico', 'ecológico', 'ecologica', 'ecológica',
      // Tamaño → da igual
      'pequeño', 'pequeña', 'pequeños', 'pequeñas',
      'mediano', 'mediana', 'medianos', 'medianas',
      'grande', 'grandes',
      // Estado cocción si se compra crudo
      'crudo', 'cruda', 'crudos', 'crudas',
      // Color cuando no diferencia variedad real
      'amarillo', 'amarilla',
      // Otros cosméticos
      'sin azucar', 'sin azúcar',
      'al gusto', 'al natural',
      'entero', 'entera', 'enteros', 'enteras',
      'deshuesado', 'deshuesada', // pollo deshuesado vs pollo da igual al comprar
      'molido', 'molida',
      // PREPARACIÓN — es lo que haces en casa, no lo que compras.
      // "cebolla picada" y "cebolla en rodajas" son LA MISMA cebolla:
      'picado', 'picada', 'picados', 'picadas',
      'troceado', 'troceada', 'troceados', 'troceadas',
      'cortado', 'cortada', 'cortados', 'cortadas',
      'rallado', 'rallada', 'rallados', 'ralladas',
      'laminado', 'laminada',
      'pelado', 'pelada', 'pelados', 'peladas',
      'escurrido', 'escurrida',
      'lavado', 'lavada',
      'en rodajas', 'en cubos', 'en dados', 'en juliana', 'en tiras', 'en trozos',
      'sin piel', 'sin hueso', 'sin semillas', 'sin pepitas',
      'finamente', 'muy fino', 'muy fina',
    ];
    cosmeticAdjectives.forEach(adj => {
      n = n.replace(new RegExp('\\b' + adj.replace(/\s+/g, '\\s+') + '\\b', 'g'), '');
    });

    // 🚫 NO tocar: cherry, perita, raf, rama (tomate); pechuga, muslo, filete (pollo);
    // feta, fresco, parmesano (queso); en agua, en aceite (atún); cocido, ahumado (jamón);
    // rojo, verde (pimiento — sí cambia); virgen, extra (aceite — sí cambia).

    // Unificaciones MUY específicas (solo cuando es claramente el mismo producto):
    n = n.replace(/\baceite\s+oliva(\s+virgen(\s+extra)?)?\b/g, 'aove');
    n = n.replace(/\baoves?(\s+virgen(\s+extra)?)?\b/g, 'aove');
    n = n.replace(/\baceite\s+virgen\s+extra\b/g, 'aove');
    n = n.replace(/\baove\s+virgen(\s+extra)?\b/g, 'aove');
    n = n.replace(/\bjamon\b/g, 'jamón');
    // FRUTA dulce (la amarilla común) → siempre "banana".
    //   En LATAM "banano" se dice mucho; estandarizamos a "banana".
    //   "plátano" sin más se queda como plátano (España = banana, LATAM = macho).
    //   Si la receta dice "plátano macho" o "plátano verde" → se mantiene tal cual.
    n = n.replace(/\bbananos?\b/g, 'banana');
    // Plátano normal (sin "macho/verde") en España = banana
    // Pero como las recetas son globales, lo dejamos como "plátano" si está solo.
    n = n.replace(/\bplatano\b/g, 'plátano');
    n = n.replace(/\bplatanos\b/g, 'plátanos');
    n = n.replace(/\bzumo\s+limon\b/g, 'limón');
    n = n.replace(/\bzumo\s+limón\b/g, 'limón');
    n = n.replace(/\bcrema\s+cacahuete\b/g, 'crema de maní');
    n = n.replace(/\bcrema\s+maní\b/g, 'crema de maní');
    n = n.replace(/\bcrema\s+mani\b/g, 'crema de maní');

    // Trim
    n = n.replace(/\s+/g, ' ').trim();
    // Singular básico (quitar -s final si la palabra es larga, salvo plural intrínseco)
    if (n.length > 4 && n.endsWith('s') && !/[aeiou]s$/.test(n.slice(-2))) {
      n = n.slice(0, -1);
    }
    return n;
  }

  function parseIngredient(ing) {
    let trimmed = String(ing || '').trim();
    // Normalizar FRACCIONES antes de parsear — así "½ cebolla", "1/2 cebolla"
    // y "media cebolla" suman todas como 0.5 de la MISMA cebolla:
    trimmed = trimmed
      .replace(/^½\s*/, '0.5 ').replace(/^¼\s*/, '0.25 ').replace(/^¾\s*/, '0.75 ')
      .replace(/^1\s*y\s*(?:media|1\/2|½)\s+/i, '1.5 ')
      .replace(/^1\/2\s*/, '0.5 ').replace(/^1\/4\s*/, '0.25 ').replace(/^3\/4\s*/, '0.75 ')
      .replace(/^(?:media|medio)\s+/i, '0.5 ')
      .replace(/^(?:un\s+cuarto\s+de|un\s+cuarto)\s+/i, '0.25 ')
      .replace(/^(?:un|una)\s+/i, '1 ');
    // Quitar acotaciones entre paréntesis: "pollo (cocido)" → "pollo"
    trimmed = trimmed.replace(/\s*\([^)]*\)/g, '').trim();
    // Medidas caseras → cuentan como unidades y NO forman parte del nombre:
    // "1 puñado almendras" → 1 u de "almendras"; "2 dientes de ajo" → 2 u de "ajo"
    trimmed = trimmed.replace(/^(\d+(?:[.,]\d+)?)\s*(?:tazas?|vasos?|puñados?|pizcas?|dientes?|rebanadas?|lonchas?|latas?|scoops?|rodajas?|hojas?|ramitas?|filetes?|sobres?)\s+(?:de\s+)?/i, '$1 ');
    const m = trimmed.match(/^(\d+(?:[.,]\d+)?)\s*(kg|g|gr|gramos|ml|l|cda|cdta|cucharadas?|cucharaditas?|u|unidades?|piezas?)?\s+(.+)$/i);
    if (!m) {
      return { qty: 0, unit: '', name: normalizeIngredientName(trimmed), original: trimmed };
    }
    const qty = parseFloat(m[1].replace(',', '.'));
    let unit = (m[2] || 'u').toLowerCase();
    if (unit === 'gr' || unit === 'gramos') unit = 'g';
    if (unit === 'kg') unit = 'kg';
    if (unit.startsWith('cucharada')) unit = 'cda';
    if (unit.startsWith('cucharadita')) unit = 'cdta';
    if (unit.startsWith('unidad') || unit.startsWith('pieza')) unit = 'u';
    let rest = m[3] || '';
    let approxG = null;
    const aprox = rest.match(/\(\s*[≈~]?\s*(\d+)\s*(g|gr|ml)\s*\)/i);
    if (aprox) {
      approxG = parseInt(aprox[1], 10);
      rest = rest.replace(aprox[0], '').trim();
    }
    return { qty, unit, name: normalizeIngredientName(rest), original: trimmed, approxG };
  }

  // Re-render cuando cambian ajustes de ingredientes en alguna receta
  const [tweakTick, setTweakTick] = useS3(0);
  React.useEffect(() => {
    const h = () => setTweakTick(t => t + 1);
    window.addEventListener('pau:ing-tweaks-updated', h);
    return () => window.removeEventListener('pau:ing-tweaks-updated', h);
  }, []);

  // Clave canónica agresiva — colapsa "Tomate", "tomates", "tomate ", "tomáte"
  // a un solo nameKey. Esto arregla duplicados que aparecían antes.
  function canonicalKey(name) {
    // Singularizar PALABRA a palabra ("frutos rojos"→"fruto rojo",
    // "huevos camperos"→"huevo campero") y luego compactar. Así
    // "2 huevos" y "1 huevo" caen SIEMPRE en la misma línea.
    return String(name || '')
      .toLowerCase()
      .normalize('NFD').replace(/[̀-ͯ]/g, '')
      .split(/[^a-z0-9]+/)
      .filter(Boolean)
      .map(w => {
        if (w.length > 4 && w.endsWith('es') && !/[aeiou]es$/.test(w)) return w.slice(0, -2); // limones→limon
        if (w.length > 3 && w.endsWith('s')) return w.slice(0, -1);   // huevos→huevo, copos→copo
        return w;
      })
      .join('')
      .slice(0, 40);
  }

  // Lista plana estilo Silbe: cada item = { name, qty: "140 g" }
  // Aplica ajustes por ingrediente:
  //  - "ya lo tengo" → se omite del cálculo
  //  - "override"    → se usa el texto sustituto en lugar del original
  const items = React.useMemo(() => {
    if (!planDays) return [];
    const accum = {}; // { canonKey: { displayName, totalG, totalU, ...} }
    planDays.forEach(d => {
      if (!selectedDays.has(d.day_number)) return;
      const meals = Array.isArray(d.meals) ? d.meals : [];
      meals.forEach(m => {
        const recipeTweaks = window.PauIngredientTweaks ? window.PauIngredientTweaks.read(m.name) : {};
        (m.ingredients || []).forEach((rawIng, idx) => {
          const tw = recipeTweaks[idx] || {};
          if (tw.have) return; // 🔕 marcado como "ya lo tengo"
          const ingFull = String(tw.override || rawIng || '');
          // "Sal, pimienta" o "AOVE, ajo, jengibre" o "mix anacardos + almendras"
          // son VARIOS ingredientes en una línea → separarlos.
          const partes = ingFull
            .replace(/\bmix\s+(de\s+)?/gi, '')
            .split(/\s*[,+]\s*|\s+y\s+(?=[a-záéíóúñ])/i)
            .map(x => x.trim()).filter(x => x.length > 1);
          partes.forEach((ing) => {
          const p = parseIngredient(ing);
          if (!p.name || p.name.length < 2) return;
          const nice = p.name.charAt(0).toUpperCase() + p.name.slice(1);
          // Clave canónica fuerte para evitar duplicados
          const ck = canonicalKey(p.name);
          if (!ck) return;
          if (!accum[ck]) {
            accum[ck] = {
              totalG: 0, totalKg: 0, totalU: 0, totalMl: 0, totalL: 0, totalCda: 0, totalCdta: 0, totalApproxG: 0,
              displayName: nice,
            };
          }
          const e = accum[ck];
          if (p.unit === 'g') e.totalG += p.qty;
          else if (p.unit === 'kg') e.totalKg += p.qty;
          else if (p.unit === 'ml') e.totalMl += p.qty;
          else if (p.unit === 'l') e.totalL += p.qty;
          else if (p.unit === 'cda') e.totalCda += p.qty;
          else if (p.unit === 'cdta') e.totalCdta += p.qty;
          else { e.totalU += p.qty; if (p.approxG) e.totalApproxG += p.approxG; }
          });
        });
      });
    });
    return Object.entries(accum).map(([key, e]) => {
      const parts = [];
      // Si totalG > 1000, convertimos a kg para que se lea mejor
      let totalG = e.totalG;
      let totalKg = e.totalKg;
      if (totalG >= 1000) {
        totalKg += Math.floor(totalG / 1000);
        totalG = totalG % 1000;
      }
      if (totalKg) parts.push(`${Number.isInteger(totalKg) ? totalKg : totalKg.toFixed(1)} kg`);
      if (totalG) parts.push(`${Math.round(totalG)} g`);
      if (e.totalL) parts.push(`${e.totalL} L`);
      if (e.totalMl) parts.push(`${e.totalMl} ml`);
      if (e.totalU) {
        const num = Math.round(e.totalU);
        parts.push(`${num} ud${num > 1 ? 's' : ''}${e.totalApproxG ? ` (≈${e.totalApproxG} g)` : ''}`);
      }
      if (e.totalCda) parts.push(`${e.totalCda} cda`);
      if (e.totalCdta) parts.push(`${e.totalCdta} cdta`);
      return {
        key,
        name: e.displayName,
        qty: parts.join(' · '),
        emoji: emojiForIngredient(e.displayName),
        cat: categoryForIngredient(e.displayName),
      };
    }).sort((a, b) => a.name.localeCompare(b.name, 'es'));
  // tweakTick fuerza recálculo cuando el usuario marca "ya lo tengo" o edita un ingrediente
  }, [planDays, selectedDays, tweakTick]);

  // Agrupar por sección del súper (en el orden de SHOP_CATEGORIES + Otros al final)
  const grouped = React.useMemo(() => {
    const order = [...SHOP_CATEGORIES.map(c => c.id), 'otros'];
    const map = {};
    items.forEach(it => {
      const id = it.cat.id;
      if (!map[id]) map[id] = { ...it.cat, items: [] };
      map[id].items.push(it);
    });
    return order.map(id => map[id]).filter(Boolean);
  }, [items]);

  const hasPlan = planDays && planDays.length > 0;

  // Fechas — formato "01 Jun"
  function fmtDate(d) {
    return d.toLocaleDateString('es', { day: '2-digit', month: 'short' }).replace('.', '');
  }
  const today = new Date();
  const startWeek = planStart || today;
  const endWeek = new Date(startWeek.getTime() + 6 * 86400000);
  const startNextWeek = new Date(endWeek.getTime() + 86400000);
  const endNextWeek = new Date(startNextWeek.getTime() + 6 * 86400000);
  const dateRangeLabel = `${fmtDate(startWeek)} - ${fmtDate(endWeek)} ${endWeek.getFullYear()}`;

  function selectDuration(option) {
    if (option === 'esta-semana') {
      setSelectedDays(new Set([1, 2, 3, 4, 5, 6, 7]));
      setDurationLabel('Esta semana');
    } else if (option === 'proxima-semana') {
      // Si plan tiene 14 días, días 8-14
      setSelectedDays(new Set([8, 9, 10, 11, 12, 13, 14]));
      setDurationLabel('La próxima semana');
    } else if (option === 'ambas') {
      setSelectedDays(new Set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]));
      setDurationLabel('Esta y la próxima semana');
    }
    setShowDurationSheet(false);
  }

  function toggle(ing) {
    const next = new Set(checked);
    if (next.has(ing)) next.delete(ing); else next.add(ing);
    setChecked(next);
    try { localStorage.setItem('paufit-shopping-checked', JSON.stringify([...next])); } catch {}
  }

  function toggleDay(d) {
    setSelectedDays(s => {
      const n = new Set(s);
      n.has(d) ? n.delete(d) : n.add(d);
      return n;
    });
  }

  async function shareList() {
    const days = [...selectedDays].sort((a, b) => a - b);
    const dayLabel = days.length === (planDays?.length || 7)
      ? 'toda la semana'
      : `días ${days.join(', ')}`;
    const lines = [];
    grouped.forEach(sec => {
      lines.push(`\n*${sec.emoji} ${sec.title}*`);
      sec.items.forEach(i => lines.push(`• ${i.name}${i.qty ? ` — ${i.qty}` : ''}`));
    });
    const text = `🛒 Lista de la compra · Pau Fit\n${planTitle} — ${dayLabel}\n${lines.join('\n')}\n\nApp: https://app.paufit.co`;
    // Intentar Web Share nativo, fallback clipboard
    try {
      if (navigator.share) {
        await navigator.share({ title: 'Lista de la compra · Pau Fit', text });
        nav.toast('¡Compartido! 💗', { icon: '✓' });
        return;
      }
    } catch (err) {
      if (err?.name === 'AbortError') return;
    }
    try {
      await navigator.clipboard.writeText(text);
      nav.toast('Copiada al portapapeles 💗', { icon: '📋' });
    } catch {
      nav.toast('No se pudo compartir', { icon: '⚠️' });
    }
  }

  // Loading state — distinguir "cargando" de "no plan" para no quedarse en blanco
  const isLoading = planDays === null;

  // Contador útil: cuántos tachados / total
  const totalCount = items.length;
  const doneCount = items.filter(it => checked.has(it.key)).length;

  return (
    <div style={{ paddingBottom: 40, minHeight: '100dvh', background: T.bg }}>
      {/* HEADER Anna — back · título "Compra" · botón calendario */}
      <div style={{ padding: 'calc(12px + env(safe-area-inset-top)) 20px 4px' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <button onClick={() => nav.back()} aria-label="Volver" style={{
            width: 36, height: 36, borderRadius: 999, border: 'none', cursor: 'pointer',
            background: T.subtle,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>{Icon.back(T.text)}</button>
          {hasPlan && (
            <button onClick={() => setShowDurationSheet(true)} aria-label="Cambiar duración" style={{
              padding: '8px 14px', borderRadius: 999, border: 'none', cursor: 'pointer',
              background: T.subtle, color: T.text,
              fontFamily: 'inherit', fontSize: 11, fontWeight: 700, letterSpacing: 0.2,
            }}>{durationLabel} ▾</button>
          )}
        </div>
        <div style={{ marginTop: 18 }}>
          <div className="pf-display" style={{
            fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95,
          }}>
            Compra
          </div>
          {hasPlan && totalCount > 0 && (
            <div className="pf-eyebrow" style={{ color: T.muted, marginTop: 10 }}>
              {doneCount} de {totalCount} · {dateRangeLabel}
            </div>
          )}
        </div>
      </div>

      {/* Loading skeleton (mientras carga el plan) */}
      {isLoading && (
        <div style={{ padding: '24px 20px 0' }}>
          {[1,2,3,4,5,6].map(i => (
            <div key={i} style={{
              display: 'flex', alignItems: 'center', gap: 14,
              padding: '12px 0',
              borderBottom: `0.5px solid ${T.muted}15`,
            }}>
              <div style={{
                width: 48, height: 48, borderRadius: 999,
                background: T.subtle, opacity: 0.6,
              }}/>
              <div style={{ flex: 1 }}>
                <div style={{ height: 14, width: '60%', background: T.subtle, borderRadius: 6, opacity: 0.6 }}/>
                <div style={{ height: 10, width: '35%', background: T.subtle, borderRadius: 6, opacity: 0.4, marginTop: 6 }}/>
              </div>
            </div>
          ))}
        </div>
      )}

      {/* Empty state — sin plan activo */}
      {!isLoading && !hasPlan && (
        <div style={{ padding: '50px 24px', textAlign: 'center' }}>
          <div style={{ fontSize: 52, opacity: 0.5, marginBottom: 14 }}>🛒</div>
          <div className="pf-display-bold" style={{
            fontSize: 18, color: T.text, marginBottom: 8, textTransform: 'none', letterSpacing: -0.4,
          }}>
            Aún sin plan
          </div>
          <div style={{ fontSize: 13, color: T.muted, lineHeight: 1.5, marginBottom: 22 }}>
            Crea tu plan con IA y aquí aparece tu lista automática.
          </div>
          <button onClick={() => nav.tab('recetas')} style={{
            padding: '12px 22px', borderRadius: 999, border: 'none', cursor: 'pointer',
            background: T.accent, color: '#fff',
            fontFamily: 'inherit', fontSize: 12, fontWeight: 800, letterSpacing: 0.3,
          }}>CREAR PLAN →</button>
        </div>
      )}

      {/* LISTA — agrupada por secciones del súper, como se compra de verdad */}
      {!isLoading && hasPlan && items.length > 0 && (
        <div style={{ padding: '8px 20px 0' }}>
          {grouped.map(sec => (
          <div key={sec.id} style={{ marginBottom: 6 }}>
          <div style={{
            marginTop: 18, marginBottom: 4,
            fontSize: 11, fontWeight: 800, letterSpacing: 0.8,
            color: T.muted, 
            display: 'flex', alignItems: 'center', gap: 6,
          }}>
            <span style={{ fontSize: 14 }}>{sec.emoji}</span> {sec.title}
            <span style={{ fontWeight: 700, opacity: 0.6 }}>· {sec.items.length}</span>
          </div>
          {sec.items.map((it, i) => {
            const isChecked = checked.has(it.key);
            return (
              <button key={it.key} onClick={() => toggle(it.key)} style={{
                width: '100%', display: 'flex', alignItems: 'center', gap: 14,
                padding: '14px 0',
                border: 'none', background: 'transparent', cursor: 'pointer',
                borderBottom: i < sec.items.length - 1 ? `0.5px solid ${T.muted}18` : 'none',
                textAlign: 'left', fontFamily: 'inherit',
              }}>
                <div style={{
                  width: 48, height: 48, borderRadius: 999, flexShrink: 0,
                  background: `${T.accent}12`,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 26, opacity: isChecked ? 0.35 : 1,
                  transition: 'opacity 200ms ease',
                }}>{it.emoji}</div>

                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{
                    fontFamily: 'Poppins', fontSize: 15, fontWeight: 700, color: T.text,
                    textDecoration: isChecked ? 'line-through' : 'none',
                    opacity: isChecked ? 0.45 : 1,
                    lineHeight: 1.2, letterSpacing: -0.2,
                    textTransform: 'capitalize',
                  }}>{it.name}</div>
                  {it.qty && (
                    <div style={{
                      fontFamily: 'Poppins', fontSize: 12, color: T.muted, marginTop: 2,
                      opacity: isChecked ? 0.45 : 1, fontWeight: 600,
                    }}>{it.qty}</div>
                  )}
                </div>

                <div style={{
                  width: 24, height: 24, borderRadius: 999, flexShrink: 0,
                  border: isChecked ? 'none' : `1.5px solid ${T.muted}55`,
                  background: isChecked ? T.accent : 'transparent',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  transition: 'all 180ms ease',
                }}>{isChecked && <span style={{ color: '#fff', fontSize: 13, fontWeight: 800 }}>✓</span>}</div>
              </button>
            );
          })}
          </div>
          ))}

          {/* Botón compartir INLINE al final de la lista (no fixed que se solapaba) */}
          <div style={{ padding: '28px 0 0' }}>
            <button onClick={shareList} style={{
              width: '100%', padding: '16px', borderRadius: 999, border: 'none',
              background: T.text, color: T.bg, cursor: 'pointer',
              fontFamily: 'Poppins', fontSize: 15, fontWeight: 700,
            }}>Compartir lista</button>
          </div>
        </div>
      )}

      {/* Lista vacía pero hay plan (todos los días desmarcados) */}
      {!isLoading && hasPlan && items.length === 0 && (
        <div style={{ padding: '40px 24px', textAlign: 'center', color: T.muted, fontSize: 13, lineHeight: 1.5 }}>
          No hay ingredientes para los días seleccionados. Toca <strong>{durationLabel} ▾</strong> arriba para cambiar la duración.
        </div>
      )}

      {/* SHEET DURACIÓN estilo Silbe — z-index 100000 (encima del TabBar y de todo) */}
      {showDurationSheet && (
        <div onClick={() => setShowDurationSheet(false)} style={{
          position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
          zIndex: 100000,
          background: 'rgba(0,0,0,0.55)',
          backdropFilter: 'blur(2px)',
          WebkitBackdropFilter: 'blur(2px)',
          display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
          animation: 'fade-in 240ms ease',
        }}>
          {/* Keyframes inline para garantizar animación */}
          <style>{`
            @keyframes pauSheetSlideUp {
              from { transform: translateY(100%); }
              to { transform: translateY(0); }
            }
            @keyframes pauSheetFadeIn {
              from { opacity: 0; }
              to { opacity: 1; }
            }
          `}</style>
          <div onClick={(e) => e.stopPropagation()} style={{
            width: '100%', maxWidth: 480,
            background: T.bg, color: T.text,
            borderTopLeftRadius: 28, borderTopRightRadius: 28,
            paddingBottom: 'calc(16px + env(safe-area-inset-bottom))',
            animation: 'pauSheetSlideUp 360ms cubic-bezier(.34,1.56,.64,1)',
            boxShadow: '0 -10px 40px rgba(0,0,0,0.18)',
            maxHeight: '85dvh', overflowY: 'auto',
          }}>
            {/* Grabber */}
            <div style={{ width: 40, height: 4, borderRadius: 2, background: T.muted, opacity: 0.3, margin: '10px auto 0' }}/>

            {/* Título */}
            <div style={{
              textAlign: 'center', fontFamily: 'Poppins', fontSize: 17, fontWeight: 700, color: T.text,
              padding: '16px 20px 8px',
            }}>Seleccionar duración</div>

            {/* Opciones */}
            <div style={{ padding: '8px 0' }}>
              {[
                { v: 'esta-semana', t: 'Esta semana', s: `${fmtDate(startWeek)} - ${fmtDate(endWeek)} ${endWeek.getFullYear()}`, current: durationLabel === 'Esta semana' },
                { v: 'proxima-semana', t: 'La próxima semana', s: `${fmtDate(startNextWeek)} - ${fmtDate(endNextWeek)} ${endNextWeek.getFullYear()}`, current: durationLabel === 'La próxima semana' },
                { v: 'ambas', t: 'Esta y la próxima semana', s: `${fmtDate(startWeek)} - ${fmtDate(endNextWeek)} ${endNextWeek.getFullYear()}`, current: durationLabel === 'Esta y la próxima semana' },
              ].map((opt, i, arr) => (
                <button key={opt.v} onClick={() => selectDuration(opt.v)} style={{
                  width: '100%', display: 'flex', alignItems: 'center', gap: 14,
                  padding: '16px 24px',
                  border: 'none', background: 'transparent', cursor: 'pointer',
                  borderBottom: i < arr.length - 1 ? `0.5px solid ${T.muted}20` : 'none',
                  textAlign: 'left', fontFamily: 'inherit',
                }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontFamily: 'Poppins', fontSize: 16, fontWeight: 600, color: T.text }}>{opt.t}</div>
                    <div style={{ fontFamily: 'Poppins', fontSize: 13, color: T.muted, marginTop: 3 }}>{opt.s}</div>
                  </div>
                  <div style={{
                    width: 26, height: 26, borderRadius: 999, flexShrink: 0,
                    border: opt.current ? 'none' : `1.5px solid ${T.muted}50`,
                    background: opt.current ? T.accent : 'transparent',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                  }}>{opt.current && <span style={{ color: '#fff', fontSize: 14, fontWeight: 800 }}>✓</span>}</div>
                </button>
              ))}
            </div>

            {/* CTA Proceder */}
            <div style={{ padding: '12px 20px 4px' }}>
              <button onClick={() => setShowDurationSheet(false)} style={{
                width: '100%', padding: '15px', borderRadius: 999, border: 'none',
                background: T.accent, color: '#fff', cursor: 'pointer',
                fontFamily: 'Poppins', fontSize: 15, fontWeight: 700,
                boxShadow: `0 8px 22px ${T.accent}48`,
              }}>Proceder</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// CALENDARIO MENSTRUAL — rastreo de ciclo estilo Silbe
// ═════════════════════════════════════════════════════════════
function CicloScreen({ theme, nav }) {
  const T = theme;
  const [logs, setLogs] = useS3(null);
  const [showForm, setShowForm] = useS3(false);

  React.useEffect(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb) { setLogs([]); return; }
    (async () => {
      const { data: { user } } = await sb.auth.getUser();
      if (!user) { setLogs([]); return; }
      try {
        const { data } = await sb.from('period_logs').select('*').eq('user_id', user.id)
          .order('start_date', { ascending: false }).limit(12);
        setLogs(data || []);
      } catch { setLogs([]); }
    })();
  }, []);

  const last = logs && logs[0];
  // Estimar próximo: 28 días después de la última start_date
  let nextStartStr = null;
  if (last && last.start_date) {
    const next = new Date(last.start_date);
    next.setDate(next.getDate() + 28);
    nextStartStr = next.toLocaleDateString('es', { day: 'numeric', month: 'long' });
  }

  async function logToday() {
    const sb = window.__PAU_SUPABASE;
    if (!sb) return;
    const { data: { user } } = await sb.auth.getUser();
    if (!user) return;
    const today = new Date().toISOString().slice(0, 10);
    try {
      await sb.from('period_logs').insert({ user_id: user.id, start_date: today });
      const { data } = await sb.from('period_logs').select('*').eq('user_id', user.id)
        .order('start_date', { ascending: false }).limit(12);
      setLogs(data || []);
      nav.toast('Registrado 💗', { icon: '✓' });
    } catch (e) { nav.toast('Error: ' + e.message, { icon: '⚠️' }); }
  }

  return (
    <div style={{ paddingBottom: 110 }}>
      <div style={{ padding: '12px 20px 0' }}>
        <BackBtn onClick={() => nav.back()}/>
        <div style={{ flex: 1 }}>
          <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95, marginTop: 14 }}>
            Ciclo
          </div>
        </div>
      </div>

      <div style={{ padding: '24px 20px 0' }}>
        {logs === null ? (
          <SkeletonList count={3} T={T}/>
        ) : logs.length === 0 ? (
          <EmptyState emoji="🌸" T={T} title="Aún no has registrado tu ciclo"
            description="Registra el inicio de tu menstruación para llevar el seguimiento."
            ctaLabel="Registrar hoy" onCta={logToday}/>
        ) : (
          <>
            <div style={{
              padding: '20px',
              background: `linear-gradient(135deg, #FF7B9B, #D4506E)`,
              borderRadius: 20, color: '#fff',
              boxShadow: '0 8px 22px rgba(212,80,110,0.3)',
            }}>
              <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: 0.6,  opacity: 0.85 }}>Próximo ciclo</div>
              <div style={{ fontSize: 24, fontWeight: 800, marginTop: 6, letterSpacing: -0.5 }}>
                {nextStartStr || 'Calculando…'}
              </div>
              <div style={{ fontSize: 12, marginTop: 4, opacity: 0.85 }}>Estimado a 28 días del último registro</div>
            </div>

            <button onClick={logToday} style={{
              marginTop: 14, width: '100%', padding: '14px',
              background: T.accent, color: '#fff', border: 'none', borderRadius: 14, cursor: 'pointer',
              fontSize: 13, fontWeight: 700, fontFamily: 'inherit',
            }}>＋ Registrar hoy</button>

            <div style={{ marginTop: 20 }}>
              <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 10 }}>Historial</div>
              <div style={{ background: T.card, borderRadius: 20, border: T.border, overflow: 'hidden' }}>
                {logs.map((l, i) => (
                  <div key={l.id || i} style={{
                    padding: '12px 16px',
                    display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                    borderBottom: i < logs.length - 1 ? `0.5px solid ${T.subtle}` : 'none',
                  }}>
                    <span style={{ fontSize: 13, fontWeight: 600, color: T.text }}>
                      {new Date(l.start_date).toLocaleDateString('es', { day: 'numeric', month: 'long', year: 'numeric' })}
                    </span>
                    <span style={{ fontSize: 11, color: T.muted }}>{l.duration_days ? `${l.duration_days} días` : ''}</span>
                  </div>
                ))}
              </div>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// GUARDADOS — recetas/rutinas/retos marcados con bookmark
// localStorage key paufit-favs-{tipo} guarda array de ids
// ═════════════════════════════════════════════════════════════
function GuardadosScreen({ theme, nav }) {
  const T = theme;
  const [tab, setTab] = useS3('retos');
  const [bump, setBump] = useS3(0); // re-render al borrar favoritas

  function getFavs(tipo) {
    try { return new Set(JSON.parse(localStorage.getItem(`paufit-favs-${tipo}`) || '[]')); }
    catch { return new Set(); }
  }
  const favRetos = getFavs('retos');
  const favRutinas = getFavs('rutinas');
  const favRecetas = getFavs('recetas');

  // Recetas custom guardadas desde MealRecipeModal (plan IA o recetas con datos completos)
  let customRecetas = [];
  try {
    const raw = JSON.parse(localStorage.getItem('paufit-favs-recetas-custom') || '[]');
    if (Array.isArray(raw)) customRecetas = raw;
  } catch {}

  const retos = (typeof RETOS !== 'undefined' ? RETOS : []).filter(r => favRetos.has(r.id));
  const rutinas = (typeof RUTINAS !== 'undefined' ? RUTINAS : []).filter(r => favRutinas.has(r.id));
  const catalogRecetas = (typeof RECETAS !== 'undefined' ? RECETAS : []).filter(r => favRecetas.has(r.id));
  // Combinar catálogo + custom; las custom van con flag para abrir MealRecipeModal
  const recetas = [
    ...catalogRecetas.map(r => ({ ...r, _kind: 'catalog' })),
    ...customRecetas.map(r => ({
      id: 'custom-' + (r.name || '').toLowerCase().replace(/\s+/g, '-'),
      title: r.name,
      emoji: r.emoji || '🍓',
      kcal: r.kcal,
      image_url: r.image_url,
      ingredients: r.ingredients,
      instructions: r.instructions,
      mealType: r.mealType,
      _kind: 'custom',
      _raw: r,
    })),
  ];

  const tabs = [
    { id: 'retos', label: 'Retos', count: retos.length },
    { id: 'rutinas', label: 'Rutinas', count: rutinas.length },
    { id: 'recetas', label: 'Recetas', count: recetas.length },
  ];

  const list = tab === 'retos' ? retos : tab === 'rutinas' ? rutinas : recetas;

  function removeCustomReceta(name) {
    try {
      const arr = JSON.parse(localStorage.getItem('paufit-favs-recetas-custom') || '[]');
      const next = (Array.isArray(arr) ? arr : []).filter(r => (r.name || '').toLowerCase() !== (name || '').toLowerCase());
      localStorage.setItem('paufit-favs-recetas-custom', JSON.stringify(next));
      setBump(x => x + 1);
      if (nav?.toast) nav.toast('Quitada de favoritas', { icon: '🤍' });
    } catch {}
  }

  return (
    <div style={{ paddingBottom: 110 }}>
      <div style={{ padding: '12px 20px 0' }}>
        <BackBtn onClick={() => nav.back()}/>
        <div style={{ flex: 1 }}>
          <div className="pf-display" style={{ fontSize: 38, fontWeight: 800, color: T.text, letterSpacing: -1.4, lineHeight: 0.95, marginTop: 14 }}>
            Guardados
          </div>
        </div>
      </div>

      {/* Sub-tabs */}
      <div style={{ margin: '20px 20px 0', display: 'flex', gap: 6 }}>
        {tabs.map(t => (
          <button key={t.id} onClick={() => setTab(t.id)} style={{
            flex: 1, padding: '10px 4px', border: 'none', cursor: 'pointer',
            background: tab === t.id ? T.text : T.card,
            color: tab === t.id ? T.bg : T.text,
            borderRadius: 999, fontSize: 12, fontWeight: 700,
            fontFamily: 'inherit',
          }}>{t.label} {t.count > 0 && `· ${t.count}`}</button>
        ))}
      </div>

      {/* Lista o empty */}
      <div style={{ padding: '20px 20px 0' }}>
        {list.length === 0 ? (
          <EmptyState
            emoji="🔖" T={T}
            title={`Sin ${tab} guardadas`}
            description={`Toca el icono de marcador en cualquier ${tab.slice(0,-1)} para guardarla aquí.`}
            ctaLabel={tab === 'recetas' ? 'Ver recetas' : tab === 'rutinas' ? 'Ver rutinas' : 'Ver retos'}
            onCta={() => nav.tab(tab === 'recetas' ? 'recetas' : 'retos')}
          />
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            {list.map(item => {
              const isCustom = item._kind === 'custom';
              return (
                <div key={item.id} onClick={() => {
                  if (isCustom && nav?.openModal && window.MealRecipeModal) {
                    nav.openModal(React.createElement(window.MealRecipeModal, {
                      T, meal: item._raw, nav,
                      onClose: () => nav.closeModal(),
                    }));
                  } else {
                    nav.go(tab === 'retos' ? 'reto' : tab === 'rutinas' ? 'rutina' : 'receta', item.id);
                  }
                }} style={{
                  padding: '14px 16px', background: T.card, borderRadius: 20, border: T.border,
                  display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer',
                  position: 'relative', overflow: 'hidden',
                }}>
                  {isCustom && item.image_url ? (
                    <div style={{
                      width: 48, height: 48, borderRadius: 14, flexShrink: 0, overflow: 'hidden',
                      background: T.subtle,
                    }}>
                      <img src={item.image_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
                    </div>
                  ) : (
                    <span style={{ fontSize: 28 }}>{item.emoji || '✨'}</span>
                  )}
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 700, color: T.text, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.title}</div>
                    <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>
                      {isCustom ? `${item.mealType || 'Receta del plan'}${item.kcal ? ` · ${item.kcal} kcal` : ''}` :
                        item.days ? `${item.days} días` : item.minutes ? `${item.minutes} min` : item.kcal ? `${item.kcal} kcal` : ''}
                    </div>
                  </div>
                  {isCustom && (
                    <button onClick={(e) => { e.stopPropagation(); removeCustomReceta(item.title); }} aria-label="Quitar de favoritas" style={{
                      width: 30, height: 30, borderRadius: 999, flexShrink: 0,
                      border: 'none', background: 'transparent', cursor: 'pointer',
                      fontSize: 14, color: T.muted,
                    }}>×</button>
                  )}
                  {!isCustom && Icon.chevR(T.muted, 8)}
                </div>
              );
            })}
          </div>
        )}
      </div>
    </div>
  );
}

// ═════════════════════════════════════════════════════════════
// PerfilPublicoScreen — ver perfil de otra usuaria
// Si is_public=false → solo muestra "perfil privado"
// Si is_public=true → avatar, nombre, bio, racha, retos completados,
// puntos, fotos PÚBLICAS, insignias unlocked
// ═════════════════════════════════════════════════════════════
function PerfilPublicoScreen({ userId, theme, nav }) {
  const T = theme;
  const [data, setData] = useS3(null);
  const [error, setError] = useS3(null);

  useE3(() => {
    const sb = window.__PAU_SUPABASE;
    if (!sb || !userId) { setError('Perfil no encontrado'); return; }
    (async () => {
      try {
        // 1) Perfil
        const { data: profile } = await sb
          .from('profiles')
          .select('id, full_name, username, bio, points, level, avatar_url, is_public, goal')
          .eq('id', userId)
          .maybeSingle();
        if (!profile) { setError('Perfil no encontrado'); return; }
        // Si el perfil NO es público → mostrar solo cuerpo bloqueado
        if (profile.is_public === false) {
          setData({ profile, isPrivate: true });
          return;
        }
        // 2) Retos completados (count)
        const { data: userRetos } = await sb
          .from('user_retos')
          .select('reto_id, completed_days, completed_at')
          .eq('user_id', userId)
          .not('completed_at', 'is', null);
        const retosCompleted = (userRetos || []).filter(r => (r.completed_days || []).length >= 7).length;
        // 3) Días totales
        const { count: totalDays } = await sb
          .from('day_completions')
          .select('*', { count: 'exact', head: true })
          .eq('user_id', userId);
        // 4) Fotos PÚBLICAS (filename empieza por "*-pub-")
        const { data: photosList } = await sb.storage
          .from('progress-photos')
          .list(userId, { limit: 50 });
        const publicPhotos = await Promise.all(
          (photosList || [])
            .filter(f => f.name && /^(frente|lateral|atras)-pub-/.test(f.name))
            .slice(0, 6)
            .map(async (f) => {
              const path = `${userId}/${f.name}`;
              let url = null;
              try {
                const { data: signed } = await sb.storage.from('progress-photos').createSignedUrl(path, 3600);
                if (signed?.signedUrl) url = signed.signedUrl;
              } catch {}
              if (!url) {
                const { data: u } = await sb.storage.from('progress-photos').getPublicUrl(path);
                url = u?.publicUrl;
              }
              const m = f.name.match(/^(frente|lateral|atras)/);
              return { id: f.name, url, angle: m?.[1] || null, date: new Date(f.created_at || f.updated_at || Date.now()).toLocaleDateString('es', { day: 'numeric', month: 'short' }) };
            })
        );
        setData({ profile, isPrivate: false, totalDays: totalDays || 0, retosCompleted, photos: publicPhotos });
      } catch (e) {
        setError(e.message || 'Error');
      }
    })();
  }, [userId]);

  if (error) return (
    <div style={{ padding: 30, textAlign: 'center', color: T.muted, fontSize: 13 }}>
      <button onClick={() => nav.back()} style={{
        margin: '12px auto', display: 'block',
        padding: '8px 16px', borderRadius: 999, border: T.border, background: T.card,
        cursor: 'pointer', color: T.text,
      }}>← Volver</button>
      {error}
    </div>
  );
  if (!data) return (
    <div style={{ padding: 60, textAlign: 'center', color: T.muted }}>
      <div style={{
        width: 32, height: 32, borderRadius: 50, margin: '0 auto 12px',
        border: '2.5px solid ' + T.subtle, borderTopColor: T.accent,
        animation: 'spin 800ms linear infinite',
      }}/>
      Cargando perfil…
    </div>
  );

  const p = data.profile;
  const displayName = p.full_name || p.username || 'Sin nombre';
  const initials = displayName.split(' ').map(s => s[0]).slice(0, 2).join('').toUpperCase();

  return (
    <div style={{ paddingBottom: 110 }}>
      {/* Hero */}
      <div style={{
        background: `linear-gradient(160deg, ${T.subtle}, ${T.accent}30)`,
        padding: '12px 20px 30px',
        borderBottomLeftRadius: 32, borderBottomRightRadius: 32,
      }}>
        <BackBtn onClick={() => nav.back()}/>
        {/* Avatar y nombre */}
        <div style={{ marginTop: 18, textAlign: 'center' }}>
          <div style={{
            display: 'inline-flex', width: 110, height: 110, borderRadius: '50%',
            background: p.avatar_url ? '#fff' : `linear-gradient(135deg, ${T.accent}, ${T.deep})`,
            alignItems: 'center', justifyContent: 'center',
            fontSize: 36, fontWeight: 800, color: '#fff',
            boxShadow: `0 8px 24px ${T.accent}40`,
            overflow: 'hidden',
          }}>
            {p.avatar_url
              ? <img src={p.avatar_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
              : initials
            }
          </div>
          <div className="pf-display" style={{ fontSize: 26, fontWeight: 700, color: T.text, marginTop: 12, letterSpacing: -0.5 }}>
            {displayName}
          </div>
          {p.username && p.full_name && (
            <div style={{ fontSize: 12, color: T.muted, marginTop: 2 }}>@{p.username}</div>
          )}
          {p.bio && (
            <div style={{ fontSize: 12, color: T.text, marginTop: 8, lineHeight: 1.4, maxWidth: 280, margin: '8px auto 0' }}>
              {p.bio}
            </div>
          )}
        </div>
      </div>

      {/* Si privado, mensaje y stop */}
      {data.isPrivate && (
        <div style={{ padding: '40px 20px', textAlign: 'center' }}>
          <div style={{ fontSize: 48, marginBottom: 12 }}>🔒</div>
          <div style={{ fontSize: 15, fontWeight: 700, color: T.text }}>
            Perfil privado
          </div>
          <div style={{ fontSize: 12, color: T.muted, marginTop: 6, lineHeight: 1.5, maxWidth: 280, margin: '6px auto 0' }}>
            {displayName} prefiere mantener su perfil en privado 💗
          </div>
        </div>
      )}

      {/* Si público, stats + fotos */}
      {!data.isPrivate && (
        <>
          {/* Stats */}
          <div style={{
            margin: '20px 20px 0', padding: '18px 20px',
            background: T.card, borderRadius: 20, border: T.border,
            display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12,
          }}>
            <div style={{ textAlign: 'center' }}>
              <div style={{ fontSize: 22, fontWeight: 800, color: T.accent, letterSpacing: -0.3 }}>{p.points || 0}</div>
              <div style={{ fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.4,  marginTop: 2 }}>⭐ Puntos</div>
            </div>
            <div style={{ textAlign: 'center', borderLeft: T.border, borderRight: T.border }}>
              <div style={{ fontSize: 22, fontWeight: 800, color: T.text, letterSpacing: -0.3 }}>{data.totalDays}</div>
              <div style={{ fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.4,  marginTop: 2 }}>🔥 Días</div>
            </div>
            <div style={{ textAlign: 'center' }}>
              <div style={{ fontSize: 22, fontWeight: 800, color: T.text, letterSpacing: -0.3 }}>{data.retosCompleted}</div>
              <div style={{ fontSize: 10, color: T.muted, fontWeight: 600, letterSpacing: 0.4,  marginTop: 2 }}>🏆 Retos</div>
            </div>
          </div>

          {/* Fotos PÚBLICAS de progreso */}
          {data.photos.length > 0 && (
            <div style={{ padding: '24px 20px 0' }}>
              <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 10 }}>📸 Fotos públicas</div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 6 }}>
                {data.photos.map(ph => (
                  <div key={ph.id} style={{
                    aspectRatio: '3 / 4', borderRadius: 14, overflow: 'hidden',
                    background: '#000', position: 'relative',
                  }}>
                    {ph.url && <img src={ph.url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>}
                    {ph.angle && (
                      <div style={{
                        position: 'absolute', top: 4, left: 4,
                        padding: '2px 6px', borderRadius: 999,
                        background: 'rgba(0,0,0,0.6)', color: '#fff',
                        fontSize: 8, fontWeight: 700, letterSpacing: 0.3, textTransform: 'capitalize',
                      }}>{ph.angle === 'frente' ? '🚶‍♀️' : ph.angle === 'lateral' ? '↔️' : '🔙'}</div>
                    )}
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* Si no tiene fotos públicas */}
          {data.photos.length === 0 && (
            <div style={{ padding: '24px 20px 0' }}>
              <div className="pf-eyebrow" style={{ color: T.muted, marginBottom: 10 }}>📸 Fotos públicas</div>
              <div style={{
                padding: '20px', background: T.card, borderRadius: 20, border: T.border,
                fontSize: 12, color: T.muted, textAlign: 'center',
              }}>
                {displayName} aún no comparte fotos públicas
              </div>
            </div>
          )}
        </>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// VasBienCard — señales positivas reales del usuario.
// "no sé si lo hago bien" → este card refuerza lo que YA está bien.
// Se nutre del cache: stats.streak, día_completions reciente, mealLogs.
// ─────────────────────────────────────────────────────────────
function VasBienCard({ T }) {
  const [signals, setSignals] = useS3([]);
  useE3(() => {
    const sb = window.__PAU_SUPABASE;
    const cache = window.__pauCache || {};
    const stats = cache.stats || { streak: 0, totalDays: 0 };
    (async () => {
      const list = [];
      // Señal 1 — racha
      if (stats.streak >= 1) {
        list.push({
          emoji: '🔥',
          title: `Llevas ${stats.streak} día${stats.streak !== 1 ? 's' : ''} seguidos`,
          note: 'La constancia se nota más que la intensidad',
        });
      }
      // Señal 2 — entrenamientos esta semana
      if (sb) {
        try {
          const { data: { user } } = await sb.auth.getUser();
          if (user) {
            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: dc } = await sb.from('day_completions')
              .select('completed_at')
              .eq('user_id', user.id)
              .gte('completed_at', monday.toISOString());
            const trainedDays = new Set((dc || []).map(d => new Date(d.completed_at).toLocaleDateString('en-CA'))).size;
            if (trainedDays >= 2) {
              list.push({
                emoji: '💪',
                title: `${trainedDays} entrenos esta semana`,
                note: 'Vas mejor que el 80% que solo "empieza el lunes"',
              });
            }
            // Señal 3 — comidas registradas
            const { data: ml } = await sb.from('meal_logs')
              .select('logged_at')
              .eq('user_id', user.id)
              .gte('logged_at', monday.toISOString());
            const mealCount = (ml || []).length;
            if (mealCount >= 3) {
              list.push({
                emoji: '📝',
                title: `${mealCount} comidas registradas esta semana`,
                note: 'Anotar es el 50% del cambio',
              });
            }
          }
        } catch {}
      }
      // Señal 4 — total acumulado
      if (stats.totalDays >= 7) {
        list.push({
          emoji: '✨',
          title: `${stats.totalDays} días completados en total`,
          note: 'Esto ya es un hábito, no un intento',
        });
      }
      // Si no hay nada que mostrar, una señal de bienvenida
      if (list.length === 0) {
        list.push({
          emoji: '🌱',
          title: 'Estás aquí',
          note: 'Eso ya es más de lo que muchas hacen. Vamos paso a paso',
        });
      }
      setSignals(list);
    })();
  }, []);
  if (signals.length === 0) return null;
  return (
    <div style={{ margin: '24px 20px 0' }}>
      <div className="pf-display-bold" style={{
        fontSize: 14, color: T.text, marginBottom: 12, paddingLeft: 4,
      }}>
        Vas bien
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {signals.map((s, i) => (
          <div key={i} style={{
            padding: '14px 16px',
            background: T.card, borderRadius: 18,
            display: 'flex', alignItems: 'center', gap: 14,
          }}>
            <div style={{
              width: 42, height: 42, borderRadius: 999, flexShrink: 0,
              background: `${T.accent}15`,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 20,
            }}>{s.emoji}</div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{
                fontSize: 13, fontWeight: 700, color: T.text, letterSpacing: -0.2,
              }}>{s.title}</div>
              <div style={{ fontSize: 11, color: T.muted, marginTop: 2, fontWeight: 500, lineHeight: 1.35 }}>
                {s.note}
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { LogrosScreen, ProgresoScreen, HistorialScreen, RutinaDetailScreen, BunnyPlayerModal, CalendarioScreen, TiendaScreen, ListaCompraScreen, CicloScreen, GuardadosScreen, PerfilPublicoScreen, VasBienCard });
