// Bortec Cycling — Entrenamientos App (Free)
// Implementation of the "Entrenamientos App - Free" design.
// Responsive: mobile uses a bottom tab bar, desktop (>=900px) uses a left sidebar.

const C = {
  accent: '#37cc50',
  accentLight: '#5fe077',
  panel: '#121212',
  card: '#1c1c1c',
  cardAlt: '#262626',
  text: '#f2f2f2',
  textSoft: '#a8a8a8',
  textMuted: '#c4c4c4',
  border: 'rgba(255,255,255,0.16)',
  borderLight: 'rgba(255,255,255,0.18)',
  greenBg: '#1f4d2a',
  greenText: '#e3fbe7',
  greenTextLight: '#8ce89c',
};

const inputStyle = {
  minHeight: 42, padding: '8px 12px', fontSize: 14, color: C.text,
  background: C.card, border: `1px solid ${C.border}`, borderRadius: 8, outline: 'none',
};
const smallInputStyle = { ...inputStyle, minHeight: 40, fontSize: 13.5 };
const labelStyle = { fontSize: 12, color: C.textSoft };

function primaryBtn(height = 46, fontSize = 15) {
  return {
    height, borderRadius: 8, border: `1px solid ${C.accent}`, background: 'transparent',
    color: C.accent, fontWeight: 500, fontSize, fontFamily: 'Inter, sans-serif', cursor: 'pointer',
  };
}
function ghostBtn(height = 46, fontSize = 13.5, color = '#f2f2f2') {
  return {
    height, borderRadius: 8, border: `1px solid ${C.borderLight}`, background: 'transparent',
    color, fontWeight: 500, fontSize, fontFamily: 'Inter, sans-serif', cursor: 'pointer',
  };
}
function badge(bg, color, border) {
  return {
    display: 'inline-flex', alignItems: 'center', fontSize: 11, padding: '3px 10px',
    borderRadius: 6, background: bg, color, border,
  };
}
function heading(size, margin) {
  return {
    margin: margin ?? 0, fontSize: size, fontWeight: 500, color: C.text,
    fontFamily: "'Oswald', sans-serif", textTransform: 'uppercase', letterSpacing: '0.01em',
    lineHeight: size >= 24 ? 1.2 : undefined,
  };
}
const statCard = {
  borderRadius: 8, background: C.card, padding: '12px 10px',
  display: 'flex', flexDirection: 'column', gap: 4,
};
const sectionCard = {
  borderRadius: 10, background: C.card, padding: 12,
  display: 'flex', flexDirection: 'column', gap: 6,
};
const tableHeadCell = {
  textAlign: 'left', fontSize: 10.5, letterSpacing: '0.06em', textTransform: 'uppercase',
  color: C.textSoft, padding: '6px 4px', borderBottom: `1px solid ${C.border}`,
};
const tableCell = { padding: '8px 4px', borderBottom: '1px solid rgba(255,255,255,0.08)' };

const PLAN_PHASES = [
  { name: 'Calentamiento', duration: '10 min', zone: 'Z1 · Muy ligero', rpe: '2/10' },
  { name: 'Bloque principal', duration: '3 × 5 min', zone: 'Z2–Z3 · Moderado', rpe: '5/10' },
  { name: 'Recuperación', duration: '2 min c/u', zone: 'Z1 · Muy ligero', rpe: '2/10' },
  { name: 'Enfriamiento', duration: '10 min', zone: 'Z1 · Muy ligero', rpe: '2/10' },
];

const SUGGESTED_RACES = [
  { id: 'r1', name: 'Vuelta al Ajusco', date: '20 sep', location: 'CDMX', distance: '110 km', note: 'Buen siguiente reto después de tu base — subidas moderadas.' },
  { id: 'r2', name: 'Copa Fondista Toluca', date: '8 nov', location: 'Toluca', distance: '60 km', note: 'Ideal si quieres tu primera carrera con grupo.' },
  { id: 'r3', name: 'Reto Bajío', date: '14 feb', location: 'Guanajuato', distance: '95 km', note: 'La proponemos para el grupo de principiantes cada año.' },
];

const WEEK_LABELS = ['L', 'M', 'X', 'J', 'V', 'S', 'D'];
const WEEK_TYPES = ['rest', 'done', 'done', 'rest', 'today', 'plan', 'plan'];
const MONTH_NAMES = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'];
const MONTH_SHORT = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'];

const TAB_SCREENS = ['inicio', 'plan', 'historial', 'chat', 'carreras'];
const CHAT_LIMIT = 3;

const NAV_ICONS = {
  inicio: (color) => (
    <svg width="18" height="18" viewBox="0 0 20 20">
      <polygon points="10,2.5 18,9 15.5,9 15.5,17.5 4.5,17.5 4.5,9 2,9" fill="none" stroke={color} strokeWidth="1.6" strokeLinejoin="round"></polygon>
    </svg>
  ),
  plan: (color) => (
    <svg width="18" height="18" viewBox="0 0 20 20">
      <rect x="2.5" y="3.5" width="15" height="14" rx="2" fill="none" stroke={color} strokeWidth="1.6"></rect>
      <line x1="2.5" y1="7.5" x2="17.5" y2="7.5" stroke={color} strokeWidth="1.6"></line>
    </svg>
  ),
  historial: (color) => (
    <svg width="18" height="18" viewBox="0 0 20 20">
      <rect x="3" y="11" width="3.4" height="6.5" rx="0.6" fill={color}></rect>
      <rect x="8.3" y="6.5" width="3.4" height="11" rx="0.6" fill={color}></rect>
      <rect x="13.6" y="2.5" width="3.4" height="15" rx="0.6" fill={color}></rect>
    </svg>
  ),
  chat: (color) => (
    <svg width="18" height="18" viewBox="0 0 20 20">
      <path d="M2.5,4 h15 a1,1 0 0 1 1,1 v8 a1,1 0 0 1 -1,1 h-9 l-4,3.5 v-3.5 h-2 a1,1 0 0 1 -1,-1 v-8 a1,1 0 0 1 1,-1 z" fill="none" stroke={color} strokeWidth="1.6" strokeLinejoin="round"></path>
    </svg>
  ),
  carreras: (color) => (
    <svg width="18" height="18" viewBox="0 0 20 20">
      <line x1="4.5" y1="2" x2="4.5" y2="18" stroke={color} strokeWidth="1.6" strokeLinecap="round"></line>
      <polygon points="4.5,3 16,6.5 4.5,10" fill={color}></polygon>
    </svg>
  ),
};

class App extends React.Component {
  state = {
    bootLoading: true,
    stage: 'onboarding',
    isDesktop: (typeof window !== 'undefined' && window.innerWidth >= 900),
    onboardingStep: 0,
    loginEmail: '', loginPassword: '',
    authMode: 'login', authLoading: false, authError: null, authInfo: null,
    userId: null,
    screen: 'inicio',
    chatInput: '',
    chatMessages: [
      { from: 'bot', text: '¡Hola! Soy tu asistente de entrenamiento. Pregúntame lo que quieras sobre tu plan de hoy.' },
    ],
    chatResponsesUsed: 0,
    introDone: false,
    intake: {
      fullName: '', contact: '', age: '', years: '', level: '', modality: '',
      days: [], schedule: '', ftp: '', weeklyKm: '', health: '', priorPlan: '',
    },
    intakeSaving: false, intakeError: null,
    perfil: null,
    marcas: [],
    pendingMark: null,
    hasCoachNotification: true,
    racesTab: 'mios',
    raceGoals: [
      { name: 'Gran Fondo CDMX', date: '12 oct', distance: '80 km' },
    ],
    addRaceOpen: false,
    newRaceName: '', newRaceDate: '', newRaceDistance: '',
    suggestedRacesMarked: {},
    connectedAccounts: [
      { id: 'garmin', name: 'Garmin Connect', connected: false },
      { id: 'tp', name: 'TrainingPeaks', connected: true },
    ],
    pushNotifications: true,
    userPlan: 'free', planLocked: true,
    liveElapsed: 0, livePaused: false, livePhaseIdx: 0,
    lastSessionDuration: '00:00',
    selectedRpe: 5,
    range: 'semana', rangeOffset: 0,
  };

  componentDidMount() {
    this._timer = setInterval(() => {
      if (this.state.screen === 'liveSession' && !this.state.livePaused) {
        this.setState(s => ({ liveElapsed: s.liveElapsed + 1 }));
      }
    }, 1000);
    this._onResize = () => this.setState({ isDesktop: window.innerWidth >= 900 });
    window.addEventListener('resize', this._onResize);

    this.bootstrapAuth();
  }

  componentWillUnmount() {
    clearInterval(this._timer);
    window.removeEventListener('resize', this._onResize);
  }

  async bootstrapAuth() {
    try {
      const { data: { session } } = await window.sb.auth.getSession();
      if (session && session.user) {
        await this.loadUserData(session.user.id);
      }
    } catch (err) {
      console.error('No se pudo restaurar la sesión', err);
    } finally {
      this.setState({ bootLoading: false });
    }
  }

  async loadUserData(userId) {
    const [{ data: perfilRow, error: perfilErr }, { data: marcasRows, error: marcasErr }] = await Promise.all([
      window.sb.from('perfil').select('*').eq('user_id', userId).maybeSingle(),
      window.sb.from('marcas').select('*').eq('user_id', userId).order('fecha', { ascending: false }),
    ]);
    if (perfilErr) console.error('Error cargando perfil', perfilErr);
    if (marcasErr) console.error('Error cargando marcas', marcasErr);
    this.setState({
      userId,
      perfil: perfilRow || null,
      marcas: marcasRows || [],
      introDone: !!perfilRow,
      stage: perfilRow ? 'app' : 'intake',
      screen: 'inicio',
    });
  }

