kg·reps esta semana ';
h+=''+series+' series registradas
';
if(top.length){
h+=''+top.map(function(n){
return '
'+n+' '+Math.round(ejMap[n]).toLocaleString('es')+' kg·reps
';
}).join('')+'
';
}else{
h+='Guarda pesos en tus ejercicios para ver el volumen acumulado.
';
}
box.innerHTML=h;
}).catch(function(){ box.innerHTML='No se pudo calcular el volumen.
'; });
}
/* ── Duelos 1v1 semanales ── */
function vvDueloCargar(){
var box=document.getElementById('vvpDuelo');
if(!box||!clienteToken) return;
clienteApi({action:'get-mi-duelo'}).then(function(j){
if(!j.optin){
box.innerHTML='Opcional: compite con otra clienta por quién entrena más esta semana (solo alias, sin datos personales).
'+
'⚔️ Activar duelos ';
return;
}
if(!j.duelo){
box.innerHTML='Estás disponible para duelo. Pulsa para emparejarte con otra clienta.
'+
'🔍 Buscar rival ';
return;
}
var d=j.duelo;
var estado=d.empate?'🤝 Empate':(d.voyGanando?'🏆 Vas ganando':'💪 Sigue — tu rival va delante');
box.innerHTML=''+
'
Tú '+d.misSesiones+' sesiones
'+
'
VS
'+
'
'+d.rival+' '+d.rivalSesiones+' sesiones
'+
'
'+estado+' · Semana '+d.semana+'
';
}).catch(function(){ box.innerHTML='No se pudo cargar el duelo.
'; });
}
document.addEventListener('click',function(e){
if(e.target.closest('#vvDueloOptin')){
clienteApi({action:'save-duelo-optin',optin:true}).then(function(){ vvDueloCargar(); }).catch(function(err){ alert(err.message||'Error'); });
return;
}
if(e.target.closest('#vvDueloBuscar')){
var st=document.getElementById('vvDueloStatus');
if(st) st.textContent='Buscando…';
clienteApi({action:'buscar-duelo'}).then(function(j){
if(st) st.textContent=j.rival?'✓ Rival: '+j.rival:'✓ Duelo creado';
vvDueloCargar();
}).catch(function(err){ if(st) st.textContent='✗ '+(err.message||'Error'); });
}
});
/* ── Contador de reps (tap + cámara opcional) ── */
function vvRepsAbrir(nombre,target){
var tgt=parseInt(target,10)||10;
var count=0;
var ov=document.createElement('div');
ov.className='vv-reps-overlay';
ov.innerHTML='✕ '+
'
'+String(nombre).replace(/'+
'
'+
'
0 / '+tgt+' reps
'+
'
↺ Reiniciar '+
'✓ Serie hecha
';
function upd(){
var el=document.getElementById('vvRepsNum');
if(el) el.textContent=String(count);
if(count>=tgt){ try{ if(navigator.vibrate) navigator.vibrate([80,40,120]); }catch(x){} }
else { try{ if(navigator.vibrate) navigator.vibrate(30); }catch(x){} }
}
ov.addEventListener('click',function(ev){
if(ev.target.closest('.vv-reps-close')||ev.target===ov){ ov.remove(); return; }
if(ev.target.closest('#vvRepsReset')){ count=0; upd(); return; }
if(ev.target.closest('#vvRepsDone')){ ov.remove(); return; }
if(ev.target.closest('#vvRepsTap')){ count++; upd(); }
});
document.body.appendChild(ov);
var vid=ov.querySelector('.vv-reps-cam');
if(vid && navigator.mediaDevices && navigator.mediaDevices.getUserMedia){
navigator.mediaDevices.getUserMedia({video:{facingMode:'environment'},audio:false})
.then(function(stream){ vid.srcObject=stream; })
.catch(function(){ vid.style.display='none'; });
}else if(vid){ vid.style.display='none'; }
}
document.addEventListener('click',function(e){
var rb=e.target.closest('[data-reps-btn]');
if(rb) vvRepsAbrir(rb.dataset.exName||'Ejercicio', rb.dataset.targetReps||'10');
});
/* ── Cofre semanal (recompensa gamificación) ── */
function vvSemanaKey(){
var d=new Date(); d.setHours(0,0,0,0);
d.setDate(d.getDate()+3-(d.getDay()+6)%7);
var sem=Math.floor((d-new Date(d.getFullYear(),0,4))/86400000/7)+1;
return d.getFullYear()+'-W'+String(sem).padStart(2,'0');
}
var VV_COFRE_PREMIOS=[
'🎙️ Mensaje de Valeria: «La constancia es tu superpoder. Sigue.»',
'🥗 Receta sorpresa: yogur griego + frutos rojos + canela = postre proteico.',
'💪 Reto: la próxima sesión, añade 1 rep extra en el último ejercicio.',
'✨ Tip VV: dormir 7h+ multiplica tus resultados esta semana.',
'🏆 Logro desbloqueado: guerrera de la semana.'
];
function vvCofreRender(){
var box=document.getElementById('vvpCofre');
if(!box||!clienteToken) return;
vvGamCargar().then(function(){
var ses=0;
for(var f in vvGam.cal||{}) if(vvEnSemanaActual(f)&&(vvGam.cal[f]||{}).entreno) ses++;
var key='vv_cofre_'+vvSemanaKey();
var abierto=false; try{ abierto=localStorage.getItem(key)==='1'; }catch(x){}
if(ses<3){
box.innerHTML='
🔒 Te faltan '+(3-ses)+' sesión(es) esta semana para abrir el cofre.
';
return;
}
if(abierto){
var premio=localStorage.getItem(key+'_premio')||VV_COFRE_PREMIOS[0];
box.innerHTML='
'+premio+'
';
return;
}
box.innerHTML='
🎁 Abrir cofre semanal ';
});
}
document.addEventListener('click',function(e){
if(!e.target.closest('#vvCofreAbrir')) return;
var key='vv_cofre_'+vvSemanaKey();
var premio=VV_COFRE_PREMIOS[Math.floor(Math.random()*VV_COFRE_PREMIOS.length)];
clienteApi({action:'claim-mi-sorpresa'}).catch(function(){});
try{ localStorage.setItem(key,'1'); localStorage.setItem(key+'_premio',premio); }catch(x){}
var box=document.getElementById('vvpCofre');
if(box) box.innerHTML='
'+premio+'
🎙️ Día sorpresa del coach activado en VV OS.
';
});
/* ── Temporada VV (ranking trimestral) ── */
function vvTemporadaCargar(){
var box=document.getElementById('vvpTemporada');
if(!box||!clienteToken) return Promise.resolve();
return clienteApi({action:'get-clasificacion-temporada'}).then(function(j){
var t=j.temporada||{};
var tit=document.getElementById('vvpTemporadaTitulo');
if(tit&&t.nombre) tit.textContent=(t.icono||'🌱')+' Temporada '+t.nombre;
var list=j.clasificacion||[];
if(!list.length){ box.innerHTML='
Aún no hay datos de la temporada. ¡Sé la primera en sumar sesiones!
'; return; }
box.innerHTML='
'+list.map(function(row,i){
return ''+(i+1)+' '+row.alias+' · '+row.sesiones+' sesiones'+(row.yo?' (tú) ':'')+' ';
}).join('')+' '+(t.inicio?'
Del '+t.inicio+' al '+t.fin+'.
':'');
}).catch(function(){ if(box) box.innerHTML='
No se pudo cargar el ranking.
'; });
}
/* ── Modo salud + suelo pélvico ── */
var vvModoSaludCache=null;
var vvSueloCache=[];
function vvModoSaludRender(){
var box=document.getElementById('vvpModoSalud');
if(!box) return;
var m=vvModoSaludCache||{modo:'ninguno',activo:false};
var mods=[['embarazo','🤰 Embarazo'],['postparto','👶 Postparto'],['menopausia','🌺 Menopausia'],['ninguno','— Ninguno']];
var h='
';
mods.forEach(function(pair){
h+=''+pair[1]+' ';
});
h+='
';
if(m.modo==='embarazo'){
h+='
Semana de embarazo ';
}
h+='
'+h;
h+='Registros: '+fechas.slice(-6).join(' · ')+'
';
h+='
';
h+='Diario de síntomas de hoy ';
h+='
';
[['retencion','💧 Retención'],['sueno','😴 Sueño'],['ansiedad','😰 Ansiedad']].forEach(function(m){
h+='
'+m[1]+'
';
for(var v=1;v<=3;v++) h+=''+v+' ';
h+='
';
});
h+='
Dolor lumbar hoy';
h+='
';
h+='
Guardar síntomas de hoy ';
if(vvSintomasDatos.length){
h+='
Últimos: '+vvSintomasDatos.slice(0,5).map(function(s){
return s.fecha+' (R'+ (s.retencion||'—') +'/S'+ (s.sueno||'—') +')';
}).join(' · ')+'
';
}
h+='
';
}
box.innerHTML=h;
if(fechas.length&&clienteToken){
clienteApi({action:'get-indice-energia-menstrual'}).then(function(j){
var ie=document.getElementById('vvIndiceEnergia');
if(!ie) return;
if(j.indice===null){ ie.innerHTML=''+j.mensaje+'
'; return; }
ie.innerHTML='⚡ Índice energía: '+j.indice+'/100 — '+j.mensaje+'
';
}).catch(function(){});
}
}
document.addEventListener('click',function(e){
var sb=e.target.closest('#vvpCiclo button[data-sint]');
if(sb){ vvSintomaSel[sb.dataset.sint]=parseInt(sb.dataset.val,10); vvCicloRender(); return; }
if(e.target.closest('#vvSintGuardar')){
var st=document.getElementById('vvSintStatus');
var lb=document.getElementById('vvSintLumbar');
var notas=document.getElementById('vvSintNotas');
if(st) st.textContent='Guardando…';
clienteApi({
action:'save-mi-sintomas', fecha:hoyISO(),
retencion:vvSintomaSel.retencion||null, sueno:vvSintomaSel.sueno||null, ansiedad:vvSintomaSel.ansiedad||null,
dolorLumbar:lb&&lb.checked, notas:notas?notas.value:''
}).then(function(j){ vvSintomasDatos=j.sintomas||[]; if(st) st.textContent='✓ Guardado'; vvCicloRender(); })
.catch(function(err){ if(st) st.textContent='✗ '+err.message; });
return;
}
if(e.target.closest('#vvCicloOptin')){
try{ localStorage.setItem('vv_ciclo_optin','1'); }catch(x){}
clienteApi({action:'get-mi-ciclo'}).then(function(j){
vvCicloDatos=j.ciclo||[];
return clienteApi({action:'get-mi-sintomas'}).catch(function(){return{sintomas:[]};});
}).then(function(j2){
vvSintomasDatos=j2.sintomas||[];
vvCicloRender(); vvCicloEntrenoBanner(); vvOsRender();
}).catch(function(){ vvCicloDatos=[]; vvCicloRender(); });
return;
}
if(e.target.closest('#vvCicloGuardar')){
var f=document.getElementById('vvCicloFecha');
var st=document.getElementById('vvCicloStatus');
if(!f||!f.value){ if(st) st.textContent='Elige una fecha.'; return; }
if(st) st.textContent='Guardando…';
clienteApi({action:'save-mi-ciclo',fecha:f.value}).then(function(j){ vvCicloDatos=j.ciclo||[]; vvCicloRender(); vvCicloEntrenoBanner(); vvOsRender(); }).catch(function(err){ if(st) st.textContent='✗ '+err.message; });
}
});
/* ── Sustituto de comida con IA ── */
document.addEventListener('click',function(e){
var b=e.target.closest('.vv-sust-btn');
if(!b) return;
var falta=window.prompt('¿Qué alimento no tienes o no te gusta de esta comida?');
if(!falta || !falta.trim()) return;
var comida=b.closest('.vv-meal');
var textoComida=comida?comida.textContent.replace(/\s+/g,' ').slice(0,500):'';
var lang='es'; try{ lang=localStorage.getItem('vv_lang')||'es'; }catch(x){}
b.disabled=true; b.textContent='🔄 Buscando sustituto…';
clienteApi({action:'ai-sustituto', falta:falta.trim(), comida:textoComida, lang:lang})
.then(function(j){
b.disabled=false; b.textContent='🔄 ¿Te falta un alimento?';
var ov=document.createElement('div');
ov.className='vv-video-overlay';
ov.innerHTML='✕ 🔄 Sustituto sugerido
'+String(j.sugerencia).replace(/')+'
Sugerencia de IA con macros equivalentes · te quedan '+j.usosRestantes+' hoy
';
ov.addEventListener('click',function(ev){ if(ev.target===ov||ev.target.classList.contains('vv-video-close')) ov.remove(); });
document.body.appendChild(ov);
})
.catch(function(err){ b.disabled=false; b.textContent='🔄 ¿Te falta un alimento?'; alert(err.message||'No disponible ahora mismo.'); });
});
/* ── Widget VV OS (racha · readiness · sesión) ── */
function vvWidgetRender(){
var w=document.getElementById('vvWidgetRow');
if(!w) return;
var s=vvGamStats();
var r=vvReadiness();
var dia=vvDiaHoy();
w.innerHTML='🔥 '+(s.racha.dias||0)+' racha '+
(r?''+r.icono+' '+r.score+' /100 ':'')+
'🏋️ Día '+dia+' '+
(vvGam.equipo&&vvGam.equipo.racha>0?'👥 Equipo '+vvGam.equipo.racha+' sem ':'');
}
/* ── Test de fuerza trimestral ── */
function vvTestFuerzaCargar(){
var box=document.getElementById('vvpTestFuerza');
if(!box||!clienteToken) return;
clienteApi({action:'get-mi-test-fuerza'}).then(function(j){
var prs=j.prs||[];
var h='📅 '+j.trimestre+' · '+j.nuevosPr+' PR'+(j.nuevosPr===1?'':'s')+' nuevos vs '+j.trimestreAnterior+'
';
if(!prs.length){
h+='Guarda pesos en tus ejercicios para ver tus récords del trimestre.
';
}else{
h+=''+prs.map(function(p){
return '
'+p.ejercicio+' '+p.max+' kg '+(p.anterior?' (antes '+p.anterior+' kg) ':'')+' '+(p.nuevoPr?'🎉 PR ':'')+'
';
}).join('')+'
';
}
if(j.testOficial){
h+='✅ Test oficial registrado el '+j.testOficial.fecha+'
';
}else if(prs.length){
h+='🏁 Marcar test trimestral hecho ';
}
box.innerHTML=h;
}).catch(function(){ box.innerHTML='No se pudo cargar el test.
'; });
}
document.addEventListener('click',function(e){
if(!e.target.closest('#vvTestMarcar')) return;
var st=document.getElementById('vvTestStatus');
if(st) st.textContent='Guardando…';
clienteApi({action:'save-mi-test-fuerza',notas:'Test trimestral completado'}).then(function(){
if(st) st.textContent='✓ Registrado';
vvTestFuerzaCargar();
}).catch(function(err){ if(st) st.textContent='✗ '+(err.message||'Error'); });
});
/* ── Analíticas de sangre ── */
function vvAnaliticasCargar(){
var box=document.getElementById('vvpAnaliticas');
if(!box||!clienteToken) return;
clienteApi({action:'get-mi-analiticas'}).then(function(j){
var h='';
if(j.recordar){
h+='🔔 Han pasado '+(j.diasDesde||'6+ meses')+(j.diasDesde?' días':'')+' desde tu última analítica. Valeria recomienda repetirla.
';
}
h+='';
h+='Resultados / notas (privado)
';
h+='Guardar analítica ';
if((j.analiticas||[]).length){
h+='Historial: '+(j.analiticas||[]).slice(0,5).map(function(a){
return a.fecha+(a.notas?(' — '+a.notas.slice(0,40)):'');
}).join(' · ')+'
';
}
box.innerHTML=h;
}).catch(function(){ box.innerHTML='No se pudo cargar.
'; });
}
document.addEventListener('click',function(e){
if(!e.target.closest('#vvAnalGuardar')) return;
var st=document.getElementById('vvAnalStatus');
var f=document.getElementById('vvAnalFecha');
var n=document.getElementById('vvAnalNotas');
if(st) st.textContent='Guardando…';
clienteApi({action:'save-mi-analitica',fecha:f?f.value:hoyISO(),notas:n?n.value:''}).then(function(){
if(st) st.textContent='✓ Guardado';
vvAnaliticasCargar();
vvAnaliticaOsCheck();
}).catch(function(err){ if(st) st.textContent='✗ '+(err.message||'Error'); });
});
var vvAnaliticaRecordar=false;
function vvAnaliticaOsCheck(){
if(!clienteToken) return Promise.resolve();
return clienteApi({action:'get-mi-analiticas'}).then(function(j){
vvAnaliticaRecordar=!!j.recordar;
if(typeof vvOsRender==='function') vvOsRender();
}).catch(function(){});
}
/* ── Máquina ocupada → sustituto IA ── */
document.addEventListener('click',function(e){
var ob=e.target.closest('.vv-ocupada-btn');
if(!ob||!clienteToken) return;
var nombre=ob.dataset.exName||'';
var series=ob.dataset.exSets||'';
var foco=document.getElementById('vvDayFocus');
var focusTxt=foco?foco.textContent:'';
ob.disabled=true;
var lang='es'; try{ lang=localStorage.getItem('vv_lang')||'es'; }catch(x){}
clienteApi({
action:'ai-herramienta', modo:'ejercicio-ocupado', lang:lang,
texto:'Ejercicio: '+nombre+' · Series: '+series+' · Grupo: '+focusTxt,
planResumen:vvPlanResumen()
}).then(function(j){
ob.disabled=false;
var ov=document.createElement('div');
ov.className='vv-video-overlay';
ov.innerHTML='✕ 🔄 Sustituto en el gym
'+String(j.respuesta).replace(/')+'
';
ov.addEventListener('click',function(ev){ if(ev.target===ov||ev.target.classList.contains('vv-video-close')) ov.remove(); });
document.body.appendChild(ov);
}).catch(function(err){ ob.disabled=false; alert(err.message||'No disponible'); });
});
/* ── Club VV ── */
var vvClubOptin=false;
function vvClubRender(){
var root=document.getElementById('vvClubRoot');
if(!root||!clienteToken) return;
if(!vvClubOptin){
root.innerHTML='El Club VV es opcional: verás mensajes de otras clientas (solo nombre de pila) y podrás compartir tus logros.
'+
'👥 Unirme al Club VV ';
return;
}
root.innerHTML='Cargando feed…
';
clienteApi({action:'get-club-feed'}).then(function(j){
var feed=j.feed||[];
var eqHtml='';
return clienteApi({action:'get-racha-equipo'}).then(function(eq){
var e=eq.equipo||{};
if(e.total>0){
eqHtml='👥 Racha de equipo · '+e.activas+' de '+e.total+' entrenaron esta semana';
if(e.racha>0) eqHtml+=' · 🔥 '+e.racha+' semana(s) seguidas';
if(e.multiplicador>1) eqHtml+=' · x'+e.multiplicador+' puntos ';
eqHtml+=(e.semanaOk?' ✅':' ⚠️ Meta: '+e.meta+' activas')+'
';
}
return {feed:feed,eqHtml:eqHtml};
}).catch(function(){ return {feed:feed,eqHtml:''}; });
}).then(function(data){
var feed=data.feed||[];
var h=(data.eqHtml||'')+''+(
feed.length?feed.map(function(p){
var ico=p.tipo==='pr'?'🏆':(p.tipo==='motivacion'?'💪':'💬');
return '
'+ico+' '+p.alias+(p.yo?' (tú)':'')+' · '+String(p.fecha||'').slice(0,16).replace('T',' ')+'
'+String(p.texto).replace(/';
}).join(''):'
Sé la primera en publicar algo hoy 💪
'
)+'
';
h+='
Publicar en el Club ';
h+='💬 General 🏆 Récord / logro 💪 Motivación ';
h+='
';
h+='
Publicar ';
root.innerHTML=h;
}).catch(function(){ root.innerHTML='
No se pudo cargar el Club.
'; });
}
document.addEventListener('click',function(e){
if(e.target.closest('#vvClubOptinBtn')){
clienteApi({action:'save-club-optin',optin:true}).then(function(){
vvClubOptin=true; vvClubRender();
}).catch(function(err){ alert(err.message||'Error'); });
return;
}
if(e.target.closest('#vvClubPublicar')){
var txt=document.getElementById('vvClubTexto');
var tipo=document.getElementById('vvClubTipo');
var st=document.getElementById('vvClubStatus');
if(!txt||!txt.value.trim()){ if(st) st.textContent='Escribe algo.'; return; }
if(st) st.textContent='Publicando…';
clienteApi({action:'post-club-mensaje',texto:txt.value.trim(),tipo:tipo?tipo.value:'general'})
.then(function(){ if(st) st.textContent='✓ Publicado'; vvClubRender(); })
.catch(function(err){ if(st) st.textContent='✗ '+err.message; });
}
});
function vvClubInit(){
if(!clienteToken) return Promise.resolve();
return clienteApi({action:'get-mi-club'}).then(function(j){
vvClubOptin=!!(j.club&&j.club.optin);
}).catch(function(){});
}
/* ── Recordatorios locales (notificaciones del navegador) ── */
function vvNotifTick(){
if(!('Notification' in window)||Notification.permission!=='granted') return;
var hoy=hoyISO();
try{
if(localStorage.getItem('vv_notif_last')===hoy) return;
if((vvGam.cal[hoy]||{}).entreno) return;
var hora=localStorage.getItem('vv_notif_hora')||'18';
var parts=hora.split(':');
var nh=parseInt(parts[0],10)||18, nm=parseInt(parts[1],10)||0;
var now=new Date();
if(now.getHours()>nh||(now.getHours()===nh&&now.getMinutes()>=nm)){
var dia=vvDiaHoy();
new Notification('Plan VV · Valeria Verazzo',{body:'Hoy toca Día '+dia+' — '+((ENTRENOS[dia]&&ENTRENOS[dia].focus)||'entreno'),icon:location.origin+'/icon-vv-192.png'});
localStorage.setItem('vv_notif_last',hoy);
}
}catch(x){}
}
setInterval(vvNotifTick, 60000);
/* ── Resumen del plan para IA ── */
function vvPlanResumen(){
var parts=[];
try{
Object.keys(ENTRENOS||{}).forEach(function(k){
var d=ENTRENOS[k];
if(!d) return;
var exs=(d.ejercicios||[]).slice(0,8).map(function(e){ return e.n+' '+e.s; }).join('; ');
parts.push('D'+k+' '+d.focus+': '+exs);
});
['entreno','descanso'].forEach(function(t){
var di=DIETAS&&DIETAS[t];
if(!di||!di.comidas) return;
var cms=di.comidas.slice(0,5).map(function(c){
return (c.title||'')+': '+(Array.isArray(c.items)?c.items.slice(0,3).join(', '):'');
}).join(' | ');
parts.push('Dieta '+t+': '+cms);
});
}catch(x){}
return parts.join('\n').slice(0,3800);
}
function vvDiaBase(){
var total=Object.keys(ENTRENOS).length;
if(!total) return 1;
var d=new Date();
var base=(d.getDay()===0?7:d.getDay())%total||total;
if(base>total) base=((base-1)%total)+1;
return base;
}
function vvDiaHoy(){
var base=vvDiaBase();
var adj=vvCicloAjuste();
if(adj&&adj.tipo==='suave'){
for(var i=1;i<=Object.keys(ENTRENOS).length;i++){
var f=(ENTRENOS[i].focus||'').toLowerCase();
if(f.indexOf('cardio')>-1||f.indexOf('movilidad')>-1||f.indexOf('descanso')>-1) return i;
}
}
return base;
}
function vvSesionTexto(dia){
var data=ENTRENOS[dia];
if(!data) return '';
var exs=(data.ejercicios||[]).map(function(e){ return '- '+e.n+': '+e.s+(e.p?' @ '+e.p+'kg':''); }).join('\n');
return 'DÍA '+dia+' — '+data.focus+'\n'+exs;
}
function vvCicloAjuste(){
var optin=false; try{ optin=localStorage.getItem('vv_ciclo_optin')==='1'; }catch(e){}
if(!optin||!vvCicloDatos||!vvCicloDatos.length) return null;
var ult=vvCicloDatos[vvCicloDatos.length-1];
var dias=Math.floor((new Date(hoyISO())-new Date(ult))/86400000)+1;
var medio=28;
if(vvCicloDatos.length>=2){
var gaps=[]; for(var i=1;i
Ciclo día '+adj.dias+' — '+adj.fase[1]+' '+
'📌 Sesión sugerida hoy: Día '+dia+' · '+(ENTRENOS[dia]?ENTRENOS[dia].focus:'')+
(adj.tipo==='suave'?' · Prioriza técnica y carga moderada ':' · Buen momento para apretar ')+'
';
var grid=document.getElementById('vvDaysGrid');
if(grid){
grid.querySelectorAll('.vv-day-btn').forEach(function(btn){
var d=parseInt(btn.dataset.day,10);
btn.classList.remove('ciclo-hoy','ciclo-suave');
if(d===dia) btn.classList.add('ciclo-hoy');
else if(adj.tipo==='suave'&&ENTRENOS[d]&&!(ENTRENOS[d].focus||'').toLowerCase().match(/cardio|movilidad/)) btn.classList.add('ciclo-suave');
});
}
}
/* ── Día sorpresa (VV OS) ── */
var vvSorpresaOsCache=null;
function vvSorpresaOsCheck(){
if(!clienteToken) return Promise.resolve();
return clienteApi({action:'get-mi-sorpresa-semana'}).then(function(j){
vvSorpresaOsCache=j;
}).catch(function(){});
}
/* ── VV OS: pantalla diaria ── */
function vvOsRender(){
var p=document.getElementById('vvOsPanel');
if(!p) return;
var hoy=hoyISO();
var r=vvReadiness();
var diaSugerido=vvDiaHoy();
var entrenoHoy=(vvGam.cal[hoy]||{}).entreno;
var foco=ENTRENOS[diaSugerido]?ENTRENOS[diaSugerido].focus:'tu sesión';
var ciclo=vvCicloAjuste();
var modoSalud=vvModoSaludCache&&vvModoSaludCache.activo?vvModoSaludCache:null;
var accion='';
if(!vvGam.estado[hoy]||!vvGam.estado[hoy].energia){
accion='Hacer check-in ↑ ';
}else if(!entrenoHoy){
accion='Ir a entreno → '+
'Plan B casa ';
}else{
accion='Ver comidas de hoy → ';
}
p.innerHTML=''+(r?'
'+r.icono+' Preparación '+r.score+'/100 — '+r.texto+'
':'
☀️ Buenos días. Empieza con tu check-in de energía, sueño y ánimo.
')+
(vvGam.temporada&&vvGam.temporada.nombre?'
'+vvGam.temporada.icono+' Temporada '+vvGam.temporada.nombre+' · '+(vvGam.temporadaSes||0)+' sesiones
':'')+
(modoSalud?'
💗 Modo '+modoSalud.modo+' activo — prioriza escuchar tu cuerpo hoy.
':'')+
(vvRiesgoCache&&vvRiesgoCache.nivel!=='bajo'?'
💬 '+vvRiesgoCache.mensajeCliente+'
':'')+
(vvAnaliticaRecordar?'
🩸 Recordatorio: toca repetir analítica de sangre (cada 6 meses). Regístrala en Progreso.
':'')+
(vvSorpresaOsCache&&vvSorpresaOsCache.desbloqueada&&!vvSorpresaOsCache.reclamada?'
🎁 Día sorpresa del coach desbloqueado — ábrelo en Progreso → Cofre.
':'')+
(ciclo?'
'+ciclo.fase[0]+' Ciclo día '+ciclo.dias+' — '+ciclo.fase[1]+'
':'')+
'
🏋️ Hoy toca: Día '+diaSugerido+' · '+foco+(entrenoHoy?' ✓ hecho ':'')+'
'+
accion+'
';
p.querySelectorAll('[data-go]').forEach(function(btn){
btn.addEventListener('click',function(){ showView(btn.dataset.go); });
});
var pb=document.getElementById('vvOsPlanB');
if(pb) pb.addEventListener('click',function(){ vvEjecutarPlanB(); });
}
function vvEjecutarPlanB(){
showView('herramientas');
vvToolAbrir('plan-b');
var res=document.getElementById('vvToolResult');
if(res) res.textContent='Generando Plan B desde tu sesión de hoy…';
var lang='es'; try{ lang=localStorage.getItem('vv_lang')||'es'; }catch(x){}
clienteApi({
action:'ai-herramienta', modo:'plan-b', lang:lang,
sesion:vvSesionTexto(vvDiaHoy()),
planResumen:vvPlanResumen()
}).then(function(j){ if(res) res.textContent=j.respuesta; })
.catch(function(err){ if(res) res.textContent='✗ '+(err.message||'Error'); });
}
/* ── Valeria IA: chat privado ── */
var vvIaHistorial=[];
function vvIaPintar(){
var box=document.getElementById('vvIaChat');
if(!box) return;
if(!vvIaHistorial.length){
box.innerHTML='Hola 💪 Soy tu Valeria IA. Pregúntame lo que necesites sobre tu plan, sensaciones o el día de hoy.
';
return;
}
box.innerHTML=vvIaHistorial.map(function(m){
return ''+String(m.texto).replace(/')+'
';
}).join('');
box.scrollTop=box.scrollHeight;
}
function vvIaCargar(){
if(!clienteToken) return Promise.resolve();
return clienteApi({action:'get-mi-ia-chat'}).then(function(j){
vvIaHistorial=(j.chat||[]).slice(-30).map(function(m){ return {rol:m.rol,texto:m.texto}; });
vvIaPintar();
}).catch(function(){});
}
document.addEventListener('submit',function(e){
if(e.target.id!=='vvIaForm') return;
e.preventDefault();
var inp=document.getElementById('vvIaInput');
var st=document.getElementById('vvIaStatus');
var txt=(inp&&inp.value||'').trim();
if(!txt||!clienteToken) return;
vvIaHistorial.push({rol:'user',texto:txt});
vvIaPintar();
if(inp) inp.value='';
if(st) st.textContent='Valeria está escribiendo…';
var lang='es'; try{ lang=localStorage.getItem('vv_lang')||'es'; }catch(x){}
clienteApi({action:'ai-valeria-chat',mensaje:txt,historial:vvIaHistorial.slice(0,-1),lang:lang,planResumen:vvPlanResumen()})
.then(function(j){
vvIaHistorial.push({rol:'assistant',texto:j.respuesta});
vvIaPintar();
if(st) st.textContent='Te quedan '+j.usosRestantes+' consultas hoy';
})
.catch(function(err){ if(st) st.textContent='✗ '+(err.message||'Error'); });
});
/* ── Herramientas IA ── */
var vvToolActivo='';
function vvToolAbrir(tool){
vvToolActivo=tool;
var panel=document.getElementById('vvToolPanel');
var title=document.getElementById('vvToolTitle');
var body=document.getElementById('vvToolBody');
var res=document.getElementById('vvToolResult');
if(!panel||!body) return;
panel.style.display='block';
if(res) res.textContent='';
var nombres={ 'plan-b':'Plan B · entreno en casa', 'lista-compra':'Lista de compra semanal', 'restaurante':'Modo restaurante', 'antojo':'Semáforo de antojos', 'sensaciones':'Traducir sensaciones', 'plato':'Escáner de plato', 'etiqueta':'Descifrar etiqueta', 'recordatorio':'Recordatorio de entreno', 'contador-reps':'Contador de reps', 'video-tecnica':'Análisis de técnica', 'auto-periodizacion':'Auto-periodización', 'batch-cooking':'Batch cooking domingo', 'modo-viaje':'Modo viaje', 'recetario':'Recetario dinámico', 'informe-mensual':'Informe mensual IA', 'temporada-alimentos':'Alimentos de temporada' };
if(title) title.textContent=nombres[tool]||tool;
if(tool==='plan-b'){
body.innerHTML='Convertiré tu sesión de hoy (Día '+vvDiaHoy()+') en versión casa.
Generar Plan B ';
}else if(tool==='plato'||tool==='etiqueta'){
body.innerHTML='Analizar foto ';
}else if(tool==='lista-compra'){
body.innerHTML='Generaré la lista por pasillos Mercadona/Lidl desde tu dieta.
Generar lista ';
}else if(tool==='recordatorio'){
var hora='18:00'; try{ hora=localStorage.getItem('vv_notif_hora')||'18:00'; }catch(x){}
body.innerHTML='Te avisará a la hora elegida si aún no has registrado el entreno de hoy (notificación del móvil).
'+
'Hora del recordatorio '+
'Activar recordatorio ';
}else if(tool==='contador-reps'){
body.innerHTML='Abre el contador con tap en pantalla. La cámara trasera es opcional (para dejar el móvil apoyado).
'+
'Reps objetivo '+
'Abrir contador ';
}else if(tool==='video-tecnica'){
body.innerHTML='Graba o sube un vídeo corto (máx. ~15 s). Analizaré un fotograma con IA — nada se guarda en el servidor salvo tu consulta.
'+
' '+
' '+
'Analizar técnica ';
}else if(tool==='auto-periodizacion'){
body.innerHTML='Propuesta de mesociclo de 4 semanas según tu adherencia, RPE y sesiones reales.
'+
'Generar periodización ';
}else if(tool==='batch-cooking'){
body.innerHTML='Reorganizaré tu semana de comidas en ~2 h de cocinado el domingo (tuppers + pasos).
'+
'Generar batch cooking ';
}else if(tool==='modo-viaje'){
body.innerHTML='¿Cuántos días de viaje? '+
'