  classifyMark(text) {
    if (/\d{1,2}:\d{2}(:\d{2})?/.test(text)) return 'tiempo';
    if (/\d+\s?(w\b|watts?)/i.test(text)) return 'potencia';
    if (/\d+\s?(km\/h|kmh)/i.test(text)) return 'velocidad';
    if (/(récord|record|marca|mejor tiempo|\bpr\b|logré)/i.test(text)) return 'record';
    return 'marca';
  }

  formatFecha(iso) {
    const d = new Date(iso);
    return d.getDate() + ' ' + MONTH_SHORT[d.getMonth()].toLowerCase();
  }

  async handleAuthSubmit() {
    const { loginEmail, loginPassword, authMode } = this.state;
    const email = loginEmail.trim();
    const password = loginPassword;
    if (!email || !password) {
      this.setState({ authError: 'Ingresa tu correo y contraseña.' });
      return;
    }
    this.setState({ authLoading: true, authError: null, authInfo: null });
    try {
      if (authMode === 'register') {
        const { data, error } = await window.sb.auth.signUp({ email, password });
        if (error) throw error;
        if (!data.session) {
          this.setState({ authLoading: false, authMode: 'login', authInfo: 'Cuenta creada. Revisa tu correo para confirmarla y luego inicia sesión.' });
          return;
        }
        await this.loadUserData(data.user.id);
      } else {
        const { data, error } = await window.sb.auth.signInWithPassword({ email, password });
        if (error) throw error;
        await this.loadUserData(data.user.id);
      }
      this.setState({ authLoading: false, loginPassword: '' });
    } catch (err) {
      this.setState({ authLoading: false, authError: err.message || 'Ocurrió un error, intenta de nuevo.' });
    }
  }

  async logout() {
    try {
      await window.sb.auth.signOut();
    } catch (err) {
      console.error('Error al cerrar sesión', err);
    }
    this.setState({
      stage: 'login', screen: 'inicio', loginEmail: '', loginPassword: '',
      userId: null, perfil: null, marcas: [], introDone: false,
      authMode: 'login', authError: null, authInfo: null,
    });
  }

  answerFor(text) {
    const t = text.toLowerCase();
    if (t.includes('zona')) return { text: 'Las zonas de FC miden qué tan duro trabajas: Zona 1-2 son ritmos suaves para construir base, Zona 3+ ya es esfuerzo alto. Hoy tu plan pide Zona 2 — cómodo, conversacional.', escalate: false };
    if (t.includes('dolor') || t.includes('lesion') || t.includes('lesión')) return { text: 'Si sientes dolor agudo, detén el entrenamiento y descansa. Las molestias musculares leves son normales, el dolor articular no.', escalate: true };
    if (t.includes('plan') || t.includes('cambiar') || t.includes('ajustar')) return { text: 'Puedo darte pautas generales, pero los cambios de plan los confirma tu entrenador.', escalate: true };
    if (t.includes('comer') || t.includes('comida') || t.includes('nutri')) return { text: 'Antes de rodar, algo ligero en carbohidratos 1-2 horas antes basta. Después del entreno, prioriza proteína y buena hidratación.', escalate: false };
    return { text: 'Buena pregunta — no tengo una respuesta certera para eso.', escalate: true };
  }

  seededRandom(seed) {
    const x = Math.sin(seed) * 10000;
    return x - Math.floor(x);
  }

  getMonday(d) {
    const date = new Date(d);
    const day = date.getDay();
    const diff = (day === 0 ? -6 : 1) - day;
    date.setDate(date.getDate() + diff);
    date.setHours(0, 0, 0, 0);
    return date;
  }

  detectMark(text) {
    const hasTimeOrNumber = /\d{1,2}:\d{2}(:\d{2})?/.test(text) || /\d+\s?(km\/h|kmh|w\b|watts?)/i.test(text);
    const hasMarkWord = /(récord|record|marca|mejor tiempo|\bpr\b|logré)/i.test(text);
    return hasTimeOrNumber || hasMarkWord;
  }

  async confirmMark(save) {
    const mark = this.state.pendingMark;
    if (!mark) return;
    if (!save) {
      this.setState(s => ({
        pendingMark: null,
        chatMessages: [...s.chatMessages, { from: 'bot', label: 'Asistente IA', text: 'Entendido, no la guardé.' }],
      }));
      return;
    }
    const { userId } = this.state;
    const tipo = this.classifyMark(mark);
    this.setState({ pendingMark: null });
    const { data, error } = await window.sb.from('marcas')
      .insert({ user_id: userId, tipo, valor: mark, fuente: 'chat' })
      .select()
      .maybeSingle();
    if (error) {
      this.setState(s => ({
        chatMessages: [...s.chatMessages, { from: 'bot', label: 'Asistente IA', text: 'No pude guardar la marca (' + error.message + ').' }],
      }));
      return;
    }
    this.setState(s => ({
      marcas: [data, ...s.marcas],
      chatMessages: [...s.chatMessages, { from: 'bot', label: 'Asistente IA', text: 'Guardado en tus marcas ✅' }],
    }));
  }

  sendText(text) {
    const clean = (text || '').trim();
    if (!clean || this.state.pendingMark) return;
    if (this.detectMark(clean)) {
      this.setState(s => ({
        chatInput: '',
        chatMessages: [...s.chatMessages, { from: 'user', text: clean }, { from: 'bot', label: 'Asistente IA', text: 'Parece una marca nueva: "' + clean + '". ¿La guardo en tu perfil de marcas?' }],
        pendingMark: clean,
      }));
      return;
    }
    if (this.state.chatResponsesUsed >= CHAT_LIMIT) return;
    const { text: reply, escalate } = this.answerFor(clean);
    this.setState(s => {
      const msgs = [...s.chatMessages, { from: 'user', text: clean }, { from: 'bot', label: 'Asistente IA', text: reply }];
      if (escalate) msgs.push({ from: 'system', text: 'Notificado a Ana, tu entrenadora — te responderá aquí mismo.' });
      return { chatInput: '', chatMessages: msgs, chatResponsesUsed: s.chatResponsesUsed + 1 };
    });
    if (escalate) {
      setTimeout(() => {
        this.setState(s => ({
          chatMessages: [...s.chatMessages, { from: 'coach', label: 'Ana · Entrenadora', text: 'Vi tu mensaje — lo revisamos en tu próxima sesión, mientras tanto no fuerces el ritmo.' }],
        }));
      }, 2200);
    }
  }

  go(screen, extra) {
    this.setState({ screen, ...(extra || {}) });
  }

  renderOnboarding() {
    const { onboardingStep } = this.state;
    const next = () => {
      if (onboardingStep === 0) this.setState({ onboardingStep: 1 });
      else this.setState({ stage: 'login' });
    };
    const dot = (active) => ({ height: 4, borderRadius: 2, width: active ? 22 : 10, background: active ? C.accent : '#3a3a3a' });
    const photo = (label) => (
      <div style={{ width: '100%', aspectRatio: 1.5, borderRadius: 16, background: 'repeating-linear-gradient(135deg,#1c1c1c,#1c1c1c 10px,#161616 10px,#161616 20px)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, color: '#6b6b6b', fontFamily: 'monospace', textAlign: 'center', padding: '0 20px' }}>{label}</div>
    );
    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', padding: '56px 24px 36px', justifyContent: 'space-between' }}>
        <img src="assets/bortec-logo.png" alt="Bortec Cycling" style={{ height: 16, width: 'auto', alignSelf: 'flex-start', opacity: 0.92 }} />
        {onboardingStep === 0 && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            {photo('foto: ciclista rodando en montaña')}
            <h1 style={{ ...heading(25), letterSpacing: '-0.02em', lineHeight: 1.2 }}>Entrena con un plan hecho para ti</h1>
            <p style={{ margin: 0, fontSize: 13.5, color: C.textSoft, lineHeight: 1.5 }}>Planes claros, tu progreso siempre visible, y el respaldo real de tu entrenador.</p>
          </div>
        )}
        {onboardingStep === 1 && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            {photo('foto: ciclista en carrera')}
            <h1 style={{ ...heading(25), letterSpacing: '-0.02em', lineHeight: 1.2 }}>Habla directo con tu entrenador</h1>
            <p style={{ margin: 0, fontSize: 13.5, color: C.textSoft, lineHeight: 1.5 }}>Resuelve dudas al instante y recibe comentarios sobre cada sesión que completas.</p>
          </div>
        )}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div style={{ display: 'flex', gap: 6, justifyContent: 'center' }}>
            <div style={dot(onboardingStep === 0)}></div>
            <div style={dot(onboardingStep === 1)}></div>
          </div>
          <button onClick={next} style={primaryBtn(46, 15)}>{onboardingStep === 0 ? 'Siguiente' : 'Comenzar'}</button>
        </div>
      </div>
    );
  }

  renderLogin() {
    const { loginEmail, loginPassword, authMode, authLoading, authError, authInfo } = this.state;
    const isRegister = authMode === 'register';
    const submit = () => this.handleAuthSubmit();
    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', padding: '56px 24px 36px', gap: 18 }}>
        <img src="assets/bortec-logo.png" alt="Bortec Cycling" style={{ height: 16, width: 'auto', alignSelf: 'flex-start', opacity: 0.92 }} />
        <div>
          <h1 style={{ ...heading(23, '0 0 4px'), letterSpacing: '-0.02em' }}>{isRegister ? 'Crea tu cuenta' : 'Inicia sesión'}</h1>
          <p style={{ margin: 0, fontSize: 13, color: C.textSoft }}>{isRegister ? 'Regístrate para armar tu plan y hablar con tu entrenador.' : 'Entra para ver tu plan y hablar con tu entrenador.'}</p>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <input value={loginEmail} onChange={e => this.setState({ loginEmail: e.target.value })} placeholder="Correo" style={inputStyle} />
          <input value={loginPassword} onChange={e => this.setState({ loginPassword: e.target.value })} onKeyDown={e => { if (e.key === 'Enter') submit(); }} placeholder="Contraseña" type="password" style={inputStyle} />
        </div>
        {authError && <p style={{ margin: 0, fontSize: 12, color: '#ff6b6b', lineHeight: 1.4 }}>{authError}</p>}
        {authInfo && <p style={{ margin: 0, fontSize: 12, color: C.greenTextLight, lineHeight: 1.4 }}>{authInfo}</p>}
        <button onClick={submit} disabled={authLoading} style={{ ...primaryBtn(46, 15), opacity: authLoading ? 0.6 : 1 }}>{authLoading ? 'Un momento…' : (isRegister ? 'Crear cuenta' : 'Iniciar sesión')}</button>
        <div style={{ height: 1, background: 'rgba(255,255,255,0.12)', margin: '2px 0' }}></div>
        <button onClick={() => this.setState({ authError: null, authInfo: 'Integración con Garmin Connect próximamente.' })} style={ghostBtn(46, 14, C.text)}>Continuar con Garmin Connect</button>
        {isRegister ? (
          <p style={{ textAlign: 'center', fontSize: 12, color: C.textSoft, margin: 0 }}>¿Ya tienes cuenta? <a href="#" onClick={e => { e.preventDefault(); this.setState({ authMode: 'login', authError: null, authInfo: null }); }}>Inicia sesión</a></p>
        ) : (
          <p style={{ textAlign: 'center', fontSize: 12, color: C.textSoft, margin: 0 }}>¿Nuevo aquí? <a href="#" onClick={e => { e.preventDefault(); this.setState({ authMode: 'register', authError: null, authInfo: null }); }}>Crea tu cuenta</a></p>
        )}
      </div>
    );
  }

  renderIntake() {
    const { intake, intakeSaving, intakeError } = this.state;
    const setField = (field) => (e) => this.setState(s => ({ intake: { ...s.intake, [field]: e.target.value } }));
    const toggleDay = (key) => () => this.setState(s => ({
      intake: { ...s.intake, days: s.intake.days.includes(key) ? s.intake.days.filter(d => d !== key) : [...s.intake.days, key] },
    }));
    const submit = async () => {
      const { userId } = this.state;
      if (!userId) {
        this.setState({ intakeError: 'Sesión no válida, inicia sesión de nuevo.' });
        return;
      }
      this.setState({ intakeSaving: true, intakeError: null });
      const row = {
        user_id: userId,
        nombre: intake.fullName.trim(),
        contacto: intake.contact.trim(),
        edad: intake.age.trim(),
        anos_entrenando: intake.years.trim(),
        nivel: intake.level,
        modalidad: intake.modality,
        dias_disponibles: intake.days,
        disponibilidad_semana: intake.schedule.trim(),
        disponibilidad_finde: intake.weeklyKm.trim(),
        ftp: intake.ftp.trim(),
        condiciones_salud: intake.health.trim(),
        acepta_responsabilidad: intake.priorPlan === 'Acepto',
      };
      const { data, error } = await window.sb.from('perfil')
        .upsert(row, { onConflict: 'user_id' })
        .select()
        .maybeSingle();
      if (error) {
        this.setState({ intakeSaving: false, intakeError: error.message });
        return;
      }
      this.setState({ intakeSaving: false, perfil: data, introDone: true, stage: 'app', screen: 'intakeInvite' });
    };
    return (
      <React.Fragment>
        <div style={{ padding: '56px 16px 10px', flex: 'none' }}>
          <h1 style={{ ...heading(20), letterSpacing: '-0.015em' }}>Cuéntanos de ti</h1>
          <p style={{ margin: '4px 0 0', fontSize: 12.5, color: C.textSoft, lineHeight: 1.4 }}>Esto ayuda a tu entrenador a armar tu plan. Toma 2 minutos.</p>
        </div>
        <div style={{ flex: 1, overflow: 'auto', padding: '12px 16px 16px', display: 'flex', flexDirection: 'column', gap: 12 }}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <label style={labelStyle}>Nombre completo</label>
            <input value={intake.fullName} onChange={setField('fullName')} placeholder="Tu nombre" style={smallInputStyle} />
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <label style={labelStyle}>Contacto (WhatsApp o email)</label>
            <input value={intake.contact} onChange={setField('contact')} placeholder="+52 55 0000 0000 o correo" style={smallInputStyle} />
          </div>
          <div style={{ display: 'flex', gap: 10 }}>
            <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 6 }}>
              <label style={labelStyle}>Edad (opcional)</label>
              <input value={intake.age} onChange={setField('age')} placeholder="Ej. 28" style={{ ...smallInputStyle, width: '100%', boxSizing: 'border-box' }} />
            </div>
            <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 6 }}>
              <label style={labelStyle}>Años entrenando</label>
              <input value={intake.years} onChange={setField('years')} placeholder="Ej. 2" style={{ ...smallInputStyle, width: '100%', boxSizing: 'border-box' }} />
            </div>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <label style={labelStyle}>Nivel autopercibido</label>
            <select value={intake.level} onChange={setField('level')} style={smallInputStyle}>
              <option value="">Selecciona…</option>
              <option value="Principiante">Principiante</option>
              <option value="Intermedio">Intermedio</option>
              <option value="Avanzado">Avanzado</option>
            </select>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <label style={labelStyle}>Modalidad</label>
            <select value={intake.modality} onChange={setField('modality')} style={smallInputStyle}>
              <option value="">Selecciona…</option>
              <option value="MTB">MTB</option>
              <option value="Ruta">Ruta</option>
              <option value="Ambas">Ambas</option>
            </select>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <label style={labelStyle}>Días disponibles para entrenar</label>
            <div style={{ display: 'flex', gap: 6 }}>
              {WEEK_LABELS.map(key => {
                const selected = intake.days.includes(key);
                return (
                  <div key={key} onClick={toggleDay(key)} style={{
                    width: 34, height: 34, borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center',
                    fontSize: 12.5, cursor: 'pointer',
                    border: `1px solid ${selected ? C.accent : 'rgba(255,255,255,0.18)'}`,
                    color: selected ? C.accent : C.textMuted,
                  }}>{key}</div>
                );
              })}
            </div>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <label style={labelStyle}>Tiempo disponible de lunes a viernes</label>
            <input value={intake.schedule} onChange={setField('schedule')} placeholder="Ej. 1 hora en las tardes" style={smallInputStyle} />
          </div>
          <div style={{ display: 'flex', gap: 10 }}>
            <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 6 }}>
              <label style={labelStyle}>Tiempo disponible fin de semana</label>
              <input value={intake.weeklyKm} onChange={setField('weeklyKm')} placeholder="Ej. 2-3 horas" style={{ ...smallInputStyle, width: '100%', boxSizing: 'border-box' }} />
            </div>
            <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 6 }}>
              <label style={labelStyle}>FTP o potencia (opcional)</label>
              <input value={intake.ftp} onChange={setField('ftp')} placeholder="Ej. 180 W" style={{ ...smallInputStyle, width: '100%', boxSizing: 'border-box' }} />
            </div>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <label style={labelStyle}>Lesiones o condiciones de salud</label>
            <textarea value={intake.health} onChange={setField('health')} placeholder="Ej. ninguna, o detalla" style={{ ...smallInputStyle, minHeight: 56, resize: 'none', fontFamily: 'Inter, sans-serif' }}></textarea>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8, borderRadius: 10, background: C.card, padding: 12 }}>
            <label style={{ fontSize: 12, color: C.textSoft, fontWeight: 500 }}>Liberación de responsabilidad</label>
            <p style={{ margin: 0, fontSize: 12, color: C.textMuted, lineHeight: 1.5 }}>Declaro que la información entregada es correcta y que participo en las actividades y planes de entrenamiento de Bortec Cycling bajo mi propia responsabilidad, liberando a Bortec Cycling de cualquier lesión o daño derivado de mi práctica del ciclismo.</p>
            <div style={{ display: 'inline-flex', overflow: 'hidden', border: `1px solid ${C.border}`, borderRadius: 8, alignSelf: 'flex-start' }}>
              {['Acepto', 'No acepto'].map(label => {
                const active = intake.priorPlan === label;
                return (
                  <div key={label} onClick={() => this.setState(s => ({ intake: { ...s.intake, priorPlan: label } }))} style={{
                    padding: '7px 16px', fontSize: 12.5, cursor: 'pointer',
                    color: active ? C.accent : C.text,
                    boxShadow: active ? `inset 0 0 0 1px ${C.accent}` : 'none',
                  }}>{label}</div>
                );
              })}
            </div>
          </div>
        </div>
        <div style={{ flex: 'none', padding: '10px 16px 24px', display: 'flex', flexDirection: 'column', gap: 8 }}>
          {intakeError && <p style={{ margin: 0, fontSize: 12, color: '#ff6b6b', lineHeight: 1.4 }}>{intakeError}</p>}
          <button onClick={submit} disabled={intakeSaving} style={{ ...primaryBtn(46, 15), width: '100%', opacity: intakeSaving ? 0.6 : 1 }}>{intakeSaving ? 'Guardando…' : 'Continuar'}</button>
        </div>
      </React.Fragment>
    );
  }

  renderSidebar() {
    const { screen } = this.state;
    const itemStyle = (active) => ({
      display: 'flex', alignItems: 'center', gap: 10, padding: '10px 10px', borderRadius: 8, cursor: 'pointer',
      background: active ? 'rgba(55,204,80,0.12)' : 'transparent',
    });
    const color = (active) => active ? C.accent : C.textSoft;
    const item = (key, label, onClick) => (
      <div onClick={onClick} style={itemStyle(screen === key)}>
        {NAV_ICONS[key](color(screen === key))}
        <span style={{ fontSize: 13.5, color: color(screen === key) }}>{label}</span>
      </div>
    );
    return (
      <div style={{ display: 'flex', flexDirection: 'column', width: 220, flex: 'none', padding: '28px 16px', gap: 4, borderRight: '1px solid rgba(255,255,255,0.08)', height: '100%', overflow: 'auto', background: '#161616' }}>
        <img src="assets/bortec-logo.png" alt="Bortec Cycling" style={{ height: 22, width: 'auto', alignSelf: 'flex-start', flex: 'none', objectFit: 'contain', margin: '0 0 22px 4px' }} />
        {item('inicio', 'Inicio', () => this.go('inicio'))}
        {item('plan', 'Plan', () => this.go('plan'))}
        {item('historial', 'Progreso', () => this.go('historial'))}
        {item('chat', 'Asistente', () => this.go('chat'))}
        {item('carreras', 'Carreras', () => this.go('carreras'))}
        <div style={{ flex: 1 }}></div>
        <div onClick={() => this.go('profile')} style={itemStyle(screen === 'profile')}>
          <div style={{ width: 22, height: 22, borderRadius: '50%', background: C.card, border: `1px solid ${C.borderLight}`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, color: C.text, flex: 'none' }}>{(this.props.userName || '?').trim().charAt(0).toUpperCase() || '?'}</div>
          <span style={{ fontSize: 13.5, color: C.text }}>Perfil</span>
        </div>
      </div>
    );
  }

  renderHeader() {
    const { screen, chatResponsesUsed } = this.state;
    const { userName } = this.props;
    const isDesktop = this.state.isDesktop;
    if (!TAB_SCREENS.includes(screen)) return null;
    const chatAvailable = chatResponsesUsed < CHAT_LIMIT;
    return (
      <div style={{ padding: isDesktop ? '32px 32px 14px' : '56px 16px 14px', flex: 'none' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
          <img src="assets/bortec-logo.png" alt="Bortec Cycling" style={{ height: 14, width: 'auto', opacity: 0.92, display: isDesktop ? 'none' : 'block' }} />
          <div onClick={() => this.go('profile')} style={{ width: 26, height: 26, borderRadius: '50%', background: C.card, border: `1px solid ${C.borderLight}`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, color: C.text, cursor: 'pointer' }}>{(userName || '?').trim().charAt(0).toUpperCase() || '?'}</div>
        </div>
        {screen === 'inicio' && (
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
            <div>
              <div style={{ fontSize: 11, letterSpacing: '0.06em', color: C.textSoft, textTransform: 'uppercase' }}>Hola, {userName}</div>
              <h1 style={heading(22, '2px 0 0')}>Tu semana</h1>
            </div>
            <div style={badge(C.greenBg, C.greenText)}>Racha: 4 días</div>
          </div>
        )}
        {screen === 'plan' && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, width: '100%' }}>
            <svg width="20" height="20" viewBox="0 0 20 20" onClick={() => this.go('inicio')} style={{ cursor: 'pointer', flex: 'none' }}>
              <polyline points="13,4 6,10 13,16" fill="none" stroke="#f2f2f2" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"></polyline>
            </svg>
            <div>
              <div style={{ fontSize: 11, letterSpacing: '0.06em', color: C.textSoft, textTransform: 'uppercase' }}>Plan de hoy</div>
              <h1 style={heading(20, '2px 0 0')}>Resistencia base</h1>
            </div>
          </div>
        )}
        {screen === 'historial' && (
          <div>
            <div style={{ fontSize: 11, letterSpacing: '0.06em', color: C.textSoft, textTransform: 'uppercase' }}>Progreso</div>
            <h1 style={heading(22, '2px 0 0')}>Tu evolución</h1>
          </div>
        )}
        {screen === 'chat' && (
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <div style={{ fontSize: 11, letterSpacing: '0.06em', color: C.textSoft, textTransform: 'uppercase' }}>Asistente</div>
              {chatAvailable && <span style={{ fontSize: 9.5, padding: '1px 7px', borderRadius: 5, background: C.greenBg, color: C.greenTextLight }}>{Math.max(CHAT_LIMIT - chatResponsesUsed, 0)}/{CHAT_LIMIT} respuestas</span>}
            </div>
            <h1 style={heading(22, '2px 0 0')}>Resuelve tus dudas</h1>
          </div>
        )}
        {screen === 'carreras' && (
          <div>
            <div style={{ fontSize: 11, letterSpacing: '0.06em', color: C.textSoft, textTransform: 'uppercase' }}>Carreras</div>
            <h1 style={heading(22, '2px 0 0')}>Tus objetivos</h1>
          </div>
        )}
      </div>
    );
  }

  renderInicio() {
    const { weeklySessionGoal, showPowerMetric } = this.props;
    const { hasCoachNotification } = this.state;
    const sessionsDone = 3;
    const weekDays = WEEK_LABELS.map((label, i) => {
      const t = WEEK_TYPES[i];
      const isToday = t === 'today';
      return {
        label,
        bg: isToday ? '#1a2e1e' : 'transparent',
        labelColor: isToday ? C.greenTextLight : C.textSoft,
        dotColor: t === 'done' ? C.accent : (t === 'today' || t === 'plan') ? '#2a8f3c' : 'transparent',
      };
    });
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 16, padding: '0 16px 24px' }}>
        {hasCoachNotification && (
          <div onClick={() => { this.setState({ hasCoachNotification: false }); this.go('historial'); }} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px', borderRadius: 10, background: C.greenBg, cursor: 'pointer' }}>
            <div style={{ width: 7, height: 7, borderRadius: '50%', background: C.accent, flex: 'none' }}></div>
            <div style={{ flex: 1, fontSize: 12.5, color: C.greenText, lineHeight: 1.4 }}>Ana (tu entrenador) comentó tu última sesión</div>
            <span style={{ fontSize: 11, color: C.greenTextLight }}>Ver</span>
          </div>
        )}

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 10 }}>
          <div style={statCard}>
            <span style={{ fontSize: 10, color: C.textSoft, letterSpacing: '0.04em' }}>FC reposo</span>
            <span style={{ fontSize: 19, fontWeight: 500, color: C.text }}>58 <span style={{ fontSize: 11, color: C.textSoft, fontWeight: 400 }}>bpm</span></span>
          </div>
          {showPowerMetric && (
            <div style={statCard}>
              <span style={{ fontSize: 10, color: C.textSoft, letterSpacing: '0.04em' }}>Potencia prom.</span>
              <span style={{ fontSize: 19, fontWeight: 500, color: C.text }}>142 <span style={{ fontSize: 11, color: C.textSoft, fontWeight: 400 }}>W</span></span>
            </div>
          )}
          {!showPowerMetric && (
            <div style={statCard}>
              <span style={{ fontSize: 10, color: C.textSoft, letterSpacing: '0.04em' }}>Esfuerzo (RPE)</span>
              <span style={{ fontSize: 19, fontWeight: 500, color: C.text }}>4<span style={{ fontSize: 11, color: C.textSoft, fontWeight: 400 }}>/10</span></span>
            </div>
          )}
          <div style={statCard}>
            <span style={{ fontSize: 10, color: C.textSoft, letterSpacing: '0.04em' }}>Sesiones</span>
            <span style={{ fontSize: 19, fontWeight: 500, color: C.text }}>{sessionsDone}<span style={{ fontSize: 11, color: C.textSoft, fontWeight: 400 }}>/{weeklySessionGoal}</span></span>
          </div>
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontSize: 13, fontWeight: 500, color: C.text }}>Esta semana</span>
            <a href="#" onClick={e => { e.preventDefault(); this.go('historial'); }} style={{ fontSize: 12, color: C.accent, textDecoration: 'none' }}>Ver todo</a>
          </div>
          <div style={{ display: 'flex', gap: 6 }}>
            {weekDays.map((d, i) => (
              <div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, padding: '8px 0', borderRadius: 8, background: d.bg }}>
                <span style={{ fontSize: 10, color: d.labelColor }}>{d.label}</span>
                <div style={{ width: 7, height: 7, borderRadius: '50%', background: d.dotColor }}></div>
              </div>
            ))}
          </div>
        </div>

        <div style={{ borderRadius: 10, border: `1px solid ${C.border}`, padding: '12px 14px', fontSize: 12.5, color: C.textMuted, lineHeight: 1.5 }}>
          Vas por buen camino. Cuatro semanas seguidas entrenando — la constancia es lo que construye la forma física, no la intensidad.
        </div>
      </div>
    );
  }

  renderPlan() {
    const intensityRaw = [15, 20, 60, 25, 65, 25, 65, 25, 60, 20, 15, 10];
    const intensityBars = intensityRaw.map(v => ({ height: v + '%', color: v > 45 ? C.accent : C.greenBg }));
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 16, padding: '0 16px 24px' }}>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <span style={badge(C.cardAlt, C.text)}>Principiante</span>
          <span style={badge('transparent', C.accent, `1px solid ${C.accent}`)}>45 min</span>
          <span style={badge(C.greenBg, C.greenText)}>Zona FC 2 · 120–140 bpm</span>
        </div>

        <p style={{ margin: 0, fontSize: 13, color: C.textMuted, lineHeight: 1.5 }}>Entrenamiento de resistencia base. Mantén un ritmo donde puedas conversar sin ahogarte — ese es el punto.</p>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <span style={{ fontSize: 12, color: C.textSoft, letterSpacing: '0.04em' }}>Perfil de intensidad</span>
          <div style={{ display: 'flex', alignItems: 'flex-end', gap: 3, height: 70, background: C.panel, borderRadius: 8, padding: '8px 10px' }}>
            {intensityBars.map((b, i) => (
              <div key={i} style={{ flex: 1, borderRadius: 2, background: b.color, height: b.height }}></div>
            ))}
          </div>
        </div>

        <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
          <thead>
            <tr>
              <th style={tableHeadCell}>Fase</th>
              <th style={tableHeadCell}>Dur.</th>
              <th style={tableHeadCell}>Zona</th>
              <th style={tableHeadCell}>RPE</th>
            </tr>
          </thead>
          <tbody>
            {PLAN_PHASES.map((p, i) => (
              <tr key={i}>
                <td style={{ ...tableCell, color: C.text }}>{p.name}</td>
                <td style={{ ...tableCell, color: C.textMuted }}>{p.duration}</td>
                <td style={{ ...tableCell, color: C.textMuted }}>{p.zone}</td>
                <td style={{ ...tableCell, color: C.textMuted }}>{p.rpe}</td>
              </tr>
            ))}
          </tbody>
        </table>

        <div style={{ borderRadius: 10, border: `1px solid ${C.border}`, padding: '12px 14px', fontSize: 12.5, color: C.textMuted, lineHeight: 1.5 }}>
          Consejo: si en el bloque principal no puedes mantener la respiración controlada, baja un poco el ritmo — el progreso viene de repetir, no de forzar.
        </div>

        <button onClick={() => this.go('liveSession', { liveElapsed: 0, livePaused: false, livePhaseIdx: 0 })} style={primaryBtn(46, 15)}>Comenzar entrenamiento</button>
      </div>
    );
  }

  renderHistorial() {
    const { range, rangeOffset, userPlan, marcas } = this.state;
    const isPremium = userPlan === 'premium';
    const rangeOptions = [
      { key: 'semana', label: 'Semana' },
      { key: 'mes', label: 'Mes' },
      { key: 'anio', label: 'Año' },
    ].map(o => ({ label: o.label, key: o.key, active: range === o.key }));

    const todayDate = new Date();
    let periodLabel = '', periodBars = [];
    const fmtDay = (d) => d.getDate() + ' ' + MONTH_SHORT[d.getMonth()].toLowerCase();

    if (range === 'semana') {
      const monday = this.getMonday(todayDate);
      monday.setDate(monday.getDate() - rangeOffset * 7);
      const sunday = new Date(monday); sunday.setDate(monday.getDate() + 6);
      periodLabel = fmtDay(monday) + ' – ' + fmtDay(sunday) + (rangeOffset === 0 ? ' · esta semana' : '');
      periodBars = WEEK_LABELS.map((lbl, i) => {
        const dayDate = new Date(monday); dayDate.setDate(monday.getDate() + i);
        const isFuture = dayDate > todayDate;
        const seed = monday.getTime() / 86400000 + i * 7.3;
        const val = isFuture ? 0 : Math.round(20 + this.seededRandom(seed) * 70);
        const isToday = dayDate.toDateString() === todayDate.toDateString();
        return { label: lbl, labelColor: isToday ? C.greenTextLight : C.textSoft, height: Math.max(val, 4) + '%', color: isToday ? C.accent : C.greenBg };
      });
    } else if (range === 'mes') {
      const monthDate = new Date(todayDate.getFullYear(), todayDate.getMonth() - rangeOffset, 1);
      periodLabel = MONTH_NAMES[monthDate.getMonth()] + ' ' + monthDate.getFullYear();
      const daysInMonth = new Date(monthDate.getFullYear(), monthDate.getMonth() + 1, 0).getDate();
      const weeksCount = Math.ceil(daysInMonth / 7);
      periodBars = Array.from({ length: weeksCount }, (_, i) => {
        const seed = monthDate.getTime() / 2629800000 + i * 3.7;
        const val = Math.round(25 + this.seededRandom(seed) * 65);
        const isCurrentWeek = rangeOffset === 0 && i === Math.floor((todayDate.getDate() - 1) / 7);
        return { label: 'S' + (i + 1), labelColor: isCurrentWeek ? C.greenTextLight : C.textSoft, height: val + '%', color: isCurrentWeek ? C.accent : C.greenBg };
      });
    } else {
      const year = todayDate.getFullYear() - rangeOffset;
      periodLabel = String(year);
      periodBars = MONTH_SHORT.map((m, i) => {
        const isFuture = rangeOffset === 0 && i > todayDate.getMonth();
        const seed = year * 12 + i * 1.9;
        const val = isFuture ? 0 : Math.round(20 + this.seededRandom(seed) * 70);
        const isCurrent = rangeOffset === 0 && i === todayDate.getMonth();
        return { label: m, labelColor: isCurrent ? C.greenTextLight : C.textSoft, height: Math.max(val, 4) + '%', color: isCurrent ? C.accent : C.greenBg };
      });
    }
    const canGoNext = rangeOffset > 0;

    const coachComments = [
      { session: 'Resistencia base', date: '3 ago', text: 'Buen ritmo constante, mantuviste la Zona 2 casi toda la sesión. Sigue así.' },
      { session: 'Recuperación activa', date: '1 ago', text: 'Notaste bien la diferencia entre esfuerzo suave y moderado — eso es justo lo que buscamos esta semana.' },
    ];

    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 16, padding: '0 16px 24px' }}>
        <div style={{ display: 'inline-flex', overflow: 'hidden', border: `1px solid ${C.border}`, borderRadius: 8, alignSelf: 'flex-start' }}>
          {rangeOptions.map(r => (
            <div key={r.key} onClick={() => this.setState({ range: r.key, rangeOffset: 0 })} style={{
              padding: '7px 14px', fontSize: 12.5, cursor: 'pointer',
              color: r.active ? C.accent : C.text,
              boxShadow: r.active ? `inset 0 0 0 1px ${C.accent}` : 'none',
            }}>{r.label}</div>
          ))}
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10 }}>
            <svg width="16" height="16" viewBox="0 0 20 20" onClick={() => this.setState(s => ({ rangeOffset: (s.rangeOffset || 0) + 1 }))} style={{ cursor: 'pointer', flex: 'none' }}>
              <polyline points="13,4 6,10 13,16" fill="none" stroke="#a8a8a8" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"></polyline>
            </svg>
            <span style={{ fontSize: 13, fontWeight: 500, color: C.text }}>{periodLabel}</span>
            <svg width="16" height="16" viewBox="0 0 20 20" onClick={() => { if (canGoNext) this.setState(s => ({ rangeOffset: Math.max((s.rangeOffset || 0) - 1, 0) })); }} style={{ cursor: canGoNext ? 'pointer' : 'default', flex: 'none', opacity: canGoNext ? 1 : 0.3 }}>
              <polyline points="7,4 14,10 7,16" fill="none" stroke="#a8a8a8" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"></polyline>
            </svg>
          </div>
          <div style={{ display: 'flex', alignItems: 'flex-end', gap: 8, height: 100, background: C.card, borderRadius: 8, padding: '12px 14px' }}>
            {periodBars.map((h, i) => (
              <div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, height: '100%', justifyContent: 'flex-end' }}>
                <div style={{ width: '100%', borderRadius: 3, background: h.color, height: h.height }}></div>
                <span style={{ fontSize: 10, color: h.labelColor }}>{h.label}</span>
              </div>
            ))}
          </div>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 10 }}>
          <div style={statCard}>
            <span style={{ fontSize: 10, color: C.textSoft, letterSpacing: '0.04em' }}>FC reposo (30d)</span>
            <span style={{ fontSize: 17, fontWeight: 500, color: C.text }}>58 <span style={{ fontSize: 11, color: C.greenTextLight, fontWeight: 400 }}>▼2</span></span>
          </div>
          <div style={statCard}>
            <span style={{ fontSize: 10, color: C.textSoft, letterSpacing: '0.04em' }}>Distancia total</span>
            <span style={{ fontSize: 17, fontWeight: 500, color: C.text }}>128 <span style={{ fontSize: 11, color: C.textSoft, fontWeight: 400 }}>km</span></span>
          </div>
          <div style={statCard}>
            <span style={{ fontSize: 10, color: C.textSoft, letterSpacing: '0.04em' }}>Racha actual</span>
            <span style={{ fontSize: 17, fontWeight: 500, color: C.text }}>4 <span style={{ fontSize: 11, color: C.textSoft, fontWeight: 400 }}>sem.</span></span>
          </div>
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontSize: 13, fontWeight: 500, color: C.text }}>Comentarios de tu entrenador</span>
            {isPremium && <span style={{ fontSize: 10, padding: '2px 8px', borderRadius: 5, background: C.greenBg, color: C.greenTextLight }}>Premium</span>}
          </div>
          {isPremium && coachComments.map((c, i) => (
            <div key={i} style={sectionCard}>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                <span style={{ fontSize: 12, fontWeight: 500, color: C.text }}>{c.session}</span>
                <span style={{ fontSize: 11, color: C.textSoft }}>{c.date}</span>
              </div>
              <p style={{ margin: 0, fontSize: 12.5, color: C.textMuted, lineHeight: 1.45 }}>{c.text}</p>
              <span style={{ fontSize: 10.5, color: C.greenTextLight }}>— Ana, entrenadora</span>
            </div>
          ))}
          {!isPremium && (
            <div style={{ borderRadius: 10, border: '1px dashed rgba(255,255,255,0.18)', padding: 16, display: 'flex', flexDirection: 'column', gap: 8, alignItems: 'flex-start' }}>
              <span style={{ fontSize: 12.5, color: C.textMuted, lineHeight: 1.5 }}>Los comentarios personalizados de tu entrenador sobre cada sesión están disponibles en Premium.</span>
              <button onClick={() => this.go('profile')} style={primaryBtn(34, 12.5)}>Mejorar a Premium</button>
            </div>
          )}
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <span style={{ fontSize: 13, fontWeight: 500, color: C.text }}>Tus marcas recientes</span>
          {marcas.length === 0 && (
            <div style={{ borderRadius: 10, border: '1px dashed rgba(255,255,255,0.18)', padding: 14, fontSize: 12, color: C.textSoft }}>Aún no hay marcas registradas. Cuéntaselas al Asistente en el chat.</div>
          )}
          {marcas.length > 0 && (
            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12.5 }}>
              <thead>
                <tr>
                  <th style={{ ...tableHeadCell, fontSize: 10 }}>Fecha</th>
                  <th style={{ ...tableHeadCell, fontSize: 10 }}>Tipo</th>
                  <th style={{ ...tableHeadCell, fontSize: 10 }}>Valor</th>
                </tr>
              </thead>
              <tbody>
                {marcas.slice(0, 8).map((m) => (
                  <tr key={m.id}>
                    <td style={{ ...tableCell, color: C.text }}>{this.formatFecha(m.fecha)}</td>
                    <td style={{ ...tableCell, color: C.textMuted, textTransform: 'capitalize' }}>{m.tipo}</td>
                    <td style={{ ...tableCell, color: C.textMuted }}>{m.valor}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      </div>
    );
  }

  renderChat() {
    const { chatMessages, chatInput, pendingMark, chatResponsesUsed } = this.state;
    const chatAvailable = chatResponsesUsed < CHAT_LIMIT;
    if (!chatAvailable) {
      return (
        <div style={{ height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 10, padding: '0 32px', textAlign: 'center' }}>
          <span style={{ fontSize: 15, fontWeight: 500, color: C.text }}>Llegaste al límite de {CHAT_LIMIT} respuestas</span>
          <p style={{ margin: 0, fontSize: 12.5, color: C.textSoft, lineHeight: 1.5 }}>Mejora a Premium para preguntas ilimitadas al Asistente y a tu entrenador.</p>
          <button onClick={() => this.go('profile')} style={{ ...primaryBtn(38, 13), marginTop: 6, padding: '0 16px' }}>Mejorar a Premium</button>
        </div>
      );
    }
    const displayMessages = chatMessages.map(m => ({
      isSystem: m.from === 'system',
      isBubble: m.from !== 'system',
      justify: m.from === 'user' ? 'flex-end' : 'flex-start',
      align: m.from === 'user' ? 'flex-end' : 'flex-start',
      bg: m.from === 'user' ? C.accent : m.from === 'coach' ? C.greenBg : C.card,
      color: m.from === 'user' ? '#0a0a0a' : m.from === 'coach' ? C.greenText : C.text,
      label: m.label || '',
      text: m.text,
    }));
    const quickQuestions = ['¿Qué es Zona 2?', '¿Puedo entrenar con dolor?', '¿Cómo ajusto el plan?'];
    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
        <div style={{ flex: 1, overflow: 'auto', padding: '0 16px', display: 'flex', flexDirection: 'column', gap: 10 }}>
          {displayMessages.map((m, i) => (
            <React.Fragment key={i}>
              {m.isSystem && <div style={{ textAlign: 'center', fontSize: 11, color: C.greenTextLight, padding: '2px 0' }}>{m.text}</div>}
              {m.isBubble && (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 2, alignItems: m.align }}>
                  {m.label && <span style={{ fontSize: 10, color: C.textSoft, padding: '0 4px' }}>{m.label}</span>}
                  <div style={{ display: 'flex', justifyContent: m.justify }}>
                    <div style={{ maxWidth: '78%', padding: '9px 12px', borderRadius: 12, fontSize: 13, lineHeight: 1.45, background: m.bg, color: m.color }}>{m.text}</div>
                  </div>
                </div>
              )}
            </React.Fragment>
          ))}
        </div>
        {pendingMark && (
          <div style={{ flex: 'none', padding: '10px 16px 4px', display: 'flex', gap: 8 }}>
            <div onClick={() => this.confirmMark(true)} style={{ flex: 'none', fontSize: 12, padding: '6px 14px', borderRadius: 999, border: `1px solid ${C.accent}`, color: C.accent, cursor: 'pointer' }}>Sí, guardar</div>
            <div onClick={() => this.confirmMark(false)} style={{ flex: 'none', fontSize: 12, padding: '6px 14px', borderRadius: 999, border: `1px solid ${C.borderLight}`, color: C.textMuted, cursor: 'pointer' }}>No, gracias</div>
          </div>
        )}
        {!pendingMark && (
          <div style={{ flex: 'none', padding: '10px 16px 4px', display: 'flex', gap: 8, overflow: 'auto' }}>
            {quickQuestions.map((q, i) => (
              <div key={i} onClick={() => this.sendText(q)} style={{ flex: 'none', whiteSpace: 'nowrap', fontSize: 12, padding: '6px 12px', borderRadius: 999, border: `1px solid ${C.borderLight}`, color: C.textMuted, cursor: 'pointer' }}>{q}</div>
            ))}
          </div>
        )}
        <div style={{ flex: 'none', padding: '8px 16px 24px', display: 'flex', gap: 8, alignItems: 'center' }}>
          <input value={chatInput} onChange={e => this.setState({ chatInput: e.target.value })} onKeyDown={e => { if (e.key === 'Enter') this.sendText(chatInput); }} placeholder="Escribe tu pregunta…" style={{ flex: 1, minHeight: 40, padding: '8px 14px', fontSize: 13.5, color: C.text, background: C.card, border: `1px solid ${C.border}`, borderRadius: 20, outline: 'none' }} />
          <div onClick={() => this.sendText(chatInput)} style={{ flex: 'none', width: 40, height: 40, borderRadius: '50%', background: C.accent, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }}>
            <svg width="16" height="16" viewBox="0 0 16 16">
              <polygon points="2,2 14,8 2,14 5,8" fill="#0a0a0a"></polygon>
            </svg>
          </div>
        </div>
      </div>
    );
  }

  renderCarreras() {
    const { racesTab, raceGoals, addRaceOpen, newRaceName, newRaceDate, newRaceDistance, suggestedRacesMarked } = this.state;
    const raceTabOptions = [
      { key: 'mios', label: 'Mis objetivos' },
      { key: 'sugeridas', label: 'Sugeridas' },
    ];
    const suggestedRaces = SUGGESTED_RACES.filter(r => !suggestedRacesMarked[r.id]);
    const submitNewRace = () => {
      if (!newRaceName.trim()) return;
      this.setState(s => ({
        raceGoals: [...s.raceGoals, { name: newRaceName.trim(), date: newRaceDate.trim() || 'Sin fecha', distance: newRaceDistance.trim() || '—' }],
        addRaceOpen: false, newRaceName: '', newRaceDate: '', newRaceDistance: '',
      }));
    };
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 16, padding: '0 16px 24px' }}>
        <div style={{ display: 'inline-flex', overflow: 'hidden', border: `1px solid ${C.border}`, borderRadius: 8, alignSelf: 'flex-start' }}>
          {raceTabOptions.map(rt => (
            <div key={rt.key} onClick={() => this.setState({ racesTab: rt.key })} style={{
              padding: '7px 14px', fontSize: 12.5, cursor: 'pointer',
              color: racesTab === rt.key ? C.accent : C.text,
              boxShadow: racesTab === rt.key ? `inset 0 0 0 1px ${C.accent}` : 'none',
            }}>{rt.label}</div>
          ))}
        </div>

        {racesTab === 'mios' && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            {raceGoals.length === 0 && (
              <div style={{ borderRadius: 10, border: '1px dashed rgba(255,255,255,0.18)', padding: 20, textAlign: 'center', display: 'flex', flexDirection: 'column', gap: 4 }}>
                <span style={{ fontSize: 13, color: C.text }}>Aún no tienes carreras objetivo</span>
                <span style={{ fontSize: 12, color: C.textSoft }}>Añade una o mira las que sugiere tu entrenador.</span>
              </div>
            )}
            {raceGoals.map((g, i) => (
              <div key={i} style={sectionCard}>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                  <span style={{ fontSize: 14, fontWeight: 500, color: C.text }}>{g.name}</span>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span style={{ display: 'inline-flex', fontSize: 10.5, padding: '2px 8px', borderRadius: 5, border: `1px solid ${C.accent}`, color: C.accent }}>Objetivo</span>
                    <span onClick={() => this.setState(s => ({ raceGoals: s.raceGoals.filter((_, j) => j !== i) }))} style={{ cursor: 'pointer', color: C.textSoft, fontSize: 13 }}>×</span>
                  </div>
                </div>
                <span style={{ fontSize: 12, color: C.textSoft }}>{g.date} · {g.distance}</span>
              </div>
            ))}

            {addRaceOpen && (
              <div style={sectionCard}>
                <input value={newRaceName} onChange={e => this.setState({ newRaceName: e.target.value })} placeholder="Nombre de la carrera" style={{ minHeight: 36, padding: '6px 10px', fontSize: 13, color: C.text, background: '#121212', border: `1px solid ${C.border}`, borderRadius: 8, outline: 'none' }} />
                <div style={{ display: 'flex', gap: 8 }}>
                  <input value={newRaceDate} onChange={e => this.setState({ newRaceDate: e.target.value })} placeholder="Fecha" style={{ flex: 1, minHeight: 36, padding: '6px 10px', fontSize: 13, color: C.text, background: '#121212', border: `1px solid ${C.border}`, borderRadius: 8, outline: 'none' }} />
                  <input value={newRaceDistance} onChange={e => this.setState({ newRaceDistance: e.target.value })} placeholder="Distancia" style={{ flex: 1, minHeight: 36, padding: '6px 10px', fontSize: 13, color: C.text, background: '#121212', border: `1px solid ${C.border}`, borderRadius: 8, outline: 'none' }} />
                </div>
                <button onClick={submitNewRace} style={primaryBtn(38, 13)}>Guardar carrera</button>
              </div>
            )}
            {!addRaceOpen && (
              <button onClick={() => this.setState({ addRaceOpen: true })} style={ghostBtn(42, 13)}>+ Añadir carrera objetivo</button>
            )}
          </div>
        )}

        {racesTab === 'sugeridas' && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            <p style={{ margin: 0, fontSize: 12.5, color: C.textSoft, lineHeight: 1.5 }}>Carreras que tu entrenador conoce y recomienda para tu nivel.</p>
            {suggestedRaces.map(s => (
              <div key={s.id} style={sectionCard}>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                  <span style={{ fontSize: 14, fontWeight: 500, color: C.text }}>{s.name}</span>
                  <span style={{ fontSize: 11, color: C.textSoft }}>{s.date}</span>
                </div>
                <span style={{ fontSize: 12, color: C.textSoft }}>{s.location} · {s.distance}</span>
                <p style={{ margin: '2px 0 0', fontSize: 12, color: C.textMuted, lineHeight: 1.4 }}>{s.note}</p>
                <button onClick={() => {
                  if (this.state.suggestedRacesMarked[s.id]) return;
                  this.setState(prev => ({
                    suggestedRacesMarked: { ...prev.suggestedRacesMarked, [s.id]: true },
                    raceGoals: [...prev.raceGoals, { name: s.name, date: s.date, distance: s.distance }],
                  }));
                }} style={{ ...primaryBtn(32, 12), alignSelf: 'flex-start', marginTop: 2, padding: '0 12px' }}>Marcar como objetivo</button>
              </div>
            ))}
          </div>
        )}
      </div>
    );
  }

  renderProfile() {
    const { userName, userEmail } = this.props;
    const { userPlan, marcas, connectedAccounts, pushNotifications, perfil } = this.state;
    const isPremium = userPlan === 'premium';
    const push = pushNotifications;
    const displayName = (perfil && perfil.nombre) || userName;
    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '56px 16px 14px', display: 'flex', alignItems: 'center', gap: 12, flex: 'none' }}>
          <svg width="20" height="20" viewBox="0 0 20 20" onClick={() => this.go('inicio')} style={{ cursor: 'pointer', flex: 'none' }}>
            <polyline points="13,4 6,10 13,16" fill="none" stroke="#f2f2f2" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"></polyline>
          </svg>
          <h1 style={heading(20)}>Perfil</h1>
        </div>
        <div style={{ flex: 1, overflow: 'auto', padding: '0 16px 24px', display: 'flex', flexDirection: 'column', gap: 22 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <div style={{ width: 52, height: 52, borderRadius: '50%', background: C.card, border: `1px solid ${C.borderLight}`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 19, color: C.text }}>{(displayName || '?').trim().charAt(0).toUpperCase() || '?'}</div>
            <div>
              <div style={{ fontSize: 15, fontWeight: 500, color: C.text }}>{displayName}</div>
              <div style={{ fontSize: 12, color: C.textSoft }}>{userEmail}</div>
            </div>
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <span style={{ fontSize: 12, color: C.textSoft, letterSpacing: '0.04em' }}>Tu plan</span>
            <div style={sectionCard}>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                <span style={{ fontSize: 14, fontWeight: 500, color: C.text }}>{isPremium ? 'Premium' : 'Free'}</span>
                {isPremium && <span style={{ fontSize: 10, padding: '2px 8px', borderRadius: 5, background: C.greenBg, color: C.greenTextLight }}>Activo</span>}
              </div>
              <p style={{ margin: 0, fontSize: 12, color: C.textSoft, lineHeight: 1.5 }}>{isPremium ? 'Incluye comentarios personalizados de tu entrenador en cada sesión.' : 'Plan gratuito. Mejora a Premium para recibir comentarios de tu entrenador sobre cada sesión.'}</p>
              <button style={primaryBtn(36, 13)}>Mejorar a Premium</button>
            </div>
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <span style={{ fontSize: 12, color: C.textSoft, letterSpacing: '0.04em' }}>Tus marcas</span>
            {marcas.length === 0 && (
              <div style={{ borderRadius: 10, border: '1px dashed rgba(255,255,255,0.18)', padding: 14, fontSize: 12, color: C.textSoft }}>Aún no tienes marcas guardadas. Cuéntaselas al Asistente en el chat.</div>
            )}
            {marcas.map((m) => (
              <div key={m.id} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderRadius: 10, background: C.card, padding: 12 }}>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
                  <span style={{ fontSize: 13, color: C.text }}>{m.valor}</span>
                  <span style={{ fontSize: 10, color: C.textSoft, textTransform: 'capitalize' }}>{m.tipo}</span>
                </div>
                <span style={{ fontSize: 11, color: C.textSoft }}>{this.formatFecha(m.fecha)}</span>
              </div>
            ))}
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <span style={{ fontSize: 12, color: C.textSoft, letterSpacing: '0.04em' }}>Cuentas conectadas</span>
            {connectedAccounts.map(a => (
              <div key={a.id} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderRadius: 10, background: C.card, padding: 12 }}>
                <span style={{ fontSize: 13.5, color: C.text }}>{a.name}</span>
                <button onClick={() => this.setState(s => ({ connectedAccounts: s.connectedAccounts.map(x => x.id === a.id ? { ...x, connected: !x.connected } : x) }))} style={a.connected ? primaryBtn(30, 12) : ghostBtn(30, 12)}>{a.connected ? 'Conectado' : 'Conectar'}</button>
              </div>
            ))}
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <span style={{ fontSize: 12, color: C.textSoft, letterSpacing: '0.04em' }}>Notificaciones</span>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderRadius: 10, background: C.card, padding: 12 }}>
              <span style={{ fontSize: 13.5, color: C.text }}>Notificaciones push</span>
              <div onClick={() => this.setState(s => ({ pushNotifications: !s.pushNotifications }))} style={{ width: 40, height: 22, borderRadius: 11, background: push ? C.greenBg : '#2e2e2e', padding: 2, cursor: 'pointer', display: 'flex', justifyContent: push ? 'flex-end' : 'flex-start' }}>
                <div style={{ width: 18, height: 18, borderRadius: 9, background: push ? C.accent : '#8a8a8a' }}></div>
              </div>
            </div>
          </div>

          <button onClick={() => this.logout()} style={ghostBtn(44, 13.5, C.textMuted)}>Cerrar sesión</button>
        </div>
      </div>
    );
  }

  renderLiveSession() {
    const { liveElapsed, livePaused, livePhaseIdx } = this.state;
    const mm = String(Math.floor(liveElapsed / 60)).padStart(2, '0');
    const ss = String(liveElapsed % 60).padStart(2, '0');
    const liveTimeLabel = mm + ':' + ss;
    const liveFc = Math.round(128 + 6 * Math.sin(liveElapsed / 3));
    const showNextPhaseBtn = livePhaseIdx < PLAN_PHASES.length - 1;
    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 22, padding: '0 24px' }}>
          <span style={{ fontSize: 12, color: C.textSoft, letterSpacing: '0.08em', textTransform: 'uppercase' }}>{PLAN_PHASES[livePhaseIdx].name}</span>
          <span style={{ fontSize: 50, fontWeight: 500, color: C.text, letterSpacing: '-0.02em' }}>{liveTimeLabel}</span>
          <div style={{ display: 'flex', gap: 28 }}>
            <div style={{ textAlign: 'center' }}><div style={{ fontSize: 22, fontWeight: 500, color: C.accent }}>{liveFc}</div><div style={{ fontSize: 10, color: C.textSoft }}>bpm</div></div>
            <div style={{ textAlign: 'center' }}><div style={{ fontSize: 22, fontWeight: 500, color: C.text }}>Z2</div><div style={{ fontSize: 10, color: C.textSoft }}>zona</div></div>
          </div>
          {showNextPhaseBtn && (
            <button onClick={() => this.setState(s => ({ livePhaseIdx: Math.min(s.livePhaseIdx + 1, PLAN_PHASES.length - 1) }))} style={ghostBtn(36, 13)}>Siguiente fase</button>
          )}
        </div>
        <div style={{ flex: 'none', padding: '16px 24px 28px', display: 'flex', gap: 10 }}>
          <button onClick={() => this.setState(s => ({ livePaused: !s.livePaused }))} style={{ ...ghostBtn(46, 14), flex: 1 }}>{livePaused ? 'Reanudar' : 'Pausar'}</button>
          <button onClick={() => this.setState({ screen: 'summary', lastSessionDuration: liveTimeLabel })} style={{ ...primaryBtn(46, 14), flex: 1 }}>Finalizar</button>
        </div>
      </div>
    );
  }

  renderIntakeInvite() {
    const { userName } = this.props;
    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 14, padding: '0 28px', textAlign: 'center' }}>
        <span style={{ fontSize: 18, fontWeight: 500, color: C.text }}>¡Listo, {userName}!</span>
        <p style={{ margin: 0, fontSize: 13, color: C.textSoft, lineHeight: 1.5 }}>Ahora cuéntanos tus marcas actuales y qué carreras tienes en la mira, en <strong style={{ color: C.text }}>Mis objetivos</strong>.</p>
        <button onClick={() => this.go('carreras')} style={{ ...primaryBtn(46, 14), width: '100%' }}>Completar mis objetivos</button>
        <button onClick={() => this.go('plan')} style={{ ...ghostBtn(44, 13.5, C.textMuted), width: '100%' }}>Ir a mi plan de hoy</button>
      </div>
    );
  }

  renderSummary() {
    const { lastSessionDuration, selectedRpe } = this.state;
    const rpeOptions = Array.from({ length: 10 }, (_, i) => i + 1);
    const saveSummary = () => {
      this.setState({ screen: 'inicio' });
    };
    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', padding: '56px 16px 28px', gap: 20 }}>
        <div>
          <h1 style={heading(24, '0 0 4px')}>¡Buen trabajo!</h1>
          <p style={{ margin: 0, fontSize: 13, color: C.textSoft }}>Sesión completada — así te fue:</p>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2,1fr)', gap: 10 }}>
          <div style={statCard}>
            <span style={{ fontSize: 10, color: C.textSoft, letterSpacing: '0.04em' }}>Duración</span>
            <span style={{ fontSize: 19, fontWeight: 500, color: C.text }}>{lastSessionDuration}</span>
          </div>
          <div style={statCard}>
            <span style={{ fontSize: 10, color: C.textSoft, letterSpacing: '0.04em' }}>FC promedio</span>
            <span style={{ fontSize: 19, fontWeight: 500, color: C.text }}>132 <span style={{ fontSize: 11, color: C.textSoft, fontWeight: 400 }}>bpm</span></span>
          </div>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <span style={{ fontSize: 12, color: C.textSoft, letterSpacing: '0.04em' }}>¿Qué tan duro se sintió? (RPE)</span>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {rpeOptions.map(v => (
              <div key={v} onClick={() => this.setState({ selectedRpe: v })} style={{
                width: 30, height: 30, borderRadius: 7, display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontSize: 12.5, cursor: 'pointer',
                border: `1px solid ${v === selectedRpe ? C.accent : 'rgba(255,255,255,0.18)'}`,
                color: v === selectedRpe ? C.accent : C.textMuted,
              }}>{v}</div>
            ))}
          </div>
        </div>
        <div style={{ flex: 1 }}></div>
        <button onClick={saveSummary} style={primaryBtn(46, 15)}>Guardar sesión</button>
      </div>
    );
  }

  renderTabBar() {
    const { screen } = this.state;
    if (!TAB_SCREENS.includes(screen)) return null;
    const tabStyle = (active) => ({
      flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
      padding: '6px 0', cursor: 'pointer', opacity: active ? 1 : 0.7,
    });
    const tabColor = (active) => active ? C.accent : C.textSoft;
    const tab = (key, label, onClick) => (
      <div onClick={onClick} style={tabStyle(screen === key)}>
        {NAV_ICONS[key](tabColor(screen === key))}
        <span style={{ fontSize: 10.5, color: tabColor(screen === key) }}>{label}</span>
      </div>
    );
    return (
      <div style={{ flex: 'none', display: 'flex', padding: '10px 8px 22px', background: '#161616', boxShadow: '0 -1px 0 rgba(255,255,255,0.1)' }}>
        {tab('inicio', 'Inicio', () => this.go('inicio'))}
        {tab('plan', 'Plan', () => this.go('plan'))}
        {tab('historial', 'Progreso', () => this.go('historial'))}
        {tab('chat', 'Asistente', () => this.go('chat'))}
        {tab('carreras', 'Carreras', () => this.go('carreras'))}
      </div>
    );
  }

  renderAppStage() {
    const { screen } = this.state;
    const isDesktop = this.state.isDesktop;
    return (
      <div style={{ display: 'flex', flexDirection: isDesktop ? 'row' : 'column', height: '100%', width: '100%' }}>
        {isDesktop && this.renderSidebar()}
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', maxWidth: isDesktop ? 640 : 'none', margin: isDesktop ? '0 auto' : 0, width: '100%' }}>
          {this.renderHeader()}
          <div style={{ flex: 1, overflow: 'auto' }}>
            {screen === 'inicio' && this.renderInicio()}
            {screen === 'plan' && this.renderPlan()}
            {screen === 'historial' && this.renderHistorial()}
            {screen === 'chat' && this.renderChat()}
            {screen === 'carreras' && this.renderCarreras()}
            {screen === 'profile' && this.renderProfile()}
            {screen === 'liveSession' && this.renderLiveSession()}
            {screen === 'intakeInvite' && this.renderIntakeInvite()}
            {screen === 'summary' && this.renderSummary()}
          </div>
          {!isDesktop && this.renderTabBar()}
        </div>
      </div>
    );
  }

  renderAuthBrandPanel() {
    return (
      <div style={{
        width: 440, flex: 'none', height: '100%', position: 'relative', overflow: 'hidden',
        background: 'linear-gradient(160deg, #16331f 0%, #0a0a0a 75%)',
        display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
        padding: '48px 44px', boxSizing: 'border-box',
      }}>
        <img src="assets/bortec-logo.png" alt="Bortec Cycling" style={{ height: 20, width: 'auto', opacity: 0.95 }} />
        <div>
          <h1 style={{ ...heading(36), lineHeight: 1.15, margin: '0 0 16px' }}>Entrena con un plan hecho para ti</h1>
          <p style={{ margin: 0, fontSize: 15, color: C.textSoft, lineHeight: 1.6, maxWidth: 340 }}>Planes claros, tu progreso siempre visible, y el respaldo real de tu entrenador — todo en un solo lugar.</p>
        </div>
        <div style={{ display: 'flex', gap: 6 }}>
          <div style={{ height: 3, width: 28, borderRadius: 2, background: C.accent }}></div>
          <div style={{ height: 3, width: 10, borderRadius: 2, background: '#2e2e2e' }}></div>
          <div style={{ height: 3, width: 10, borderRadius: 2, background: '#2e2e2e' }}></div>
        </div>
      </div>
    );
  }

  render() {
    const { stage, bootLoading } = this.state;
    const isDesktop = this.state.isDesktop;
    if (bootLoading) {
      return (
        <div style={{ height: '100vh', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: C.panel, color: C.textSoft, fontFamily: 'Inter, system-ui, sans-serif', fontSize: 13 }}>
          Cargando…
        </div>
      );
    }
    const isAuthStage = stage === 'onboarding' || stage === 'login' || stage === 'intake';
    const formWidth = stage === 'intake' ? 480 : 420;
    const formBox = (
      <div style={{
        width: isDesktop ? formWidth : '100%', maxWidth: isDesktop ? formWidth : '100%',
        height: isDesktop ? 'min(780px, 90vh)' : '100%', boxSizing: 'border-box',
        display: 'flex', flexDirection: 'column', overflow: 'hidden',
        borderRadius: isDesktop ? 16 : 0,
        background: isDesktop ? '#161616' : 'transparent',
        boxShadow: isDesktop ? '0 20px 60px rgba(0,0,0,0.4)' : 'none',
      }}>
        {stage === 'onboarding' && this.renderOnboarding()}
        {stage === 'login' && this.renderLogin()}
        {stage === 'intake' && this.renderIntake()}
      </div>
    );
    return (
      <div style={{ height: '100vh', width: '100%', display: 'flex', flexDirection: isDesktop && isAuthStage ? 'row' : 'column', background: C.panel, fontFamily: 'Inter, system-ui, sans-serif', overflow: 'hidden' }}>
        {isDesktop && isAuthStage && this.renderAuthBrandPanel()}
        {isAuthStage && (
          <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', overflow: 'auto', boxSizing: 'border-box', padding: isDesktop ? 32 : 0 }}>
            {formBox}
          </div>
        )}
        {stage === 'app' && this.renderAppStage()}
      </div>
    );
  }
}

App.defaultProps = {
  userName: 'Marco',
  userEmail: 'atleta@bortec.mx',
  weeklySessionGoal: 4,
  showPowerMetric: false,
};

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