PageSourceSearch

https://redentrenadores-268bb8.webflow.io/pregunta-al-experto

html redentrenadores-268bb8.webflow.io collected 2026-10-03 13:38:37 UTC 229,134 bytes, 3,690 lines download raw bytes

1<!DOCTYPE html><!-- This site was created in Webflow. https://webflow.com --><!-- Last Published: Sat Oct 03 2026 13:28:24 GMT+0000 (Coordinated Universal Time) --><html data-wf-domain="redentrenadores-268bb8.webflow.io" data-wf-page="6908a45a566828a761f9d274" data-wf-site="68c5a729c71fa3f37858fbbe" lang="es"><head><meta charset="utf-8"/><link href="https://cdn.prod.website-files.com" rel="preconnect" crossorigin="anonymous"/><title>Pregunta al experto | RedEntrenadores</title><meta content="Resuelve tus dudas sobre entrenamiento físico, adelgazamiento y ganancia muscular. ¡Respuestas gratuitas de entrenadores personales en 48 horas!" name="description"/><meta content="Pregunta al experto | RedEntrenadores" property="og:title"/><meta content="Resuelve tus dudas sobre entrenamiento físico, adelgazamiento y ganancia muscular. ¡Respuestas gratuitas de entrenadores personales en 48 horas!" property="og:description"/><meta content="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68cf1d169359063e6094caad_logo-256x256.png" property="og:image"/><meta content="Pregunta al experto | RedEntrenadores" name="twitter:title"/><meta content="Resuelve tus dudas sobre entrenamiento físico, adelgazamiento y ganancia muscular. ¡Respuestas gratuitas de entrenadores personales en 48 horas!" name="twitter:description"/><meta property="og:type" content="website"/><meta content="summary_large_image" name="twitter:card"/><meta content="width=device-width, initial-scale=1" name="viewport"/><meta content="Ibl2YfMxHx18YwgwBo_Cf7Y0noaXDXPp8qxAux6bR7I" name="google-site-verification"/><meta content="Webflow" name="generator"/><link href="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/css/redentrenadores-268bb8.webflow.shared.5fdd5e548.min.css" rel="stylesheet" type="text/css" integrity="sha384-X91eVImZbdbFw8t2Y6QKDl7HUD/gaoQ44rfY64Qq+p7zJMJt635EzDj9JzMjxLwR" crossorigin="anonymous"/><link href="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/css/redentrenadores-268bb8.webflow.6908a45a566828a761f9d274.359fa989a.opt.min.css" rel="stylesheet" type="text/css" integrity="sha384-NZ+piafdpavFkpz/DtcJy0Su1V+7rTG+Meoe1lNWSoav6Yf2PX9YHlVW6ymKS9mE" crossorigin="anonymous"/><link href="https://fonts.googleapis.com" rel="preconnect"/><link href="https://fonts.gstatic.com" rel="preconnect" crossorigin="anonymous"/>
1<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js" type="text/javascript"></script>
1<script type="text/javascript">WebFont.load({  google: {    families: ["Open Sans:300,300italic,400,400italic,600,600italic,700,700italic,800,800italic","Barlow:300,400,500,600,700","DM Sans:300,400,500,600,700","Plus Jakarta Sans:300,400,500,600,700"]  }});</script>
1<script type="text/javascript">!function(o,c){var n=c.documentElement,t=" w-mod-";n.className+=t+"js",("ontouchstart"in o||o.DocumentTouch&&c instanceof DocumentTouch)&&(n.className+=t+"touch")}(window,document);</script>
1<link href="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68cf1cb49e59be031d2f13dc_favicon-32x32.png" rel="shortcut icon" type="image/x-icon"/><link href="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68cf1d169359063e6094caad_logo-256x256.png" rel="apple-touch-icon"/>
1<script type="application/ld+json">
2{
3  "@context": "https://schema.org",
4  "@type": "FAQPage",
5  "mainEntity": [
6    {
7      "@type": "Question",
8      "acceptedAnswer": {
9        "@type": "Answer",
10        "text": "Es completamente gratis. Puedes hacer tantas preguntas como quieras sin ningún coste ni compromiso de contratación."
11      },
12      "name": "¿Cuánto cuesta preguntar a un entrenador personal?"
13    },
14    {
15      "@type": "Question",
16      "acceptedAnswer": {
17        "@type": "Answer",
18        "text": "El tiempo medio de respuesta es de menos de 24 horas, aunque puede variar según la categoría y disponibilidad."
19      },
20      "name": "¿En cuánto tiempo responden los entrenadores?"
21    },
22    {
23      "@type": "Question",
24      "acceptedAnswer": {
25        "@type": "Answer",
26        "text": "Entrenadores personales certificados verificados por RedEntrenadores, activos en la plataforma."
27      },
28      "name": "¿Quién responde las preguntas?"
29    },
30    {
31      "@type": "Question",
32      "acceptedAnswer": {
33        "@type": "Answer",
34        "text": "Sí, no es necesario compartir datos personales para publicar tu pregunta."
35      },
36      "name": "¿Puedo hacer una pregunta de forma anónima?"
37    },
38    {
39      "@type": "Question",
40      "acceptedAnswer": {
41        "@type": "Answer",
42        "text": "Sí, desde la respuesta puedes ver el perfil del entrenador y contactar con él si quieres profundizar."
43      },
44      "name": "¿Puedo contactar directamente con el entrenador que me respondió?"
45    }
46  ]
47}
48</script>
48<script>(function(w,i,g){w[g]=w[g]||[];if(typeof w[g].push=='function')w[g].push.apply(w[g],Array.isArray(i)?i:[i]);})(window,['G-GSEP18CGXX'],'google_tags_first_party');</script>
48<script async="" src="/avljl2rk9q5pNjhjNWE3MjljNzFmYTNmMzc4NThmYmJl/peOuxr4X3pGz_RiXea8PdbkeTQo"></script>
48<script type="text/javascript">window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('set', 'developer_id.dZGVlNj', true);gtag('set', 'developer_id.dYWYxNW', true);gtag('js', new Date());gtag('config', 'G-GSEP18CGXX');</script>
48<script type="text/javascript">!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';n.agent='plwebflow';n.queue=[];t=b.createElement(e);t.async=!0;t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,document,'script','https://connect.facebook.net/en_US/fbevents.js');fbq('init', '1689976748571004');fbq('track', 'PageView');</script>
48<!-- Google Tag Manager -->
49<script>
vendor: 327 bytes, lines 49-53
49(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
50new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
51j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
52'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
53})(window,document,'script','dataLayer','
53GTM-KBD83QKS
vendor: 3 bytes, line 53
53');
53</script>
53
54<!-- End Google Tag Manager -->
55
56<!-- Start cookieyes banner --> 
57<script id="cookieyes" type="text/javascript" src="https://cdn-cookieyes.com/client_data/3b90490e3918e885f5cdab3d/script.js"></script>
57 
58<!-- End cookieyes banner -->
59
60
61<!-- Hotjar / Contentsquare: async porque es analitica pura, nada en el repo
62     depende de que este cargado (grep sin resultados fuera de backups de este
63     mismo bloque). Eran 623 KB bloqueando el primer pintado: el mayor peso de
64     los 4760ms de "solicitudes que bloquean el renderizado" de PageSpeed. -->
65<script src="https://t.contentsquare.net/uxa/e67e16087d6b8.js" async></script>
65
66
67
68<!-- Memberstack: solo fuera de /lp/*. Fuente y porque:
69     widgets/custom-code-memberstack-por-ruta.html -->
70<script>
71(function () {
72  // Las landings de paid no tienen nada que dependa de Memberstack. Cualquier
73  // otra ruta lo carga igual que siempre, bloqueando, porque los widgets del
74  // panel dan por hecho que $memberstackDom ya existe cuando ellos corren.
75  if (/^\/lp(\/|$)/.test(location.pathname)) return;
76
77  document.write(
78    '
78<script data-memberstack-app="app_cmgl62z3b00kw0tt21gk97j2x"' +
79    ' src="https://static.memberstack.com/scripts/v1/memberstack.js"' +
80    ' type="text/javascript"><\/script>'
81  );
82})();
83</script>
83
84
85<!-- Para que los dropdown sean blancos --> 
86<style>
87.w-dropdown-list {
88  background-color: white !important;
89}
90
91.w-dropdown.w--open .w-dropdown-list {
92  background-color: white !important;
93}
94
95.search-dropdown {
96  background-color: white !important;
97}
98</style>
99
100
101<!-- Limita el texto descriptivo de cada entrenador -->
102<script>
103document.addEventListener("DOMContentLoaded", function() {
104  const elements = document.querySelectorAll(".limit-text");
105
106  elements.forEach(el => {
107    const text = el.textContent.trim();
108    
109    if (text.length > 80) {
110      el.textContent = text.substring(0, 80) + "...";
111    }
112  });
113});
114</script>
114
115
116
117<!-- Canonical autorreferenciado (auditoria SEO 2026-08) -->
118<script>
119(function () {
120  if (document.querySelector('link[rel="canonical"]')) return;
121  var p = location.pathname;
122  while (p.length > 1 && p.charAt(p.length - 1) === '/') p = p.slice(0, -1);
123  var l = document.createElement('link');
124  l.rel = 'canonical';
125  l.href = 'https://www.redentrenadores.com' + (p || '/');
126  document.head.appendChild(l);
127})();
128</script>
128
129
130
131<!-- Impide que se descargue el video de fondo de la seccion de FAQ.
132     Es decorativo, esta al final de la pagina y su webm pesa 4,6 MB en las 61
133     paginas que lo llevan (home, listado y las 59 de ciudad). Ocultarlo por CSS
134     no basta: Webflow crea el <video> con autoplay y el navegador se lo baja
135     igual aunque el contenedor este en display:none (comprobado: paused=false,
136     buffered=15s). Sin data-video-urls el script de Webflow no crea el <video>,
137     asi que no hay nada que pedir.
138     Para revertirlo, basta con borrar este bloque y quitar el display:none de
139     la clase bg-video_faqs.
140     ACTUALIZACION 2026-08-11: en /entrenadores-personales/* el elemento se
141     borro directamente (ver commit), asi que este bloque ya no hace nada ahi.
142     Sigue haciendo falta en / y /entrenador-personal hasta que se borre el
143     elemento tambien en esas dos plantillas (vive dentro de un componente que
144     la API no puede recorrer). -->
145<script>
146document.addEventListener('DOMContentLoaded', function () {
147  document.querySelectorAll('[data-video-urls]').forEach(function (el) {
148    el.removeAttribute('data-video-urls');
149  });
150});
151</script>
151
152
153
154<!-- ══════════════════════════════════════════════════════════
155     AMPLITUDE - analitica + session replay (2026-08-14)
156     El proyecto no tiene bundler (no existe package.json en el repo), asi que
157     en vez de `npm install @amplitude/unified` se carga EL MISMO paquete como
158     modulo ESM desde jsDelivr por import() dinamico. La llamada de init es la
159     de la documentacion: initAll, no init.
160
161     CONSENTIMIENTO: no arranca hasta que cookieyes tiene la categoria
162     "analytics" en yes. Esto vale tanto para la analitica como para Session
163     Replay, que graba el 100% de las sesiones (sampleRate 1) y sin
164     consentimiento no puede grabar nada. Verificado sobre el propio banner.js
165     que sirve el sitio: la cookie es "cookieyes-consent" con pares
166     clave:valor separados por coma, las categorias son necessary / functional
167     / analytics / performance / advertisement, y al aceptar se despacha
168     "cookieyes_consent_update" sobre document. Bajo RGPD analytics arranca en
169     "no", asi que por defecto aqui no se carga nada.
170
171     El stub de window.amplitude existe desde el primer momento y encola los
172     track() previos a la carga del modulo; al inicializar se vacia la cola. Si
173     el usuario nunca da su consentimiento, la cola se descarta sin enviarse.
174     Los embeds de widgets/ llaman a amplitude.track(...) contando con esto.
175     ══════════════════════════════════════════════════════════ -->
176<script>
177(function () {
178  window.amplitude = window.amplitude || { _q: [], track: function () { this._q.push(['track', arguments]); } };
179
180  function hayConsentimiento() {
181    var m = document.cookie.match(/(?:^|;\s*)cookieyes-consent=([^;]*)/);
182    if (!m) return false;
183    return decodeURIComponent(m[1]).split(',').indexOf('analytics:yes') !== -1;
184  }
185
186  var arrancado = false;
187  function arrancar() {
188    if (arrancado || !hayConsentimiento()) return;
189    arrancado = true;
190    import('https://cdn.jsdelivr.net/npm/@amplitude/unified@1/+esm').then(function (amplitude) {
191      var pendientes = (window.amplitude && window.amplitude._q) || [];
192      // Amplitude ingestion key — public by design; move to an env var when you set up environments.
193      amplitude.initAll('21ce582269eabdf7c4208d1960a7731e', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1}});
194      window.amplitude = amplitude;
195      pendientes.forEach(function (c) { amplitude[c[0]].apply(null, c[1]); });
196    }).catch(function () { arrancado = false; });
197  }
198
199  arrancar();
200  document.addEventListener('cookieyes_consent_update', arrancar);
201})();
202</script>
202
203
204<!-- ══════════════════════════════════════════════════════════
205     SIMULAR PANEL DE ENTRENADOR (2026-08-21)
206     Deja que el equipo recorra /panel-entrenador viendo los datos de un
207     entrenador concreto, sin entrar con su cuenta y sin poder tocar nada. Lo
208     enciende la pestana "Simular entrenador" de /admin, que abre el panel con
209     ?simular=<token>. Solo lectura: el shim corta cualquier metodo que no sea
210     GET, y las Edge Functions lo rechazan tambien en el servidor.
211
212     Va aqui y no como registered script porque esos tienen un tope de 2.000
213     caracteres y este no cabe (son 10 KB). Se pega MINIFICADO: la fuente
214     legible, con el porque de cada parte, es
215     widgets/custom-code-simular-entrenador.js del repo, y lo de abajo sale de
216     widgets/custom-code-simular-entrenador.min.js. Para cambiarlo: editar la
217     fuente, regenerar con terser y volver a pegar. No editar esto a mano.
218
219     Se corta solo en cuanto la ruta no empieza por /panel-entrenador, asi que
220     en el resto del sitio no hace nada mas que existir.
221     ══════════════════════════════════════════════════════════ -->
222<script>
223/*! Simular panel de entrenador — RedEntrenadores
224 *  Version minificada. La fuente legible, con el porque de cada parte, esta en
225 *  widgets/custom-code-simular-entrenador.js del repo. Para cambiarlo: editar
226 *  ese fichero, regenerar con `npx terser` y volver a pegar aqui. No editar
227 *  este bloque a mano. */
228!function(){var e='https://yrzanatzshdpvypazgeb.supabase.co/functions/v1/',t='rt-simulacion',r=['comprobar-titulo'],n=/^rt-perfil-/;if(0===location.pathname.indexOf('/panel-entrenador')){var o=new URL(location.href),a=o.searchParams.get('simular');if(a){!function(e){try{sessionStorage.setItem(t,JSON.stringify(e))}catch(e){}}({token:a,nombre:o.searchParams.get('simular_nombre')||'este entrenador'}),o.searchParams.delete('simular'),o.searchParams.delete('simular_nombre');var i=o.pathname+(o.searchParams.toString()?'?'+o.searchParams:'')+o.hash;history.replaceState(null,'',i)}var s=function(){try{return JSON.parse(sessionStorage.getItem(t)||'null')}catch(e){return null}}();if(s&&s.token)if(g(s.token)<Date.now())h();else{window.rtSimulacion={entrenador:s.nombre,expira:new Date(g(s.token))};var l='rt-sim:'+s.token.split('.')[1]+':',c=Storage.prototype.getItem,u=Storage.prototype.setItem,d=Storage.prototype.removeItem;Storage.prototype.getItem=function(e){return c.call(this,v(this)?b(e):e)},Storage.prototype.setItem=function(e,t){return u.call(this,v(this)?b(e):e,t)},Storage.prototype.removeItem=function(e){return d.call(this,v(this)?b(e):e)};var m=window.fetch;if(window.fetch=function(t,n){var o='string'==typeof t?t:t&&t.url||'';if(0!==o.indexOf(e))return m.apply(this,arguments);var a=(n&&n.method||t&&t.method||'GET').toUpperCase(),i=o.slice(54).split(/[/?#]/)[0];if('GET'!==a&&'OPTIONS'!==a&&r.indexOf(i)<0)return function(){var e=document.getElementById('rt-sim-aviso');if(!e)return;e.textContent='No se ha guardado nada: el panel simulado es solo de lectura.',e.hidden=!1,clearTimeout(f),f=setTimeout(function(){e.hidden=!0},5e3)}(),Promise.resolve(new Response('simulacion_solo_lectura',{status:403}));if('string'==typeof t){var l=new Headers(n&&n.headers||{});l.set('Authorization','Bearer '+s.token);var c={};for(var u in n||{})c[u]=n[u];return c.headers=l,m.call(this,t,c)}var d=new Request(t,n);return d.headers.set('Authorization','Bearer '+s.token),m.call(this,d)},window.$memberstackDom)y(window.$memberstackDom);else try{var p;Object.defineProperty(window,'$memberstackDom',{configurable:!0,get:function(){return p},set:function(e){p=y(e)}})}catch(e){}var f=null;'loading'===document.readyState?document.addEventListener('DOMContentLoaded',x):x()}}function h(){try{sessionStorage.removeItem(t)}catch(e){}}function g(e){var t=String(e||'').split('.');return 4===t.length?Number(t[2]):0}function b(e){return n.test(String(e))?l+e:e}function v(e){try{return e===window.localStorage}catch(e){return!1}}function y(e){if(!e||'function'!=typeof e.getMemberCookie||e.__rtSimParcheado)return e;var t=e.getMemberCookie.bind(e);return e.getMemberCookie=function(){return Promise.resolve().then(t).then(function(e){return e||s.token}).catch(function(){return s.token})},e.__rtSimParcheado=!0,e}function x(){if(!document.getElementById('rt-sim-banner')){var e=document.createElement('style');e.textContent=['#rt-sim-banner{position:fixed;left:0;right:0;bottom:0;z-index:10000;','background:#FEF3C7;border-top:1px solid #92400E;color:#92400E;','font-family:\'Plus Jakarta Sans\',-apple-system,system-ui,sans-serif;font-size:13px;','display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:10px 16px;box-sizing:border-box}','#rt-sim-banner .rt-sim-texto{flex:1;
228min-width:200px;margin:0}','#rt-sim-banner strong{color:#1B1E22}','#rt-sim-banner .rt-sim-salir{padding:9px 18px;border:1px solid #089F3B;border-radius:8px;','background:#fff;color:#089F3B;text-transform:uppercase;font-weight:700;letter-spacing:0.03em;','font-size:11px;font-family:inherit;cursor:pointer;flex-shrink:0}','#rt-sim-banner .rt-sim-salir:hover{background:#EAF3DE}','#rt-sim-aviso{flex-basis:100%;background:#FCEBEB;color:#791F1F;border-radius:8px;','padding:10px 12px;font-size:13px;margin:0}','#rt-sim-aviso[hidden]{display:none}','body{padding-bottom:64px}'].join(''),document.head.appendChild(e);var t,r=document.createElement('div');r.id='rt-sim-banner',r.innerHTML='<p class="rt-sim-texto">Estás viendo el panel de <strong>'+(t=s.nombre,String(null==t?'':t).replace(/[&<>"']/g,function(e){return{'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;','\'':'&#39;'}[e]})+'</strong>. Solo lectura: nada de lo que hagas aquí se guarda.</p><button type="button" class="rt-sim-salir">Salir de la simulación</button><p id="rt-sim-aviso" hidden></p>'),document.body.appendChild(r),r.querySelector('.rt-sim-salir').addEventListener('click',function(){h(),function(){try{for(var e=[],t=0;t<localStorage.length;t++){var r=localStorage.key(t);r&&0===r.indexOf('rt-sim:')&&e.push(r)}e.forEach(function(e){d.call(localStorage,e)})}catch(e){}}(),location.href='/admin'})}}}();
229
230</script>
230<!-- Canonical estatico (2026-08-19)
231     Los 8 chips de categoria de esta pagina son enlaces reales a
232     /pregunta-al-experto?categoria=..., que devuelven el mismo title, la misma
233     meta description y el mismo contenido base. Ubersuggest los marcaba como
234     metadescripciones duplicadas.
235
236     El script del <head> del sitio ya inyecta este mismo canonical por JS
237     (construido desde location.pathname, tirando la query), pero solo lo ve
238     quien ejecuta JS. Aqui va en el HTML servido, para el resto.
239
240     GOTCHA: ese script tiene un "si ya hay canonical, no hagas nada", pero NO
241     se aparta. El custom code del sitio va antes que el de la pagina, asi que
242     cuando el script corre este <link> todavia no esta parseado y acaba metiendo
243     un segundo canonical. Por eso el script de abajo: se queda con este y borra
244     cualquier otro. Solo afecta a esta pagina; el HTML servido ya trae uno solo.
245     Si algun dia se ponen canonicals estaticos en mas paginas, sale mas a cuenta
246     retrasar el script del sitio a DOMContentLoaded que repetir esto. -->
247<link rel="canonical" id="canonical-fijo" href="https://www.redentrenadores.com/pregunta-al-experto">
248<script>
249(function () {
250  function limpiar() {
251    var todos = document.querySelectorAll('link[rel="canonical"]');
252    for (var i = 0; i < todos.length; i++) {
253      if (todos[i].id !== 'canonical-fijo' && todos[i].parentNode) {
254        todos[i].parentNode.removeChild(todos[i]);
255      }
256    }
257  }
258  limpiar();
259  if (document.readyState === 'loading') {
260    document.addEventListener('DOMContentLoaded', limpiar);
261  }
262})();
263</script>
vendor: 1 bytes, line 263
263
264<script src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe%2F689e5ba67671442434f3ca35%2F6a7b1ad624d451ff7ab95b57%2Fdeep_link_panel_tras_login-0.0.1.js" type="text/javascript"></script>
264<script src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe%2F689e5ba67671442434f3ca35%2F6a8188e2745a991cf146025e%2Fredirect_urls_antiguas_panel-1.0.0.js" type="text/javascript"></script>
264<script src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe%2F689e5ba67671442434f3ca35%2F6a90ae0a69c0c92f2517a3b6%2Fawintracking2-1.0.0.js" type="text/javascript"></script>
264<script src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe%2F689e5ba67671442434f3ca35%2F6aa83d1b2d69974135a99d99%2Fguardian_sesion_panel-1.0.0.js" type="text/javascript"></script>
264<script src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe%2F689e5ba67671442434f3ca35%2F6aaed9e15b8290737a4ca482%2Fguardia_onboarding_entrenador-1.0.0.js" type="text/javascript"></script>
264<script type="text/javascript">window.__WEBFLOW_CURRENCY_SETTINGS = {"currencyCode":"USD","symbol":"$","decimal":".","fractionDigits":2,"group":",","template":"{{wf {\"path\":\"symbol\",\"type\":\"PlainText\"} }} {{wf {\"path\":\"amount\",\"type\":\"CommercePrice\"} }} {{wf {\"path\":\"currencyCode\",\"type\":\"PlainText\"} }}","hideDecimalForWholeNumbers":false};</script>
264</head><body><div data-animation="default" class="rl_navbar5_component padding-top-medium w-nav" data-easing2="ease" data-easing="ease" data-collapse="medium" role="banner" data-duration="400" data-wf--header-v6--variant="base" ms-code-scroll-offset="40"><div class="rl_navbar5_container"><a href="/" class="rl_navbar5_logo-link w-nav-brand"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68c5a7f45b0c2bd091e87d5c_RedEntrenadores_Cropped_400px.avif" loading="lazy" width="63" sizes="63px" alt="Logotipo de RedEntrenadores" srcset="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68c5a7f45b0c2bd091e87d5c_RedEntrenadores_Cropped_400px-p-500.avif 500w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68c5a7f45b0c2bd091e87d5c_RedEntrenadores_Cropped_400px-p-800.avif 800w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68c5a7f45b0c2bd091e87d5c_RedEntrenadores_Cropped_400px.avif 2246w" class="rl_navbar5_logo"/></a><nav role="navigation" data-ms-content="!members" class="rl_navbar5_menu w-nav-menu"><div class="nav-pills-grupo"><a href="/pregunta-al-experto" aria-current="page" class="nav-pill-item w--current">Pregunta al experto</a><div class="nav-pill-sep"></div><a href="/donde-entrenar" class="nav-pill-item">Dónde entrenar</a></div><div class="rl_navbar5_menu-left"><div data-hover="true" data-delay="300" class="rl_navbar5_menu-dropdown w-dropdown"><div class="rl_navbar5_dropdown-toggle w-dropdown-toggle"><div class="rl-dropdown-icon w-embed"><svg width=" 100%" height=" 100%" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
265<path fill-rule="evenodd" clip-rule="evenodd" d="M2.55806 6.29544C2.46043 6.19781 2.46043 6.03952 2.55806 5.94189L3.44195 5.058C3.53958 4.96037 3.69787 4.96037 3.7955 5.058L8.00001 9.26251L12.2045 5.058C12.3021 4.96037 12.4604 4.96037 12.5581 5.058L13.4419 5.94189C13.5396 6.03952 13.5396 6.19781 13.4419 6.29544L8.17678 11.5606C8.07915 11.6582 7.92086 11.6582 7.82323 11.5606L2.55806 6.29544Z" fill="currentColor"/>
266</svg></div><div class="rl_navbar5_link-text">Recursos</div></div><nav class="rl_navbar5_dropdown-list-copy w-dropdown-list"><div class="rl_navbar5_container"><div class="rl_navbar5_dropdown-content"><div class="rl_navbar5_dropdown-content-left"><div class="rl_navbar5_dropdown-link-list"><h4 class="rl_navbar5_dropdown-column-text">Herramientas gratis</h4><a href="/calculadoras/precio-entrenador-personal" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/6a637ee83ad287f97c093036_euro%20coin.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Cuánto cuesta un entrenador</div><p class="rl_navbar5_text-small">La horquilla real por sesión y al mes, con 126 tarifas publicadas.</p></div></a><a href="/calculadoras/donde-te-conviene-entrenar" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/6a6333b155392ae72dc3544a_icono%20lugar.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">¿Dónde te conviene entrenar?</div><p class="rl_navbar5_text-small">Gimnasio, a domicilio, al aire libre u online: seis preguntas y la pega de cada uno.</p></div></a><a href="/calculadoras/plan-primer-mes" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176197f33ec0043320f2a_dumbbell-01.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Tu primer mes de entrenamiento</div><p class="rl_navbar5_text-small">Cuántas sesiones y con qué foco cada semana, según los días que tengas y dónde entrenes.</p></div></a><a href="/calculadoras/calculadora-calorias" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176422d38a3a4f0d2241b_weight-scale.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Calculadora de calorías</div><p class="rl_navbar5_text-small">Calcula las calorías diarias que necesitas según tu edad, peso y nivel de actividad física.</p></div></a><a href="/calculadoras/calculadora-proteina" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a17619966cea24bd9bfe87_ta
266pe-measure.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Calculadora de proteína</div><p class="rl_navbar5_text-small">Cuánta proteína necesitas al día según tu peso y tu objetivo, con las calorías al lado.</p></div></a><a href="/calculadoras/calculadora-imc" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/6ac016796b7d40191f7cf036_indice-imc.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Calculadora de IMC</div><p class="rl_navbar5_text-small">Tu índice de masa corporal y el rango de peso que corresponde a tu altura.</p></div></a><a href="/calculadoras/test-nivel-fitness" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176695fe1a56f4bbbe03b_workout-run.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Test de nivel de fitness</div><p class="rl_navbar5_text-small">Evalúa tu condición física actual y descubre qué tipo de entrenador necesitas.</p></div></a><a href="/calculadoras/pruebas-fisicas-oposiciones" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69c411fa43d84eac3751591f_medal.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Pruebas físicas de oposición</div><p class="rl_navbar5_text-small">Compara tus marcas con las que te piden y descubre cuál es tu punto débil.</p></div></a></div><div class="rl_navbar5_dropdown-link-list"><h4 class="rl_navbar5_dropdown-column-text">Entrenadores por ciudad</h4><a href="https://www.redentrenadores.com/entrenadores-personales/madrid" target="_blank" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176ab30392e5a11168bd3_icon%20simple.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Madrid</div></div></a><a href="https://www.redentrenadores.com/entrenadores-personales/barcelona" target="_blank" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176ab30392e5a11168bd3_icon%20simple.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Barcelona</div></div></a><a href="https://www.redentrenadores.com/entrenadores-personales/zaragoza" target="_blank" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176ab30392e5a11168bd3_icon%20simple.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Zaragoza</div></div></a><a href="https://www.redentrenadores.com/entrenadores-personales/valencia" target="_blank" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176ab30392e5a11168bd3_icon%20simple.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Valencia</div></div></a><a href="https://www.redentrenadores.com/entrenadores-personales/sevilla" target="_blank" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176ab30392e5a11168bd3_icon%20simple.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Sevilla</div></div></a><a href="https://www.redentrenadores.com/entrenadores-personales/malaga" target="_blank" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176ab30392e5a11168bd3_icon%20simple.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Málaga</div></div></a><a href="https://www.redentrenadores.com/entrenadores-personales/murcia" target="_blank" class="rl_navbar5_dropdown-link w-inline-block"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176ab30392e5a11168bd3_icon%20simple.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div id="w-node-_64a5fb61-5186-864a-70be-7b9103a4a59d-03a4a53d" class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Murcia</div></div></a><a href="/donde-entrenar" class="rl_navbar5_dropdown-link"><div class="rl_navbar5_icon-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/69a176ab30392e5a11168bd3_icon%20simple.svg" loading="lazy" alt="" class="rl_navbar5_icon"/></div><div class="rl_navbar5_item-right"><div class="rl_navbar5_item-title">Dónde entrenar</div></div></a><a id="w-node-_64a5fb61-5186-864a-70be-7b9103a4a5a0-03a4a53d" href="/entrenador-personal" class="btn-green indide-menu w-inline-block"><div>Ver más ciudades</div></a></div></div><div class="rl_navbar5_dropdown-content-right"><div class="rl_navbar5_dropdown-content-wrapper"><h4 class="rl_navbar5_dropdown-column-text white">Artículos destacados</h4><div id="w-node-_64a5fb61-5186-864a-70be-7b9103a4a5a7-03a4a53d" class="rl_navbar5_blog-list-wrapper"><div class="rl_navbar5_blog-list"><a href="https://www.redentrenadores.com/blog/precio-entrenador-personal-online" target="_blank" class="rl_navbar5_blog-item w-inline-block"><div class="rl_navbar5_blog-image-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d4c5d1b40a16e775245_pexels-cottonbro-4064840.webp" loading="lazy" sizes="100vw" srcset="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d4c5d1b40a16e775245_pexels-cottonbro-4064840-p-500.webp 500w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d4c5d1b40a16e775245_pexels-cottonbro-4064840-p-800.webp 800w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d4c5d1b40a16e775245_pexels-cottonbro-4064840-p-1080.webp 1080w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d4c5d1b40a16e775245_pexels-cottonbro-4064840-p-1600.webp 1600w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d4c5d1b40a16e775245_pexels-cottonbro-4064840-p-2000.webp 2000w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d4c5d1b40a16e775245_pexels-cottonbro-4064840-p-2600.webp 2600w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d4c5d1b40a16e775245_pexels-cottonbro-4064840-p-3200.webp 3200w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d4c5d1b40a16e775245_pexels-cottonbro-4064840.webp 4152w" alt="Persona realizando un entrenamiento personal" class="rl_navbar5_blog-image"/></div><div class="rl_navbar5_large-item-content"><div class="rl_navbar5_item-title white">¿Cuánto cuesta realmente un entrenador personal online?</div><div class="rl_navbar5_item-link">Leer artículo</div></div></a><a href="https://www.redentrenadores.com/blog/tipos-entrenamiento-personal" target="_blank" class="rl_navbar5_blog-item w-inline-block"><div class="rl_navbar5_blog-image-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d6acffc97162b06e822_In-home-personal-training.webp" loading="lazy" sizes="100vw" srcset="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d6acffc97162b06e822_In-home-personal-training-p-500.webp 500w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d6acffc97162b06e822_In-home-personal-training.webp 600w" alt="Entrenador personal entrenando a un cliente a domicilio" class="rl_navbar5_blog-image"/></div><div class="rl_navbar5_large-item-content"><div class="rl_navbar5_item-title white">Tipos de entrenamiento personal: descubre cuál se adapta mejor a ti</div><div class="rl_navbar5_item-link">Leer artículo</div></div></a><a href="https://www.redentrenadores.com/blog/entrenador-personal-nutricionista-fisioterapeuta" target="_blank" class="rl_navbar5_blog-item w-inline-block"><div id="w-node-_64a5fb61-5186-864a-70be-7b9103a4a5c2-03a4a53d" class="rl_navbar5_blog-image-wrapper"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d7b2b95815407c9ec1e_pexels-olly-3813341.webp" loading="lazy" sizes="100vw" srcset="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d7b2b95815407c9ec1e_pexels-olly-3813341-p-500.webp 500w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d7b2b95815407c9ec1e_pexels-olly-3813341-p-800.webp 800w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d7b2b95815407c9ec1e_pexels-olly-3813341-p-1080.webp 1080w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d7b2b95815407c9ec1e_pexels-olly-3813341-p-1600.webp 1600w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d7b2b95815407c9ec1e_pexels-olly-3813341-p-2000.webp 2000w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d7b2b95815407c9ec1e_pexels-olly-3813341-p-2600.webp 2600w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d7b2b95815407c9ec1e_pexels-olly-3813341-p-3200.webp 3200w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/693c5d7b2b95815407c9ec1e_pexels-olly-3813341.webp 7680w" alt="Persona realizando un entrenamiento personal" class="rl_navbar5_blog-image"/></div><div class="rl_navbar5_large-item-content"><div class="rl_navbar5_item-title white">Entrenador personal, nutricionista y fisioterapeuta: el equipo perfecto para transformar tu cuerpo</div><div class="rl_navbar5_item-link">Leer artículo</div></div></a></div></div><div class="rl-button-group"><a href="/blog" class="rl-button-link w-inline-block"><div class="rl-button-link-text">Ver todos los artículos</div><div class="rl-button-link-icon w-embed">
266<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
267<path d="M6 3L11 8L6 13" stroke="CurrentColor" stroke-width="1.5"/>
268</svg></div></a></div></div><div class="rl_navbar5_dropdown-background-layer"></div></div></div></div></nav></div></div><div class="rl_navbar5_menu-right"><div data-hover="true" data-delay="0" data-ms-content="!members" class="drop-menu_entrenador w-dropdown"><div class="rl_navbar5_dropdown-toggle menu w-dropdown-toggle"><div class="rl-dropdown-icon w-icon-dropdown-toggle"></div><div>¿Eres entrenador?</div></div><nav class="dropdown-list-2 w-dropdown-list"><a href="/panel-entrenador" class="w-dropdown-link">Zona para entrenadores</a><a href="/registrate" class="w-dropdown-link">Activar perfil gratuito</a></nav></div><a href="https://www.redentrenadores.com/encuentra-entrenador" class="btn-green menu w-button">Buscar entrenador</a></div></nav><div class="w-layout-hflex"><a data-ms-content="members" href="/panel-entrenador" class="btn-green white_version w-button">Mi panel</a><div class="rl_navbar5_menu-button w-nav-button"><div data-ms-content="!members" class="rl_menu-icon"><div class="rl_menu-icon_line-top"></div><div class="rl_menu-icon_line-middle"><div class="rl_menu-icon_line-middle-inner"></div></div><div class="rl_menu-icon_line-bottom"></div></div></div></div></div><div class="w-embed w-script">
268<script>
269/* Webflow solo permite 991/767/479 como punto de colapso y su JS decide por su
270   cuenta (ignora el CSS). Con data-collapse=all lo gestiona en todos los anchos
271   y el <style> de abajo devuelve la fila a partir de 1180px. Este embed es hijo
272   del navbar y se ejecuta al parsearse, antes de que cargue webflow.js al final
273   del body, asi que el atributo ya esta puesto cuando Webflow inicializa.
274   El listener de resize hace falta porque, al creer que siempre esta colapsado,
275   Webflow ya no cierra el menu al cruzar un breakpoint: sin esto, quien abre el
276   menu a 1100px y luego amplia la ventana se queda con el menu enganchado
277   (Webflow mueve el <nav> dentro de .w-nav-overlay al abrir). Se cierra con su
278   propio boton para que Webflow restaure el DOM y limpie sus estilos inline. */
279(function(){
280  var s=document.currentScript,n=s&&s.parentElement&&s.parentElement.closest(".w-nav");
281  if(!n)return;
282  n.setAttribute("data-collapse","all");
283  var t;
284  window.addEventListener("resize",function(){
285    clearTimeout(t);
286    t=setTimeout(function(){
287      if(window.innerWidth<1180)return;
288      var b=n.querySelector(".w-nav-button.w--open");
289      if(b)b.click();
290    },100);
291  });
292})();
293</script>
293
294<style>
295/* El menu colapsa a hamburguesa hasta 1179px: por debajo de ~1165 no caben los
296   dos enlaces destacados y el flexbox aplasta el logo de 250 a 99px (medido).
297   Este bloque replica, para 992-1179px, las reglas que Webflow ya aplica bajo
298   991px al desplegar el menu. Revertir: borrar este embed entero y poner
299   "Collapse menu at" = medium en el Navbar.
300   Fuente en el repo: widgets/custom-code-header-colapso.css */
301@media screen and (min-width:992px) and (max-width:1179px){.nav-pills-grupo{flex-direction:column;width:100%;background-color:transparent;border-radius:0;padding:0;row-gap:0}.nav-pill-item{width:100%;justify-content:center;font-size:1.125rem;padding:.75rem 0}.nav-pill-sep{display:none}.rl_menu-icon_line-middle{background-color:#000;justify-content:center;align-items:center;width:24px;height:2px;margin-top:6px;margin-bottom:6px;padding-bottom:0;padding-right:0;display:flex}.rl_navbar5_dropdown-toggle{flex-flow:row;justify-content:center;align-items:center;min-width:250px;padding:.75rem 0;font-size:1.125rem;display:flex}.rl_navbar5_dropdown-toggle.menu{width:100%;min-width:100%}.rl_navbar5_dropdown-toggle.menu.profile{justify-content:flex-end;align-items:center}.rl_navbar5_blog-item{width:100%}.rl_menu-icon_line-top{background-color:#000;width:24px;height:2px;padding-bottom:0;padding-right:0}.rl_menu-icon{flex-direction:column;justify-content:center;align-items:center;width:48px;height:48px;margin-right:-.5rem;padding-bottom:0;padding-right:0;display:flex}.rl_navbar5_dropdown-background-layer{border-radius:4px;min-width:100%}.rl_navbar5_dropdown-content-left{align-content:start;align-items:start;padding-left:0;padding-right:0}.rl_navbar5_large-item-content{width:100%}.rl_navbar5_dropdown-content{flex-direction:column}.rl_navbar5_menu-button{flex-flow:row}.rl_navbar5_menu-button.w--open{background-color:#0000}.rl_navbar5_blog-image{object-fit:cover}.rl_navbar5_blog-list{width:100%}.rl_menu-icon_line-middle-inner{width:4px;height:0;padding-bottom:0;padding-right:0}.rl-dropdown-icon{grid-column-gap:0px;grid-row-gap:0px;justify-content:center;align-items:center;inset:auto 0% auto auto}.rl_menu-icon_line-bottom{background-color:#000;width:24px;height:2px;padding-bottom:0;padding-right:0}.rl_navbar5_menu-right{flex-direction:column;grid-template-rows:auto auto;grid-template-columns:1fr;align-items:stretch;margin-top:1rem}.rl_navbar5_menu{-webkit-text-fill-color:inherit;background-color:#fff;background-clip:border-box;border-bottom:1px solid #000;align-items:flex-start;height:100vh;margin-left:0;padding:1rem 5% 15rem;position:absolute;overflow:auto}.rl_navbar5_dropdown-content-right{align-items:flex-start;max-width:none}.rl_navbar5_menu-left{flex-flow:column;justify-content:center;align-items:center}.rl_navbar5_menu-dropdown{flex-flow:column;width:100%;display:flex;position:relative}.rl_navbar5_dropdown-content-wrapper{width:100%}.drop-menu_entrenador{width:100%}.drop-menu_entrenador.profile{margin-left:0}.rl_navbar5_dropdown-list-copy{position:relative;overflow:hidden}.rl_navbar5_dropdown-list-copy.w--open{border-style:none;align-items:flex-start;min-width:auto;margin-bottom:1.5rem;padding:0 0 0;position:relative;top:0;left:0;right:0}}
302@media screen and (min-width:1180px){.w-nav[data-collapse=all] .w-nav-menu{display:flex}.w-nav[data-collapse=all] .w-nav-button{display:none}}
303</style></div></div><div class="w-layout-vflex flex-block-16"><section class="re-hero"><div class="re-hero__wrap"><div class="re-hero__content"><div class="re-hero__eyebrow"><span class="re-hero__dot"></span><span class="re-hero__eyebrow-text">Entrenadores respondiendo ahora</span></div><h1 class="re-hero__title">Pregunta a un entrenador personal certificado</h1><p class="re-hero__subtitle">Escribe tu duda y varios entrenadores certificados te responden, casi siempre el mismo día. Sin registro.</p><a href="#formulario" class="re-hero__cta">Escribe tu pregunta gratis ↓</a></div><div class="w-embed w-script"><!-- RedEntrenadores — /pregunta-al-experto · hero en dos columnas + formulario en dos pasos
304     Rediseño 2026-08-19 (prototipo de Figma Make "Optimizar formulario cuestionario").
305
306     DÓNDE VIVE: es el único HtmlEmbed de la landing (page 6908a45a566828a761f9d274) y está
307     dentro de `.re-hero__wrap`, justo después de `.re-hero__content` (antes estaba en su
308     propia sección `.re-form-section`, que ahora queda oculta en el Designer).
309
310     QUÉ PINTA: la tarjeta del formulario (columna derecha) y la fila de cifras (columna
311     izquierda, bajo el subtítulo). El eyebrow, el H1 y el subtítulo siguen siendo elementos
312     nativos de Webflow: aquí solo se les cambia el estilo. El CSS convierte `.re-hero__wrap`
313     en un grid de dos columnas y el `.w-embed` que envuelve este embed se hace `display:
314     contents` para que la tarjeta y las cifras sean items del grid.
315
316     DATOS: inserta en `preguntas` por la REST de Supabase con la clave publicable. No pide
317     categoría: llega como `sin-clasificar` y la asigna `avisar-pregunta-pendiente` al
318     recibir la pregunta (ver supabase/functions/avisar-pregunta-pendiente). Si el texto
319     pasa de 25 palabras se parte en `pregunta` (título) + `detalle`, como hacía el
320     formulario antiguo con sus dos campos.
321
322     Al editar: cambiar este fichero Y el `code` del embed en Webflow (no se sincronizan). -->
323
324<div id="formulario" class="pae-card" data-paso="1">
325
326  <!-- Paso 1: la pregunta -->
327  <form class="pae-paso pae-paso--1" id="pae-form-1" novalidate>
328    <label class="pae-card__titulo" for="pae-pregunta">¿Cuál es tu duda?</label>
329    <textarea id="pae-pregunta" class="pae-field pae-field--area" rows="4"
330      placeholder="Escríbela con tus palabras, aunque sea corta" autocomplete="off"></textarea>
331    <p class="pae-error" id="pae-error-1" role="alert"></p>
332    <button type="submit" class="pae-btn" id="pae-continuar">Continuar →</button>
333  </form>
334
335  <!-- Paso 2: quién pregunta -->
336  <form class="pae-paso pae-paso--2" id="pae-form-2" novalidate hidden>
337    <div class="pae-resumen">
338      <p class="pae-resumen__texto" id="pae-resumen-texto"></p>
339      <button type="button" class="pae-link" id="pae-editar">Editar</button>
340    </div>
341
342    <label class="pae-label" for="pae-nombre">Tu nombre</label>
343    <input id="pae-nombre" class="pae-field" type="text" placeholder="ej. María"
344      autocomplete="given-name" maxlength="80" />
345    <p class="pae-ayuda">Aparecerá junto a tu pregunta cuando se publique</p>
346
347    <label class="pae-label" for="pae-email">Tu email</label>
348    <input id="pae-email" class="pae-field" type="email" placeholder="[email protected]"
349      autocomplete="email" inputmode="email" maxlength="160" />
350    <p class="pae-ayuda">Te avisamos aquí en cuanto haya respuesta. No se publica.</p>
351
352    <!-- Trampa para bots: nunca visible ni rellenable por una persona -->
353    <div class="pae-hp" aria-hidden="true">
354      <label for="pae-web">Web</label>
355      <input id="pae-web" type="text" tabindex="-1" autocomplete="off" />
356    </div>
357
358    <div class="pae-pasos">
359      <p class="pae-pasos__titulo">Qué pasa ahora</p>
360      <ol class="pae-pasos__lista">
361        <li><span>1.</span>Varios entrenadores certificados leen tu pregunta.</li>
362        <li><span>2.</span>Te avisamos por email en cuanto tengas respuestas.</li>
363        <li><span>
3633.</span>Publicamos tu pregunta y las respuestas solo con tu nombre, para que ayuden a otros.</li>
364      </ol>
365    </div>
366
367    <p class="pae-error" id="pae-error-2" role="alert"></p>
368    <button type="submit" class="pae-btn" id="pae-enviar">Enviar pregunta</button>
369    <p class="pae-legal">Al enviar aceptas nuestra <a href="https://www.redentrenadores.com/legal/politica-de-privacidad" target="_blank" rel="noopener">política de privacidad</a>.</p>
370  </form>
371
372  <!-- Enviada -->
373  <div class="pae-paso pae-paso--ok" id="pae-ok" hidden>
374    <div class="pae-ok__icono" aria-hidden="true">
375      <svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>
376    </div>
377    <p class="pae-ok__titulo">¡Pregunta enviada!</p>
378    <p class="pae-ok__texto">La revisamos y, en cuanto esté publicada, te avisamos por email. Las respuestas suelen llegar el mismo día.</p>
379    <button type="button" class="pae-link pae-link--lg" id="pae-otra">Hacer otra pregunta</button>
380  </div>
381</div>
382
383<!-- Cifras bajo el subtítulo (columna izquierda). Los valores del HTML son reales a
384     fecha de 2026-08-19; el JS los recalcula al cargar y solo pisa los que consigue.
385     El número va en su propio span para animarlo como un recuento. -->
386<div class="pae-stats" role="group" aria-label="Cifras del servicio">
387  <div class="pae-stat"><span class="pae-stat__num"><span class="pae-stat__n" data-cifra="respuestas" data-valor="15">15</span><span class="pae-stat__unidad" data-unidad="respuestas"> respuestas</span></span><span class="pae-stat__txt">de media por pregunta</span></div>
388  <div class="pae-stat"><span class="pae-stat__num"><span class="pae-stat__n" data-cifra="horas" data-valor="3">3</span><span class="pae-stat__unidad" data-unidad="horas"> horas</span></span><span class="pae-stat__txt">hasta la primera respuesta</span></div>
389  <div class="pae-stat"><span class="pae-stat__num"><span class="pae-stat__n" data-cifra="entrenadores" data-valor="98">98</span></span><span class="pae-stat__txt">entrenadores certificados</span></div>
390</div>
391
392<style>
393  /* ---------- Hero: de bloque centrado sobre foto a dos columnas sobre blanco ---------- */
394  section.re-hero { background: #fff; }
395  .re-hero .re-hero__wrap {
396    display: grid;
397    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
398    /* La segunda fila absorbe lo que la tarjeta crezca (paso 2 es el doble de alta que
399       el 1): así el texto de la izquierda se queda quieto y la tarjeta se centra sola. */
400    grid-template-rows: auto 1fr;
401    align-items: start;
402    gap: 32px 64px;
403    max-width: 1152px;
404    margin: 0 auto;
405    padding: 56px 24px;
406  }
407  /* Webflow envuelve el embed en un div .w-embed: transparente para el grid, y sin los
408     pseudo-elementos de clearfix que Webflow le pone (serían items fantasma del grid). */
409  .re-hero .re-hero__wrap > .w-embed { display: contents; }
410  .re-hero .re-hero__wrap > .w-embed::before,
411  .re-hero .re-hero__wrap > .w-embed::after { content: none; display: none; }
412
413  .re-hero .re-hero__content { grid-column: 1; grid-row: 1; align-self: start; max-width: none; text-align: left; }
414  .re-hero .pae-stats        { grid-column: 1; grid-row: 2; align-self: start; }
415  .re-hero .pae-card         { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
416
417  .re-hero .re-hero__eyebrow {
418    display: inline-flex; align-items: center; gap: 8px;
419    background: #EAF3DE; border-radius: 999px; padding: 6px 12px; margin: 0 0 20px;
420  }
421  .re-hero .re-hero__dot { width: 8px; height: 8px; border-radius: 50%; background: #089F3B; animation: pae-pulso 2s cubic-bezier(.4, 0, .6, 1) infinite; }
422  .re-hero .re-hero__eyebrow-text { font-size: 12px; line-height: 16px; font-weight: 700; color: #089F3B; text-transform: none; letter-spacing: 0; }
423  @keyframes pae-pulso { 50% { opacity: .35; } }
424
425  .re-hero .re-hero__title { color: #1B1E22; text-align: left; font-size: 44px; line-height: 1.1; margin: 0 0 16px; }
426  .re-hero .re-hero__subtitle { color: #4B5563; text-align: left; font-size: 18px; line-height: 1.6; max-width: 520px; margin: 0; }
427  .re-hero .re-hero__cta { display: none; } /* el formulario ya está al lado: sobra la flecha */
428
429  /* ---------- Cifras ---------- */
430  .pae-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
431  .pae-stat { text-align: center; }
432  .pae-stat__num { display: block; font-size: 24px; line-height: 1.2; font-weight: 700; color: #089F3B; }
433  .pae-stat__n { font-variant-numeric: tabular-nums; } /* el recuento no baila de ancho */
434  .pae-stat__txt { display: block; font-size: 13px; line-height: 18px; color: #6b7280; margin-top: 2px; }
435
436  /* ---------- Tarjeta ---------- */
437  .pae-card {
438    background: #fff; border: 1px solid #d9dde2; border-radius: 12px;
439    box-shadow: 0 4px 14px rgba(0, 0, 0, .09); padding: 28px; box-sizing: border-box;
440    width: 100%; min-width: 0; scroll-margin-top: 90px;
441  }
442  .pae-card * { box-sizing: border-box; }
443  .pae-card__titulo { display: block; font-size: 16px; line-height: 22px; font-weight: 700; color: #1B1E22; margin: 0 0 14px; }
444  .pae-label { display: block; font-size: 14px; line-height: 20px; font-weight: 600; color: #1B1E22; margin: 0 0 6px; }
445  .pae-field {
446    width: 100%; border: 1px solid #d9dde2; border-radius: 8px; background: #fff;
447    padding: 11px 14px; font: inherit; font-size: 15px; line-height: 22px; color: #1B1E22; margin: 0;
448  }
449  .pae-field::placeholder { color: #6b7280; opacity: 1; }
450  .pae-field:focus { outline: none; border-color: #089F3B; box-shadow: 0 0 0 3px rgba(8, 159, 59, .15); }
451  .pae-field.is-invalid { border-color: #791F1F; }
452  .pae-field--area { resize: vertical; min-height: 112px; display: block; }
453  .pae-ayuda { font-size: 12px; line-height: 16px; color: #6b7280; margin: 6px 0 16px; }
454  .pae-error { font-size: 13px; line-height: 18px; color: #791F1F; margin: 0; min-height: 0; }
455  .pae-error:not(:empty) { margin: 8px 0 0; }
456  .pae-paso--1 .pae-error:not(:empty) { margin-bottom: 4px; }
457
458  /* Botón público (§51: 14px) con las mayúsculas que ya usan los CTA de esta página. */
459  .pae-btn {
460    display: block; width: 100%; margin-top: 14px; padding: 13px 18px; border: none; border-radius: 8px;
461    background: #089F3B; color: #fff; cursor: pointer; font: inherit; font-size: 14px; font-weight: 700;
462    letter-spacing: .03em; text-transform: uppercase; line-height: 18px; transition: background .15s;
463  }
464  .pae-btn:hover { background: #067A2D; }
465  .pae-btn:disabled { opacity: .5;
465 cursor: not-allowed; }
466  /* Enlaces-botón con área de toque de 44px sin mover el texto (padding compensado con margen). */
467  .pae-link { background: none; border: none; padding: 12px 8px; margin: -12px -8px; cursor: pointer; font: inherit; font-size: 13px; line-height: 20px; font-weight: 600; color: #089F3B; }
468  .pae-link:hover { color: #067A2D; text-decoration: underline; }
469  .pae-link:disabled { opacity: .5; cursor: not-allowed; text-decoration: none; }
470  .pae-link--lg { font-size: 14px; margin-top: -8px; }
471
472  .pae-resumen { display: flex; align-items: flex-start; gap: 12px; background: #F9FAFB; border-radius: 8px; padding: 12px 14px; margin: 0 0 18px; }
473  .pae-resumen__texto { flex: 1; min-width: 0; font-size: 14px; line-height: 20px; color: #1B1E22; margin: 0; max-height: 120px; overflow: auto; overflow-wrap: anywhere; }
474  .pae-resumen .pae-link { flex-shrink: 0; margin: -12px -8px -12px 0; }
475
476  .pae-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
477
478  .pae-pasos { background: #EAF3DE; border-radius: 12px; padding: 14px 16px; margin: 2px 0 0; }
479  .pae-pasos__titulo { font-size: 12px; line-height: 16px; font-weight: 700; color: #089F3B; margin: 0 0 8px; }
480  .pae-pasos__lista { list-style: none; margin: 0; padding: 0; }
481  .pae-pasos__lista li { display: flex; gap: 8px; font-size: 14px; line-height: 20px; color: #1B1E22; margin: 0 0 6px; padding: 0; }
482  .pae-pasos__lista li:last-child { margin-bottom: 0; }
483  .pae-pasos__lista li span { font-weight: 700; color: #089F3B; flex-shrink: 0; }
484
485  .pae-legal { font-size: 12px; line-height: 16px; color: #6b7280; text-align: center; margin: 10px 0 0; }
486  .pae-legal a { color: #4B5563; text-decoration: underline; }
487
488  .pae-paso--ok { text-align: center; padding: 16px 0 8px; }
489  .pae-ok__icono { width: 56px; height: 56px; border-radius: 50%; background: #EAF3DE; color: #089F3B; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; }
490  .pae-ok__titulo { font-size: 20px; line-height: 26px; font-weight: 700; color: #1B1E22; margin: 0 0 8px; }
491  .pae-ok__texto { font-size: 14px; line-height: 21px; color: #4B5563; margin: 0 auto 12px; max-width: 360px; }
492
493  /* ---------- Responsive ---------- */
494  @media (max-width: 991px) {
495    .re-hero .re-hero__wrap { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto auto; gap: 28px; padding: 40px 20px; }
496    .re-hero .re-hero__content { grid-row: 1; align-self: auto; }
497    .re-hero .pae-stats { grid-row: 2; align-self: auto; }
498    .re-hero .pae-card { grid-column: 1; grid-row: 3; align-self: auto; }
499    .re-hero .re-hero__title { font-size: 36px; }
500    .re-hero .re-hero__subtitle { max-width: none; }
501  }
502  @media (max-width: 767px) {
503    .pae-field { font-size: 16px; } /* por debajo de 16px iOS hace zoom al enfocar */
504    .pae-card { padding: 22px 18px; }
505  }
506  @media (max-width: 600px) {
507    /* Por debajo de ~550px "15 respuestas" no cabe en un tercio de columna: cada cifra
508       pasa a ser una fila con número y texto seguidos. */
509    .pae-stats { grid-template-columns: minmax(0, 1fr); gap: 6px; }
510    .pae-stat { display: flex; align-items: baseline; justify-content: center; gap: 8px; flex-wrap: wrap; }
511    .pae-stat__num { font-size: 20px; }
512    .pae-stat__txt { font-size: 13px; line-height: 18px; margin-top: 0; }
513  }
514  @media (max-width: 479px) {
515    .re-hero .re-hero__wrap { padding: 28px 16px 36px; gap: 24px; }
516    .re-hero .re-hero__title { font-size: 30px; }
517    .re-hero .re-hero__subtitle { font-size: 16px; }
518  }
519  @media (prefers-reduced-motion: reduce) {
520    .re-hero .re-hero__dot { animation: none; }
521  }
522</style>
523
524<script>
525(function () {
526  var SUPABASE_URL = 'https://yrzanatzshdpvypazgeb.supabase.co';
527  var SUPABASE_KEY = 'sb_publishable_O4cIYIJcDs3_GcbjNHdd9g_e2cQKOUD';
528  var CABECERAS = { apikey: SUPABASE_KEY, Authorization: 'Bearer ' + SUPABASE_KEY };
529  var MAX_PALABRAS_TITULO = 25;   // el mismo tope que tenía el campo "Tu pregunta" antiguo
530  var MAX_SLUG = 90;
531  var RE_EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
532  // Marcas combinantes (tildes sueltas tras normalizar a NFD): el rango se construye con
533  // fromCharCode a propósito, sin secuencias de escape unicode en el fuente, porque el
534  // canal con el que se escribe el embed en Webflow no las transporta bien.
535  var MARCAS_COMBINANTES = new RegExp('[' + String.fromCharCode(0x300) + '-' + String.fromCharCode(0x36f) + ']', 'g');
536
537  var card = document.getElementById('formulario');
538  if (!card) return;
539  var $ = function (id) { return document.getElementById(id); };
540  var form1 = $('pae-form-1'), form2 = $('pae-form-2'), ok = $('pae-ok');
541  var preguntaEl = $('pae-pregunta'), nombreEl = $('pae-nombre'), emailEl = $('pae-email'), hpEl = $('pae-web');
542  var error1 = $('pae-error-1'), error2 = $('pae-error-2');
543  var btnContinuar = $('pae-continuar'), btnEnviar = $('pae-enviar');
544
545  /* ---- medición (Amplitude; el head del sitio deja un stub hasta el consentimiento) ---- */
546  var vistos = {};
547  function medir(evento, props) {
548    var datos = props || {};
549    datos.pagina = location.pathname;
550    try { if (window.amplitude && window.amplitude.track) window.amplitude.track(evento, datos); } catch (e) {}
551  }
552  function medirPaso(paso) {
553    if (vistos[paso]) return;
554    vistos[paso] = true;
555    medir('pae_paso_visto', { paso: paso });
556  }
557
558  /* ---- helpers ---- */
559  function contarPalabras(t) { t = t.trim(); return t ? t.split(/\s+/).length : 0; }
560
561  function slugify(texto) {
562    var s = texto.normalize('NFD').replace(MARCAS_COMBINANTES, '')
563      .toLowerCase().trim()
564      .replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
565    if (s.length > MAX_SLUG) {
566      var corte = s.lastIndexOf('-', MAX_SLUG);
567      s = s.slice(0, corte > 20 ? corte : MAX_SLUG);
568    }
569    // Sin letras latinas (cirílico, solo emojis, solo signos) el slug quedaría vacío y la
570    // página no se podría publicar: se le da uno provisional que se puede retocar al moderar.
571    return s || 'pregunta-' + Date.now().toString(36);
572  }
573
574  // El título de la página es `pregunta` tal cual, así que no puede ser un párrafo entero.
575  // Si el texto pasa del tope: se corta en el último "?" que quede dentro de las primeras
576  // 25 palabras (con al menos 3 delante) o, si no lo hay, en la palabra 25. El resto va a
577  // `detalle`, que la ficha pinta debajo del título.
578  function partirPregunta(texto) {
579    var limpio = texto.replace(/\s+/g, ' ').trim();
580    var palabras = limpio.split(' ');
581    if (palabras.length <= MAX_PALABRAS_TITULO) return { pregunta: limpio, detalle: null };
582
583    var cabeza = palabras.slice(0, MAX_PALABRAS_TITULO).join(' ');
584    var corte = -1, i = -1;
585    while ((i = cabeza.indexOf('?', i + 1)) !== -1) {
586      if (contarPalabras(cabeza.slice(0, i)) >= 3) corte = i + 1;
587    }
588    var titulo = corte > 0 ? cabeza.slice(0, corte) : cabeza;
589    var resto = limpio.slice(titulo.length).trim();
590    return { pregunta: titulo.trim(), detalle: resto || null };
591  }
592
593  function mostrar(paso) {
594    form1.hidden = paso !== 1;
595    form2.hidden = paso !== 2;
596    ok.hidden = paso !== 'ok';
597    card.setAttribute('data-paso', String(paso));
598    medirPaso(paso);
599  }
600
601  function marcar(el, mal) { el.classList.toggle('is-invalid', !!mal); }
602
603  /* ---- paso 1 → 2 ---- */
604  form1.addEventListener('submit', function (ev) {
605    ev.preventDefault();
606    var texto = preguntaEl.value.trim();
607    if (contarPalabras(texto) < 3) {
608      error1.textContent = texto ? 'Escribe tu pregunta un poco más completa.' : 'Escribe tu pregunta antes de continuar.';
609      marcar(preguntaEl, true);
610      preguntaEl.focus();
611      medir('pae_datos_invalidos', { campo: 'pregunta' });
612      return;
613    }
614    error1.textContent = '';
615    marcar(preguntaEl, false);
616    $('pae-resumen-texto').textContent = texto;
617    mostrar(2);
618    medir('pae_continuar', { palabras: contarPalabras(texto) });
619    nombreEl.focus();
620  });
621  preguntaEl.addEventListener('input', function () { if (error1.textContent) { error1.textContent = ''; marcar(preguntaEl, false); } });
622
623  /* ---- volver a editar ---- */
624  $('pae-editar').addEventListener('click', function () {
625    if (enviando) return; // con el envío en vuelo, volver atrás duplicaría la pregunta
626    mostrar(1);
627    medir('pae_editar');
628    preguntaEl.focus();
629  });
630
631  /* ---- paso 2: enviar ---- */
632  var enviando = false;
633  form2.addEventListener('submit', function (ev) {
634    ev.preventDefault();
635    if (enviando) return;
636    var nombre = nombreEl.value.trim();
637    var email = emailEl.value.trim();
638    marcar(nombreEl, false); marcar(emailEl, false);
639
640    if (!nombre) {
641      error2.textContent = 'Escribe tu nombre.';
642      marcar(nombreEl, true); nombreEl.focus();
643      medir('pae_datos_invalidos', { campo: 'nombre' });
644      return;
645    }
646    if (!RE_EMAIL.test(email)) {
647      error2.textContent = 'Introduce un email válido.';
648      marcar(emailEl, true); emailEl.focus();
649      medir('pae_datos_invalidos', { campo: 'email' });
650      return;
651    }
652    error2.textContent = '';
653
654    var partes = partirPregunta(preguntaEl.value);
655
656    // Bot: se le enseña el éxito y no se guarda nada.
657    if (hpEl && hpEl.value) { mostrar('ok'); return; }
658
659    enviando = true;
660    btnEnviar.disabled = true;
661    $('pae-editar').disabled = true;
662    var textoBtn = btnEnviar.textContent;
663    btnEnviar.textContent = 'Enviando…';
664
665    fetch(SUPABASE_URL + '/rest/v1/preguntas', {
666      method: 'POST',
667      headers: Object.assign({ 'Content-Type': 'application/json', Prefer: 'return=minimal' }, CABECERAS),
668      body: JSON.stringify({
669        nombre_usuario: nombre,
670        pregunta: partes.pregunta,
671        detalle: partes.detalle,
672        // La asigna el servidor al recibir la pregunta (avisar-pregunta-pendiente).
673        categoria: 'sin-clasificar',
674        categoria_publica: null,
675        slug: slugify(partes.pregunta),
676        email_usuario: email
677      })
678    }).then(function (r) {
679      if (!r.ok) throw new Error('HTTP ' + r.status);
680      medir('pae_pregunta_enviada', { palabras: contarPalabras(preguntaEl.value), con_detalle: !!partes.detalle });
681      mostrar('ok');
682    }).catch(function (e) {
683      console.error('No se pudo guardar la pregunta:', e);
684      error2.textContent = 'No hemos podido enviar tu pregunta. Inténtalo de nuevo en unos segundos.';
685      medir('pae_envio_fallido');
686    }).then(function () {
687      enviando = false;
688      btnEnviar.disabled = false;
689      $('pae-editar').disabled = false;
690      btnEnviar.textContent = textoBtn;
691    });
692  });
693  nombreEl.addEventListener('input', function () { if (error2.textContent) { error2.textContent = ''; marcar(nombreEl, false); } });
694  emailEl.addEventListener('input', function () { if (error2.textContent) { error2.textContent = ''; marcar(emailEl, false); } });
695
696  /* ---- otra pregunta ---- */
697  $('pae-otra').addEventListener('click', function () {
698    preguntaEl.value = ''; nombreEl.value = ''; emailEl.value = '';
699    error1.textContent = ''; error2.textContent = '';
700    vistos = {};
701    mostrar(1);
702    preguntaEl.focus();
703  });
704
705  medirPaso(1);
706
707  /* ---- cifras: recuento animado + valores reales (los del HTML son el respaldo) ---- */
708  var REDUCIR_MOVIMIENTO = !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
709  var enCurso = {};
710
711  // Lleva el número de una cifra desde lo que muestra ahora hasta `objetivo`, como un
712  // contador rápido (1,2 s, frenando al final). Con "reducir movimiento" va directo.
713  function animarCifra(clave, objetivo) {
714    var el = document.querySelector('.pae-stat__n[data-cifra="' + clave + '"]');
715    if (!el || !(objetivo >= 0)) return;
716    var desde = parseInt(el.getAttribute('data-actual') || '0', 10) || 0;
717    el.setAttribute('data-valor', String(objetivo));
718    if (REDUCIR_MOVIMIENTO || !window.requestAnimationFrame) {
719      el.textContent = String(objetivo); el.setAttribute('data-actual', String(objetivo));
720      return;
721    }
722    if (enCurso[clave]) cancelAnimationFrame(enCurso[clave]);
723    var inicio = null, DURACION = 1200;
724    function paso(t) {
725      if (inicio === null) inicio = t;
726      var p = Math.min((t - inicio) / DURACION, 1);
727      var suave = 1 - Math.pow(1 - p, 3);
728      var v = Math.round(desde + (objetivo - desde) * suave);
729      el.textContent = String(v);
730      el.setAttribute('data-actual', String(v));
731      if (p < 1) enCurso[clave] = requestAnimationFrame(paso);
732    }
733    enCurso[clave] = requestAnimationFrame(paso);
734  }
735  function pintarUnidad(clave, texto) {
736    var u = document.querySelector('.pae-stat__unidad[data-unidad="' + clave + '"]');
737    if (u) u.textContent = texto;
738  }
739  function plural(n, uno, varios) { return n === 1 ? uno : varios; }
740
741  // Arranca el recuento (de 0 al valor del HTML) cuando las cifras entran en pantalla.
742  function arrancarRecuento() {
743    var nums = document.querySelectorAll('.pae-stat__n');
744    for (var i = 0; i < nums.length; i++) {
745      var objetivo = parseInt(nums[i].getAttribute('data-valor'), 10);
746      nums[i].setAttribute('data-actual', '0');
747      animarCifra(nums[i].getAttribute('data-cifra'), objetivo);
748    }
749  }
750  var stats = document.querySelector('.pae-stats');
751  if (stats && 'IntersectionObserver' in window && !REDUCIR_MOVIMIENTO) {
752    var obs = new IntersectionObserver(function (entradas) {
753      if (entradas.some(function (e) { return e.isIntersecting; })) { obs.disconnect(); arrancarRecuento(); }
754    }, { threshold: 0.3 });
755    obs.observe(stats);
756  } else if (stats) {
757    arrancarRecuento();
758  }
759
760  function mediana(nums) {
761    if (!nums.length) return null;
762    var s = nums.slice().sort(function (a, b) { return a - b; });
763    var m = s.length >> 1;
764    return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
765  }
766
767  // Preguntas aprobadas con la fecha de cada respuesta: de ahí salen la media de
768  // respuestas y las horas hasta la primera. La RLS solo devuelve preguntas con respuesta.
769  fetch(SUPABASE_URL + '/rest/v1/preguntas?select=id,created_at,respuestas(created_at)&aprobada=eq.true&limit=1000', { headers: CABECERAS })
770    .then(function (r) { return r.ok ? r.json() : []; })
771    .then(function (filas) {
772      var conRespuestas = (filas || []).filter(function (p) { return p.respuestas && p.respuestas.length; });
773      if (!conRespuestas.length) return;
774      var total = conRespuestas.reduce(function (n, p) { return n + p.respuestas.length; }, 0);
775      var media = Math.round(total / conRespuestas.length);
776      if (media >= 1) { animarCifra('respuestas', media); pintarUnidad('respuestas', plural(media, ' respuesta', ' respuestas')); }
777
778      var horas = conRespuestas.map(function (p) {
779        var primera = Math.min.apply(null, p.respuestas.map(function (r) { return new Date(r.created_at).getTime(); }));
780        return (primera - new Date(p.created_at).getTime()) / 36e5;
781      }).filter(function (h) { return isFinite(h) && h >= 0; });
782      var med = mediana(horas);
783      if (med !== null) {
784        var h = Math.max(1, Math.round(med));
785        animarCifra('horas', h);
786        pintarUnidad('horas', med < 1 ? ' hora o menos' : plural(h, ' hora', ' horas'));
787      }
788    })
789    .catch(function () {});
790
791  fetch(SUPABASE_URL + '/rest/v1/entrenadores_publico?select=id&limit=1', {
792    method: 'HEAD',
793    headers: Object.assign({ Prefer: 'count=exact' }, CABECERAS)
794  })
795    .then(function (r) {
796      var rango = r.headers.get('content-range') || '';
797      var n = parseInt(rango.split('/')[1], 10);
798      if (n > 0) animarCifra('entrenadores', n);
799    })
800    .catch(function () {});
801})();
802</script>
802
803</div><div class="w-embed w-script"><!-- RedEntrenadores — /pregunta-al-experto · pop-up de confirmación tras enviar la pregunta
804
805     DÓNDE VIVE: segundo HtmlEmbed de la landing, hermano del formulario
806     (`widget-pregunta-al-experto.html`) dentro de `.re-hero__wrap`. Va DESPUÉS de él:
807     su JS busca en el DOM la tarjeta que pinta el otro.
808
809     QUÉ HACE: cuando la pregunta se ha guardado de verdad, el formulario deja su
810     tarjeta en el estado "enviada" y este embed abre encima un pop-up que lo confirma
811     y ofrece el recomendador. Es el único momento en que se tiene toda la atención de
812     quien pregunta, y quien acaba de escribir una duda de entrenamiento es justo a
813     quien le sirve que le propongan entrenador.
814
815     CÓMO SE ENTERA: mirando cuándo `#pae-ok` deja de estar oculto. Ese id y su `hidden`
816     son el contrato con el otro embed; van juntos aunque estén en dos ficheros. Se hace
817     así, y no con una llamada directa, para no tener que reescribir el embed del
818     formulario —27 KB que hoy funcionan— solo para colgarle un pop-up.
819
820     EL RECOMENDADOR: `window.reRecomendador.abrir()`, el gancho manual del bloque 3 de
821     `widget-popup-recomendador.html`, que vive en el Footer V1 y carga también aquí. En
822     esta ruta ese pop-up no sale por su cuenta —está en RUTAS_EXCLUIDAS—, pero desde el
823     25/09/2026 sí se monta y deja el gancho disponible (lo hace el parche
824     `widget-popup-recomendador-gancho-manual.html`, también en el Footer V1). Si no
825     estuviera, el botón lleva al buscador en vez de quedarse muerto.
826
827     Al editar: cambiar este fichero Y el `code` del embed en Webflow (no se sincronizan). -->
828
829<div class="pae-modal" id="pae-modal" hidden>
830  <div class="pae-modal__caja" role="dialog" aria-modal="true" aria-labelledby="pae-modal-titulo">
831    <button type="button" class="pae-modal__cerrar" id="pae-modal-x" aria-label="Cerrar">
832      <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
833    </button>
834
835    <div class="pae-modal__icono" aria-hidden="true">
836      <svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>
837    </div>
838    <h2 class="pae-modal__titulo" id="pae-modal-titulo">Ya hemos enviado tu pregunta</h2>
839    <p class="pae-modal__texto">Varios entrenadores certificados la están leyendo. Te avisamos por email en cuanto tengas respuestas: suelen llegar el mismo día.</p>
840
841    <div class="pae-modal__oferta">
842      <p class="pae-modal__oferta-titulo">Mientras llegan las respuestas</p>
843      <p class="pae-modal__oferta-texto">Contesta tres preguntas rápidas y te ponemos en contacto con los entrenadores que mejor encajan contigo. Gratis y sin compromiso.</p>
844      <button type="button" class="pae-btn pae-modal__cta" id="pae-modal-recomendador">Encontrar mi entrenador</button>
845    </div>
846
847    <button type="button" class="pae-link pae-modal__no" id="pae-modal-no">Ahora no</button>
848  </div>
849</div>
850
851<style>
852  /* `.pae-btn` y `.pae-link` los define el embed del formulario: los botones de aquí
853     son los mismos y se heredan tal cual. Lo propio de este bloque es el `pae-modal__*`. */
854
855  /* Por debajo del recomendador (10000): al pulsar el CTA este se cierra y el otro se
856     abre encima, y durante esa décima de segundo el orden tiene que ser este. */
857  .pae-modal {
858    position: fixed; inset: 0; z-index: 9990;
859    display: flex; align-items: center; justify-content: center;
860    padding: 20px;
861    background: rgba(27, 30, 34, .55);
862    /* `dvh` mide el viewport que se ve de verdad en móvil; el `vh` es el respaldo. */
863    height: 100vh; height: 100dvh;
864    overflow-y: auto;
865    animation: pae-modal-fundido .18s ease-out;
866  }
867  .pae-modal[hidden] { display: none; }
868  .pae-modal__caja {
869    position: relative;
870    width: 100%; max-width: 440px;
871    box-sizing: border-box;
872    background: #fff; border-radius: 12px;
873    box-shadow: 0 18px 44px rgba(0, 0, 0, .22);
874    padding: 32px 28px 24px;
875    text-align: center;
876    animation: pae-modal-sube .22s cubic-bezier(.22, 1, .36, 1);
877  }
878  .pae-modal__caja * { box-sizing: border-box; }
879  .pae-modal__cerrar {
880    position: absolute; top: 10px; right: 10px;
881    width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
882    background: none; border: none; border-radius: 8px; cursor: pointer; color: #6b7280;
883  }
884  .pae-modal__cerrar:hover { background: #F9FAFB; color: #1B1E22; }
885  .pae-modal__icono {
886    width: 60px; height: 60px; border-radius: 50%;
887    background: #EAF3DE; color: #089F3B;
888    display: flex; align-items: center; justify-content: center;
889    margin: 0 auto 16px;
890  }
891  .pae-modal__titulo { font-size: 22px; line-height: 28px; font-weight: 700; color: #1B1E22; margin: 0 0 8px; }
892  .pae-modal__texto { font-size: 14px; line-height: 21px; color: #4B5563; margin: 0 auto 20px; max-width: 340px; }
893  .pae-modal__oferta { background: #EAF3DE; border-radius: 12px; padding: 18px 18px 20px; text-align: left; }
894  .pae-modal__oferta-titulo { font-size: 12px; line-height: 16px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: #089F3B; margin: 0 0 6px; }
895  .pae-modal__oferta-texto { font-size: 14px; line-height: 20px; color: #1B1E22; margin: 0; }
896  .pae-modal__cta { margin-top: 14px; }
897  .pae-modal__no { display: block; margin: 14px auto 0; color: #6b7280; }
898  .pae-modal__no:hover { color: #4B5563; }
899  @keyframes pae-modal-fundido { from { opacity: 0; } }
900  @keyframes pae-modal-sube { from { opacity: 0; transform: translateY(14px); } }
901
902  @media (max-width: 479px) {
903    .pae-modal { padding: 12px; padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); align-items: flex-end; }
904    .pae-modal__caja { padding: 26px 18px 20px; }
905    .pae-modal__titulo { font-size: 20px;
905 line-height: 26px; }
906  }
907  @media (prefers-reduced-motion: reduce) {
908    .pae-modal, .pae-modal__caja { animation: none; }
909  }
910</style>
911
912<script>
913(function () {
914  var modal = document.getElementById('pae-modal');
915  var ok = document.getElementById('pae-ok');
916  if (!modal || !ok) return;   // sin el embed del formulario delante, aquí no hay nada que hacer
917
918  var $ = function (id) { return document.getElementById(id); };
919  var focoPrevio = null;
920
921  /* Medición (Amplitude; el head del sitio deja un stub hasta el consentimiento). Los
922     nombres siguen la familia `pae_*` del formulario para que el embudo se lea seguido. */
923  function medir(evento, props) {
924    var datos = props || {};
925    datos.pagina = location.pathname;
926    try { if (window.amplitude && window.amplitude.track) window.amplitude.track(evento, datos); } catch (e) {}
927  }
928
929  function abrir() {
930    if (!modal.hidden) return;
931    focoPrevio = document.activeElement;
932    modal.hidden = false;
933    document.body.style.overflow = 'hidden';
934    var cta = $('pae-modal-recomendador');
935    if (cta) cta.focus();
936    medir('pae_popup_enviada_visto');
937  }
938
939  // `motivo` "recomendador" es el único que no devuelve el foco: justo después se abre
940  // el otro pop-up y es él quien decide dónde tiene que estar.
941  function cerrar(motivo) {
942    if (modal.hidden) return;
943    modal.hidden = true;
944    document.body.style.overflow = '';
945    medir('pae_popup_cerrado', { motivo: motivo || 'cerrar' });
946    if (motivo === 'recomendador') return;
947    // El foco vuelve a la tarjeta, que es donde ha quedado la confirmación.
948    var destino = $('pae-otra') || focoPrevio;
949    if (destino && destino.focus) destino.focus();
950  }
951
952  // El recomendador vuelve a pedir nombre y email en su último paso. Están escritos
953  // ahí al lado, en el formulario que se acaba de enviar: se los damos hechos.
954  function prellenarRecomendador() {
955    try {
956      var estado = window.__reRecNucleo && window.__reRecNucleo.estado;
957      if (!estado) return;
958      var nombre = $('pae-nombre'), email = $('pae-email');
959      if (!estado.nombre && nombre) estado.nombre = nombre.value.trim();
960      if (!estado.email && email) estado.email = email.value.trim();
961    } catch (e) {}
962  }
963
964  modal.addEventListener('click', function (ev) { if (ev.target === modal) cerrar('fondo'); });
965  $('pae-modal-x').addEventListener('click', function () { cerrar('aspa'); });
966  $('pae-modal-no').addEventListener('click', function () { cerrar('ahora-no'); });
967  document.addEventListener('keydown', function (ev) {
968    if (ev.key === 'Escape' && !modal.hidden) cerrar('escape');
969  });
970
971  $('pae-modal-recomendador').addEventListener('click', function () {
972    var rec = window.reRecomendador;
973    var disponible = !!(rec && rec.abrir);
974    medir('pae_recomendador_pulsado', { disponible: disponible });
975    cerrar('recomendador');
976    if (disponible) {
977      prellenarRecomendador();
978      rec.abrir();
979      return;
980    }
981    window.location.href = '/encuentra-entrenador';
982  });
983
984  /* El disparo: `#pae-ok` es el bloque "¡Pregunta enviada!" del embed del formulario y
985     solo pierde su `hidden` cuando la fila ya está guardada en `preguntas`. Vigilarlo es
986     lo que permite dejar aquel embed sin tocar. Si algún día el formulario emite un
987     evento propio, este observer se cambia por él y el resto se queda igual. */
988  new MutationObserver(function () {
989    if (!ok.hidden) abrir();
990  }).observe(ok, { attributes: true, attributeFilter: ['hidden'] });
991})();
992</script>
992
993</div></div></section><section class="re-cats"><h2 class="re-cats__title">Preguntas por categoría</h2><ul role="list" class="re-cats__list"><li class="re-cats__item"><a href="/pregunta-al-experto?categoria=perdida-peso" class="re-cats__link"><div class="re-cats__icon-1"></div>Pérdida de peso</a></li><li class="re-cats__item"><a href="/pregunta-al-experto?categoria=entrenamiento-funcional" class="re-cats__link"><div class="re-cats__icon-2"></div>Entrenamiento funcional</a></li><li class="re-cats__item"><a href="/pregunta-al-experto?categoria=recuperacion-lesiones" class="re-cats__link"><div class="re-cats__icon-3"></div>Recuperación de lesiones</a></li><li class="re-cats__item"><a href="/pregunta-al-experto?categoria=recuperacion-posparto" class="re-cats__link"><div class="re-cats__icon-4"></div>Recuperación posparto</a></li><li class="re-cats__item"><a href="/pregunta-al-experto?categoria=prevencion-lesiones" class="re-cats__link"><div class="re-cats__icon-5"></div>Prevención de lesiones</a></li><li class="re-cats__item"><a href="/pregunta-al-experto?categoria=aumento-masa-muscular" class="re-cats__link"><div class="re-cats__icon-6"></div>Aumento de masa muscular</a></li><li class="re-cats__item"><a href="/pregunta-al-experto?categoria=movilidad-flexibilidad" class="re-cats__link"><div class="re-cats__icon-7"></div>Movilidad y flexibilidad</a></li><li class="re-cats__item"><a href="/pregunta-al-experto?categoria=preparacion-oposiciones" class="re-cats__link"><div class="re-cats__icon-8"></div>Preparación de oposiciones</a></li></ul></section><div class="w-layout-vflex general-seccion max-width"><div id="otras-preguntas" class="re-oq-section"><div class="otras_preguntas-header"><h2 class="re-oq-title">¿Tienes prisa? Encuentra tu respuesta al instante</h2></div><div class="w-dyn-list"><div role="list" class="re-oq-grid w-dyn-items"><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/como-reducir-la-barriga-entrenando-6-dias-por-semana" class="re-oq-card w-inline-block"><div class="re-oq-hidden">perdida-peso</div><div class="re-oq-tag">Pérdida de peso</div><div class="re-oq-question">Cómo reducir la barriga entrenando 6 días por semana</div><div class="re-oq-detail w-richtext">Mucha panza y hago gimnasia 6 veces por semana 45 minutos por día</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Ovidio</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/como-volver-a-entrenar-a-los-7-meses-del-parto-con-tripa-floja" class="re-oq-card w-inline-block"><div class="re-oq-hidden">recuperacion-posparto</div><div class="re-oq-tag">Recuperación posparto</div><div class="re-oq-question">Cómo volver a entrenar a los 7 meses del parto</div><div class="re-oq-detail w-richtext">hola, tuve a mi hijo hace 7 meses y quiero volver a entrenar pero me noto la tripa muy floja. que ejercicios puedo hacer para empezar? merece la pena un entrenador especializado en postparto o con videos de youtube me vale? gracias</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Jana</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/entrenar-pesas-todos-los-dias-sin-descanso-es-malo" class="re-oq-card w-inline-block"><div class="re-oq-hidden">entrenamiento-funcional</div><div class="re-oq-tag">Entrenamiento funcional</div><div class="re-oq-question">Entrenar pesas todos los días sin descanso: ¿es malo?</div><div class="re-oq-detail w-richtext">Buenas, una duda. Hace unos meses que entreno y ahora estoy yendo al gym todos los días a hacer pesas, sin descansar ningun día. Me encuentro bien y voy progresando, pero he leido en varios sitios que hay que descansar minimo 1 o 2 dias a la semana. Hacer pesas todos los días es realmente malo o depende de como lo organizes?</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Dolo</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/como-combinar-entrenamiento-de-fuerza-e-hipertrofia-en-la-misma-rutina" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Aumento de masa muscular</div><div class="re-oq-question">Cómo combinar entrenamiento de fuerza e hipertrofia en la misma rutina</div><div class="re-oq-detail w-richtext">Para alguien que trabaja la hipertrofia pero que también quiere trabajar fuerza , ¿Quue le recomendarías ? ¿Ajustar pesos de máquinas pesados y enfocarse en rangos de repeticiones de 5 a 6 , o directamente hacer un planning de días específicos y ejercicios específicos diferentes a los días de hipertrofia? Gracias de antemano!</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Karim</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/como-ganar-peso-y-masa-muscular-combinando-gimnasio-y-baloncesto" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Aumento de masa muscular</div><div class="re-oq-question">
993Cómo ganar peso y masa muscular combinando gimnasio y baloncesto</div><div class="re-oq-detail w-richtext">Soy una persona que peso muy muy poco y mido 1,80 y me gustaria subir de peso y masa muscular .hago baloncesto 3 dias a la semana y me gustaria ir al gimasion 5 haciendo en un mismo dia gym y baloncesto pero no estoy descansando nada ¿que hago?</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Iker</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/cuantas-calorias-comer-al-dia-para-ganar-musculo-sin-perder-definicion" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Aumento de masa muscular</div><div class="re-oq-question">Cuántas calorías comer al día para ganar músculo sin perder definición</div><div class="re-oq-detail w-richtext">Hola peso 58-59 kg y mido 1.54 y quiero aumenta masa muscular sí que tengo algo poco y tengo aún un poco de grasa llevo menos de año en el gym y quiero aumentar brazosarcas espalda y sobretodo tren inferior entonces quiero saber cuántas kcal debo comer al día para aumentar glúteos y tal pero seguir definida</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Bea</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/estancamiento-muscular-en-pecho-y-hombros-tras-8-meses" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Aumento de masa muscular</div><div class="re-oq-question">Estancamiento muscular en pecho y hombros tras 8 meses</div><div class="re-oq-detail w-richtext">Llevo 8 meses entrenando 4 dias a la semana y he dejado de ganar musculo, sobre todo en pecho y hombros. Deveria cambiar de rutina o es que estoy comiendo poco?</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">JP</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/cuando-volver-a-correr-tras-un-esguince-de-tobillo" class="re-oq-card w-inline-block"><div class="re-oq-hidden">recuperacion-lesiones</div><div class="re-oq-tag">Recuperación de lesiones</div><div class="re-oq-question">Cuándo volver a correr tras un esguince de tobillo</div><div class="re-oq-detail w-richtext">Me doblé el tobillo jugando al padel hace tres semanas. Ya casi no me duele al andar, pero lo noto inestable cuando bajo escaleras, puedo volver a correr o me arriesgo a recaer?</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Iago</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/merece-la-pena-un-entrenador-online-si-solo-tengo-esterilla-y-gomas" class="re-oq-card w-inline-block"><div class="re-oq-hidden">entrenamiento-funcional</div><div class="re-oq-tag">Entrenamiento funcional</div><div class="re-oq-question">¿Merece la pena un entrenador online entrenando en casa con gomas?</div><div class="re-oq-detail w-richtext">Trabajo desde casa y tengo huecos sueltos entre reuniones asi que ir al gimnasio a una hora fija no me cuadra nada. He estado mirando lo del entrenador online pero en casa solo tengo una esterilla y unas gomas, no tengo espacio para mas cosas la verdad. Lo que me echa para atras es pagar por algo que igual me lo puedo bajar gratis de internet, y ademas por videollamada nadie te corrige la tecnica digo yo. Que diferencia hay de verdad entre pagar un entrenador online y seguir una rutina de internet? y se puede avanzar solo con gomas o al final necesitas pesas si o si? Gracias de anteman</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Rafa</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/me-da-tiempo-a-arreglar-la-trepa-de-cuerda-para-bombero-en-mayo" class="re-oq-card w-inline-block"><div class="re-oq-hidden">preparacion-oposiciones</div><div class="re-oq-tag">Preparación de oposiciones</div><div class="re-oq-question">Cómo entrenar la trepa de cuerda para la oposición de bombero</div><div class="re-oq-detail w-richtext">Hola, voy a por bombero y llevo un año preparandome por mi cuenta. El problema que tengo es que en la prueba de press banca me quedo corto de repeticiones y en la de trepa de cuerda directamente no subo, o subo con las piernas pero eso creo que ahi no vale. En natacion voy justo tambien. Yo entreno en un gimnasio normal de barrio, hago pecho lunes, espalda miercoles y pierna viernes, lo tipico, pero me da la sensacion de que estoy entrenando como para ponerme fuerte y no como para pasar la prueba, no se si me explico. Lo que quiero saber es si un entrenador personal normal me sirve o tengo que buscar a alguien que sepa especificamente de opos de bombero, porque los que he mirado son de fitness y no se si sabran de esto. Y si de aqui a la convocatoria de mayo me da tiempo a arreglar lo de la cuerda. Gracias</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Filo</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/que-mejoro-en-mi-entreno-con-mancuernas-para-volver-a-bajar-de-peso" class="re-oq-card w-inline-block"><div class="re-oq-hidden">perdida-peso</div><div class="re-oq-tag">Pérdida de peso</div><div class="re-oq-question">Cómo mejorar el entrenamiento con mancuernas en casa para perder peso</div><div class="re-oq-detail w-richtext">43años, 1,82 m, 84/85 kg. Llevo 9 meses entrenando en casa 2/3 días por semana con mancuernas (3 series, superior/inferior alternos, series de 12 con 14 kg, y una de 16 para peso muerto y extensiones). Sin abdominales ni banco andaba 15km semanales y baje a 82 kg pero entre el calor y una mala racha llevo un mes parado y he rebotado. Tomaba 3 g de creatina, como 1.200 kcal diarias y no duelo dormir mucho. que mejorarias en entreno y en bajada de peso?</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Juanma</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/mi-rutina-de-5-dias-sirve-para-ganar-volumen-en-piernas" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Aumento de masa muscular</div><div class="re-oq-question">¿Sirve una rutina de 5 días para ganar volumen en piernas?</div><div class="re-oq-detail w-richtext">Hola o/ Tengo 1 semana y media en el gym, primera vez en mi vida. Peso 66 kilos y mido 1,63. Mi meta es sacar volumen en piernas y bajar algo de grasa en general. Llevo dieta sana hace 2 meses (sin galletas, papitas ni refresco) y solo con eso baje 3 kilos. Ahora entreno 5 dias a la semana con entrenador, rutina 4x12: pierna-brazo-espalda, piern
993a-gluteo, pecho-brazo, pierna. Hago mi calentamiento por mi cuenta con videos segun el area del dia, y el entrenador me pone 2 series mas. Al final hago 20 min de cardio y estiramientos yo sola. Mi duda es: ¿esta rutina y el calentamiento que hago estan bien para lo que quiero (piernas + bajar grasa), o le falta algo?</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Mary</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/como-tomar-creatina-monohidrato-a-diario-tambien-en-dias-sin-entrenar" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Aumento de masa muscular</div><div class="re-oq-question">Cómo tomar creatina monohidrato a diario, también en días sin entrenar</div><div class="re-oq-detail w-richtext">Creatina monohidrato, cómo se lleva en el día a día? Sé lo básico (3-5g, sin carga) pero tengo dudas prácticas: se toma también los días sin entrenar? da molestias digestivas al principio? vale la pena el sello Creapure o cualquier marca sirve? se nota mucho la retención de agua?</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">JR</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/mi-brazo-no-engorda" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Ganar masa muscular</div><div class="re-oq-question">Cómo ganar volumen en los brazos</div><div class="re-oq-detail w-richtext">No es un brazo flácido, tiene silueta de un buen brazo, pero no es nada ancho/no tiene volumen (por decirlo de alguna forma) y no se que hacer para que cambie
994
995He cambiado agarres, hecho un día especifico para el brazo, priorizarlos al inicio de la rutina y aun así no cambia
996
997Normalmente para triceps hago 4 series de extensión, 2 de press francés y 2 de unilateral tras nuca (o como se llame), para bíceps 4 predicador y 2 martillos, para hombro 4 laterales y 2 press militar
998
999Qué hago o cambio? Mi torso (sin ser algo espectacular) se ve mucho mas grande que mis brazos</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Fear</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/puedo-perder-peso-pero-manteniendo-mi-figura-rellenita-como-esque-soy-gordita-y-quiero-seguir-asi-pero-bajar-algo-de-peso-120kg-peso-actualmente" class="re-oq-card w-inline-block"><div class="re-oq-hidden">perdida-peso</div><div class="re-oq-tag">Pérdida de peso</div><div class="re-oq-question">Cómo perder peso sin dejar de tener una figura rellenita</div><div class="re-oq-detail w-richtext">soy gordita y quiero seguir así, pero bajar algo de peso... 120kg peso actualmente... Tengo inseguridades por culpa de la gente... El tema es que quiero seguir siendo rellenita, una bolita tierna de grasa, pero quiero perder algo de kilos, al menos llegar a 60-70kg pero sin cambiar mi físico...</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Shadow</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/ejercicios-para-mejorar-la-condicion-fisica" class="re-oq-card w-inline-block"><div class="re-oq-hidden">perdida-peso</div><div class="re-oq-tag">Pérdida de peso</div><div class="re-oq-question">Ejercicios en casa para mejorar la condición física con sobrepeso</div><div class="re-oq-detail w-richtext">actualmente tengo sobre peso, mido 1.80 y ando Serca de los 130 kilos, quiero comenzar hacer ejercicio e ir al gim pero hace poco tuve un choque y no e tenido la solvencia económica por estos pagos, en ese Inter que ejercicios recomendarían para agarrar condición y posteriormente ir al gimnasio.</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Gio</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/dejar-el-gym" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Ganar masa muscular</div><div class="re-oq-question">Dejar el gimnasio por falta de motivación tras dos años entrenando</div><div class="re-oq-detail w-richtext">Todos los principios de mes me hago la misma pregunta, debería dejarlo? Ya no siento ganas de ir, pero al mismo tiempo siento q soy esclavo de mi físico, no estoy como para el olympia, pero estoy mejor q nunca, y todos me halagan por eso, cosa q me molestaría perder, me desmotiva el hecho de q hace frio, no puedo darme el lujo de comer bien las proteínas necesarias, y me consume energía que puedo usar en otras cosas, debería descansar un mes? Debería cambiarme de gym? Entreno hace mas de 2 años, y me gustaría recibir el consejo de alguien q haya estado en esta situación</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Juan</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/como-afecta-el-ciclo-menstrual-al-entreno-de-fuerza" class="re-oq-card w-inline-block"><div class="re-oq-hidden">entrenamiento-funcional</div><div class="re-oq-tag">Entrenamiento funcional</div><div class="re-oq-question">Cómo afecta el ciclo menstrual al entrenamiento de fuerza</div><div class="re-oq-detail w-richtext">¿Como afecta el ciclo menstrual al entreno de fuerza?</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Anna</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/si-hago-2-dias-a-la-semana-pierna-y-gluteo-los-dos-dias-debo-de-hacer-la-misma-rutina" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Aumento de masa muscular</div><div class="re-oq-question">¿Hay que hacer la misma rutina de pierna dos días por semana?</div><div class="re-oq-detail w-richtext">Me he estado preguntando eso, ya que veo que hay gente que tiene una rutina para cada dia pero quiero saber si se progresa con una sola rutina los dos dias. Yo hago ejercicio en casa con mancuernas y no tengo a quien preguntarle.</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Gisela</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div><div role="listitem" class="w-dyn-item"><a href="/pregunta-al-experto/necesito-ayuda-con-las-rutinas" class="re-oq-card w-inline-block"><div class="re-oq-hidden">aumento-masa-muscular</div><div class="re-oq-tag">Aumento de masa muscular</div><div class="re-oq-question">Cómo armar tu propia rutina de entrenamiento en el gimnasio</div><div class="re-oq-detail w-richtext">He estado entrenando casi por 1 año ya pero estuve siempre en smartfit, ahora como no me queda ninguno cerca pasare a uno más clasico por asi decirlo, quisiera que me ayudaran a como buscar informacion sobre como hacer mis rutinas, tengo la rutina en pdf que me mandaba mi coach anterior pero no quiero depender completamente de una rutina asi, si me pudieran guiar sobre como ir buscando o armando las rutinas lo agradeceria mucho.</div><div data-oq-toggle="true" class="re-oq-toggle">Ver más</div><div class="re-oq-footer"><div class="re-oq-autor">Medhi</div><div data-oq-ver="true" class="re-oq-ver">Leer respuesta del entrenador →</div></div></a></div></div></div></div></div><section class="re-cta"><h2 class="re-cta__title">¿No encuentras la respuesta que buscas?</h2><p class="re-cta__subtitle">
999Haz tu pregunta gratis y un entrenador personal certificado te responderá en menos de 24h</p><a href="#formulario" class="re-cta__btn">Haz tu pregunta gratis</a></section><section class="re-faq"><h2 class="re-faq__title">Preguntas frecuentes sobre el servicio</h2><div class="re-faq__list"><details class="re-faq__item re-faq__item"><summary class="re-faq__q re-faq__q">¿Cuánto cuesta preguntar a un entrenador personal?</summary><p class="re-faq__a">Es completamente gratis. Puedes hacer tantas preguntas como quieras sin ningún coste ni compromiso de contratación.</p></details><details class="re-faq__item re-faq__item"><summary class="re-faq__q re-faq__q">¿En cuánto tiempo responden los entrenadores?</summary><p class="re-faq__a">El tiempo medio de respuesta es de menos de 24 horas, aunque puede variar según la categoría y disponibilidad.</p></details><details class="re-faq__item re-faq__item"><summary class="re-faq__q re-faq__q">¿Quién responde las preguntas?</summary><p class="re-faq__a">Entrenadores personales certificados verificados por RedEntrenadores, activos en la plataforma.</p></details><details class="re-faq__item re-faq__item"><summary class="re-faq__q re-faq__q">¿Puedo hacer una pregunta de forma anónima?</summary><p class="re-faq__a">Sí, no es necesario compartir datos personales para publicar tu pregunta.</p></details><details class="re-faq__item re-faq__item"><summary class="re-faq__q re-faq__q">¿Puedo contactar directamente con el entrenador que me respondió?</summary><p class="re-faq__a">Sí, desde la respuesta puedes ver el perfil del entrenador y contactar con él si quieres profundizar.</p></details></div></section></div><footer class="footer"><div class="footer-middle footer-v1"><div class="footer-grid"><a id="w-node-_5855e537-ce68-c8ac-151a-51c4bee40e4e-1e51f0f5" href="#" class="w-inline-block"><img src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68c5a7f45b0c2bd091e87d5c_RedEntrenadores_Cropped_400px.avif" alt="Logotipo de RedEntrenadores" sizes="100vw" srcset="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68c5a7f45b0c2bd091e87d5c_RedEntrenadores_Cropped_400px-p-500.avif 500w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68c5a7f45b0c2bd091e87d5c_RedEntrenadores_Cropped_400px-p-800.avif 800w, https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/68c5a7f45b0c2bd091e87d5c_RedEntrenadores_Cropped_400px.avif 2246w" class="footer-logo"/></a><div class="w-layout-vflex"><div>Encuentra entrenador personal para alcanzar tus objetivos en cualquier ciudad de España</div><div class="space_36"></div><div><strong>Síguenos</strong></div><div class="social-grid"><a href="https://twitter.com/redentrenadores" target="_blank" class="social-link-icon w-inline-block"><div class="social-icon-font">î ©</div></a><a href="https://www.instagram.com/redentrenadores" target="_blank" class="social-link-icon w-inline-block"><div class="social-icon-font"></div></a><a href="https://facebook.com/redentrenadores" target="_blank" class="social-link-icon w-inline-block"><div class="social-icon-font"></div></a></div><div><strong>Escríbenos si necesitas ayuda.</strong></div><a href="mailto:[email protected]" class="footer-link">[email protected]</a></div><div class="w-layout-vflex"><h4 class="h4-footer">Páginas</h4><a href="/sobre-nosotros" class="footer-link">Nosotros</a><a href="/blog" class="footer-link">Blog</a><a href="/donde-entrenar" class="footer-link">Dónde entrenar</a><a href="/calculadoras/precio-entrenador-personal" class="footer-link">Cuánto cuesta un entrenador</a><a href="/calculadoras/donde-te-conviene-entrenar" class="footer-link">¿Dónde te conviene entrenar?</a><a href="/calculadoras/plan-primer-mes" class="footer-link">Tu primer mes de entrenamiento</a><a href="/calculadoras/calculadora-calorias" class="footer-link">Calculadora de calorías</a><a href="/calculadoras/calculadora-proteina" class="footer-link">Calculadora de proteína</a><a href="/calculadoras/calculadora-imc" class="footer-link">Calculadora de IMC</a><a href="/calculadoras/test-nivel-fitness" class="footer-link">Test de nivel de fitness</a><a href="/calculadoras/pruebas-fisicas-oposiciones" class="footer-link">Pruebas físicas de oposición</a><a href="/herramientas" class="footer-link">Todas las herramientas</a><a href="https://www.redentrenadores.com/entrenamiento/online" class="footer-link">Entrenador personal online</a><a href="https://www.redentrenadores.com/entrenamiento/a-domicilio" class="footer-link">Entrenador personal a domicilio</a><a href="https://www.redentrenadores.com/entrenamiento/en-un-gimnasio" class="footer-link">Entrenador personal en gimnasio</a><a href="https://www.redentrenadores.com/entrenamiento/en-exteriores" class="footer-link">Entrenador personal al aire libre</a><a href="https://www.redentrenadores.com/entrenamiento/presencial" class="footer-link">Entrenador personal presencial</a><a href="/usuarios/mi-cuenta" class="footer-link">Área de usuario</a><a href="/legal/politica-de-privacidad" class="footer-link">Política de privacidad</a><a href="/legal/terminos-y-condiciones" class="footer-link">Términos y condiciones</a><a href="/legal/aviso-legal" class="footer-link">Aviso legal</a></div><div class="w-layout-vflex"><h4 class="h4-footer">Entrenadores por ciudad</h4><a href="https://www.redentrenadores.com/entrenadores-personales/barcelona" class="footer-link">Entrenadores personales Barcelona</a><a href="https://www.redentrenadores.com/entrenadores-personales/madrid" class="footer-link">Entrenadores personales Madrid</a><a href="https://www.redentrenadores.com/entrenadores-personales/valencia" class="footer-link">Entrenadores personales Valencia</a><a href="https://www.redentrenadores.com/entrenadores-personales/malaga" class="footer-link">Entrenadores personales Málaga</a><a href="https://www.redentrenadores.com/entrenadores-personales/zaragoza" class="footer-link">Entrenadores personales Zaragoza</a><a href="https://www.redentrenadores.com/entrenadores-personales/sevilla" class="footer-link">Entrenadores personales Sevilla</a><a href="https://www.redentrenadores.com/entrenadores-personales/granada" class="footer-link">Entrenadores personales Granada</a><a href="https://www.redentrenadores.com/entrenadores-personales/alicante" class="footer-link">Entrenadores personales Alicante</a><a href="https://www.redentrenadores.com/entrenadores-personales/getafe" class="footer-link">Entrenadores personales Getafe</a><a href="https://www.redentrenadores.com/entrenadores-personales/alcala-de-henares" class="footer-link">Entrenadores personales Alcalá de Henares</a><a href="https://www.redentrenadores.com/entrenadores-personales/pozuelo-de-alarcon" class="footer-link">Entrenadores personales Pozuelo de Alarcón</a><a href="https://www.redentrenadores.com/entrenadores-personales/terrassa" class="footer-link">Entrenadores personales Terrassa</a><a href="https://www.redentrenadores.com/entrenadores-personales/badalona" class="footer-link">Entrenadores personales Badalona</a></div><div class="w-layout-vflex"><h4 class="h4-footer">Entrenadores por objetivo</h4>
999<a href="https://www.redentrenadores.com/objetivo/movilidad-flexibilidad" class="footer-link">Movilidad y flexibilidad</a><a href="https://www.redentrenadores.com/objetivo/prevencion-lesiones" class="footer-link">Prevención de lesiones</a><a href="https://www.redentrenadores.com/objetivo/preparacion-oposiciones" class="footer-link">Preparación de oposiciones</a><a href="https://www.redentrenadores.com/objetivo/mejora-forma-fisica" class="footer-link">Mejora de forma física</a><a href="https://www.redentrenadores.com/objetivo/aumento-masa-muscular" class="footer-link">Aumento de masa muscular</a><a href="https://www.redentrenadores.com/objetivo/entrenamiento-funcional" class="footer-link">Entrenamiento funcional</a><a href="https://www.redentrenadores.com/objetivo/recuperacion-lesiones" class="footer-link">Recuperación de lesiones</a><a href="https://www.redentrenadores.com/objetivo/preparacion-deportistas" class="footer-link">Preparación para deportistas</a><a href="https://www.redentrenadores.com/objetivo/correccion-postural" class="footer-link">Corrección postural</a><a href="https://www.redentrenadores.com/objetivo/perdida-peso" class="footer-link">Pérdida de peso</a><a href="https://www.redentrenadores.com/objetivo/recuperacion-posparto" class="footer-link">Recuperación posparto</a></div></div></div><div class="footer-bottom-copy"><div id="w-node-_2744af48-cdd0-c61e-6264-4fdb1e51f15a-1e51f0f5" class="text-block-14">© 2026 RedEntrenadores. Todos los derechos reservados.</div></div><div class="footer-bottom"><a href="/legal/politica-de-privacidad" class="footer-link xs">Política de Privacidad</a><a href="/legal/aviso-legal" class="footer-link xs">Aviso Legal</a><a href="/legal/terminos-y-condiciones" class="footer-link xs">Términos y condiciones</a></div><div class="w-embed"><style>
1000  /* Namespace propio `re-rec-*` en TODO. El sitio ya tiene `.re-modal`,
1001     `.re-cta` y `.re-field` en las landings y en el listado, y este bloque
1002     carga en las mismas páginas: reutilizar esos nombres es garantizarse un
1003     choque de estilos el día que alguien toque cualquiera de los dos. */
1004  #re-rec-overlay {
1005    position: fixed;
1006    inset: 0;
1007    /* En móvil `inset: 0` mide el viewport GRANDE — el de la barra del
1008       navegador escondida —, así que el borde de abajo del overlay cae por
1009       debajo de lo que se ve. Como en móvil el modal se ancla justo ahí
1010       (bottom sheet), el último campo y el botón de enviar quedaban tapados
1011       por la barra del navegador, sin forma de llegar a ellos: el modal ya
1012       está en su tope de scroll interno y lo que sobra queda fuera de
1013       pantalla. `dvh` mide el viewport visible de verdad; el `vh` de arriba
1014       es el respaldo para navegadores sin `dvh`, que se quedan como estaban.
1015       El teclado no lo arregla ninguna unidad CSS — de eso se encarga
1016       `ajustarAlViewport()` en el bloque 3. */
1017    height: 100vh;
1018    height: 100dvh;
1019    z-index: 10000;
1020    display: none;
1021    align-items: center;
1022    justify-content: center;
1023    padding: 20px;
1024    background: rgba(27, 30, 34, 0.55);
1025    font-family: 'Plus Jakarta Sans', sans-serif;
1026    box-sizing: border-box;
1027  }
1028  #re-rec-overlay.re-rec-abierto { display: flex; }
1029
1030  #re-rec-overlay .re-rec-modal {
1031    position: relative;
1032    width: 100%;
1033    max-width: 520px;
1034    max-height: 90vh;
1035    overflow-y: auto;
1036    /* Que el scroll del modal no arrastre la página de detrás al llegar al
1037       final: en iOS el encadenado hace que el sheet parezca que "se va". */
1038    overscroll-behavior: contain;
1039    background: #fff;
1040    border-radius: 12px;
1041    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.09);
1042    padding: 28px;
1043    box-sizing: border-box;
1044    /* El paso de confirmación es bastante más ancho que las preguntas (tres
1045       fichas en fila); se ensancha con una clase en vez de dejar el modal
1046       ancho todo el rato, que haría que las 4 preguntas naden en el hueco. */
1047  }
1048  #re-rec-overlay .re-rec-modal.re-rec-ancho { max-width: 640px; }
1049
1050  #re-rec-overlay .re-rec-cerrar {
1051    position: absolute;
1052    top: 10px;
1053    right: 10px;
1054    width: 44px;
1055    height: 44px;
1056    border: 0;
1057    background: none;
1058    color: #6b7280;
1059    font-size: 20px;
1060    line-height: 1;
1061    cursor: pointer;
1062  }
1063  #re-rec-overlay .re-rec-cerrar:hover { color: #1B1E22; }
1064
1065  #re-rec-overlay .re-rec-dots { display: flex; gap: 6px; margin-bottom: 18px; }
1066  #re-rec-overlay .re-rec-dots span {
1067    width: 26px;
1068    height: 4px;
1069    border-radius: 999px;
1070    background: #d9dde2;
1071  }
1072  #re-rec-overlay .re-rec-dots span.re-rec-hecho { background: #089F3B; }
1073
1074  #re-rec-overlay .re-rec-eyebrow {
1075    font-size: 12px;
1076    font-weight: 700;
1077    letter-spacing: 0.48px;
1078    text-transform: uppercase;
1079    color: #089F3B;
1080    margin: 0 0 6px;
1081  }
1082  #re-rec-overlay h3 {
1083    font-family: 'Barlow', sans-serif;
1084    font-size: 24px;
1085    line-height: 1.25;
1086    font-weight: 700;
1087    color: #1B1E22;
1088    margin: 0 0 6px;
1089    padding-right: 34px;
1090  }
1091  #re-rec-overlay .re-rec-sub {
1092    font-size: 16px;
1093    line-height: 1.6;
1094    color: #4B5563;
1095    margin: 0 0 18px;
1096  }
1097
1098  #re-rec-overlay .re-rec-pregunta {
1099    font-size: 14px;
1100    font-weight: 600;
1101    color: #1B1E22;
1102    margin: 0 0 8px;
1103  }
1104
1105  /* Opciones: botón grande de fila completa. Objetivo táctil holgado (48px)
1106     por el punto 13 del Apéndice A del design system — el código nuevo no
1107     debería nacer ya con deuda de accesibilidad. */
1108  #re-rec-overlay .re-rec-opciones { display: grid; gap: 8px; }
1109  #re-rec-overlay .re-rec-op {
1110    display: flex;
1111    align-items: center;
1112    gap: 12px;
1113    width: 100%;
1114    min-height: 48px;
1115    padding: 10px 14px;
1116    text-align: left;
1117    background: #fff;
1118    border: 1px solid #d9dde2;
1119    border-radius: 8px;
1120    font-family: inherit;
1121    font-size: 16px;
1122    font-weight: 500;
1123    color: #1B1E22;
1124    cursor: pointer;
1125    box-sizing: border-box;
1126  }
1127  #re-rec-overlay .re-rec-op:hover { border-color: #089F3B; background: #EAF3DE; }
1128  #re-rec-overlay .re-rec-op.re-rec-op-on { border-color: #089F3B; background: #EAF3DE; color: #067A2D; font-weight: 600; }
1129  #re-rec-overlay .re-rec-op-txt { flex: 1; min-width: 0; }
1130  #re-rec-overlay .re-rec-op-nota { display: block; font-size: 13px; font-weight: 400; color: #6b7280; margin-top: 2px; }
1131
1132  /* El icono va en su propia pastilla verde clara y no suelto: suelto se
1133     pierde contra el blanco de la fila, y es lo que hace que la lista se lea
1134     de un vistazo en vez de leerse renglón a renglón. Al pasar por encima el
1135     fondo de la fila se vuelve `#EAF3DE`, el mismo de la pastilla, así que
1136     ahí la pastilla invierte a verde macizo para no desaparecer. */
1137  #re-rec-overlay .re-rec-op-ico {
1138    display: flex;
1139    align-items: center;
1140    justify-content: center;
1141    width: 38px;
1142    height: 38px;
1143    flex-shrink: 0;
1144    border-radius: 10px;
1145    background: #EAF3DE;
1146    color: #089F3B;
1147  }
1148  #re-rec-overlay .re-rec-op-ico svg { width: 21px; height: 21px; display: block; }
1149  #re-rec-overlay .re-rec-op:hover .re-rec-op-ico,
1150  #re-rec-overlay .re-rec-op.re-rec-op-on .re-rec-op-ico { background: #089F3B; color: #fff; }
1151
1152  #re-rec-overlay .re-rec-op-flecha {
1153    width: 17px;
1154    height: 17px;
1155    flex-shrink: 0;
1156    color: #C9CED4;
1157  }
1158  #re-rec-overlay .re-rec-op:hover .re-rec-op-flecha,
1159  #re-rec-overlay .re-rec-op.re-rec-op-on .re-rec-op-flecha { color: #089F3B; }
1160
1161  /* "Ver más opciones" no es una opción más: va sin caja para que no compita
1162     con las dos que sí son elegibles. */
1163  #re-rec-overlay .re-rec-vermas {
1164    justify-self: start;
1165    min-height: 44px;
1166    padding: 4px 0;
1167    background: none;
1168    border: 0;
1169    cursor: pointer;
1170    font-family: inherit;
1171    font-size: 14px;
1172    font-weight: 600;
1173    color: #089F3B;
1174    text-decoration: underline;
1175  }
1176  #re-rec-overlay .re-rec-vermas:hover { color: #067A2D; }
1177
1178  #re-rec-overlay .re-rec-campo { margin-bottom: 14px; }
1179  /* La ciudad aparece debajo de las opciones de modalidad, no en su propia
1180     pantalla: necesita aire por arriba para no leerse como una quinta opción. */
1181  #re-rec-overlay .re-rec-campo-ciudad { margin-top: 18px; margin-bottom: 0; }
1182  #re-rec-overlay .re-rec-campo label {
1183    display: block;
1184    font-size: 14px;
1185    font-weight: 600;
1186    color: #1B1E22;
1187    margin-bottom: 6px;
1188  }
1189  #re-rec-overlay .re-rec-campo input {
1190    width: 100%;
1191    min-height: 48px;
1192    padding: 12px 14px;
1193    border: 1px solid #d9dde2;
1194    border-radius: 8px;
1195    font-family: inherit;
1196    font-size: 16px;
1197    color: #1B1E22;
1198    background: #fff;
1199    box-sizing: border-box;
1200  }
1201  /* 16px en los inputs y no 14: por debajo de 16 Safari en iPhone hace zoom
1202     al enfocar el campo y descoloca el modal entero. */
1203  #re-rec-overlay .re-rec-campo input:focus {
1204    outline: none;
1205    border-color: #089F3B;
1206    box-shadow: 0 0 0 3px rgba(8, 159, 59, 0.15);
1207  }
1208  #re-rec-overlay .re-rec-opcional { font-weight: 400; color: #6b7280; }
1209
1210  #re-rec-overlay .re-rec-btn {
1211    display: inline-block;
1212    border: 0;
1213    border-radius: 8px;
1214    background: #089F3B;
1215    color: #fff;
1216    font-family: inherit;
1217    font-size: 16px;
1218    font-weight: 600;
1219    padding: 13px 22px;
1220    min-height: 48px;
1221    cursor: pointer;
1222  }
1223  #re-rec-overlay .re-rec-btn:hover { background: #067A2D; }
1224  #re-rec-overlay .re-rec-btn:disabled { opacity: 0.5;
1224 cursor: not-allowed; }
1225
1226  #re-rec-overlay .re-rec-nav { display: flex; align-items: center; gap: 14px; margin-top: 12px; }
1227  #re-rec-overlay .re-rec-atras {
1228    background: none;
1229    border: 0;
1230    padding: 12px 0;
1231    cursor: pointer;
1232    font-family: inherit;
1233    font-size: 14px;
1234    font-weight: 600;
1235    color: #4B5563;
1236    text-decoration: underline;
1237  }
1238  #re-rec-overlay .re-rec-atras:hover { color: #067A2D; }
1239
1240  #re-rec-overlay .re-rec-error {
1241    display: none;
1242    margin: 12px 0 0;
1243    font-size: 14px;
1244    color: #791F1F;
1245    background: #FCEBEB;
1246    border-radius: 8px;
1247    padding: 10px 12px;
1248  }
1249  #re-rec-overlay .re-rec-legal {
1250    margin: 12px 0 0;
1251    font-size: 12px;
1252    line-height: 1.45;
1253    color: #6b7280;
1254  }
1255  #re-rec-overlay .re-rec-legal a { color: #6b7280; text-decoration: underline; }
1256
1257  /* El pie: lo que antes era el subtítulo de arriba. Va separado por un filete
1258     y en cuerpo pequeño — es contexto para quien duda, no la instrucción de la
1259     pantalla, que ahora es el título más las propias opciones. */
1260  #re-rec-overlay .re-rec-pie {
1261    display: flex;
1262    align-items: flex-start;
1263    gap: 9px;
1264    margin: 18px 0 0;
1265    padding-top: 14px;
1266    border-top: 1px solid #E5E6E8;
1267    font-size: 13px;
1268    line-height: 1.5;
1269    color: #6b7280;
1270  }
1271  #re-rec-overlay .re-rec-pie-ico { color: #089F3B; flex-shrink: 0; line-height: 0; }
1272  #re-rec-overlay .re-rec-pie-ico svg { width: 17px; height: 17px; display: block; }
1273
1274  /* ---------------------------------------------------------- confirmación */
1275
1276  #re-rec-overlay .re-rec-fichas {
1277    display: grid;
1278    grid-template-columns: repeat(3, minmax(0, 1fr));
1279    gap: 12px;
1280    margin: 18px 0 0;
1281  }
1282  /* Sin `:hover` ni cursor de mano: la ficha no es clicable (ver fichaHTML), y
1283     un borde que reacciona al pasar por encima promete un clic que no existe. */
1284  #re-rec-overlay .re-rec-ficha {
1285    display: block;
1286    text-align: center;
1287    border: 1px solid #d9dde2;
1288    border-radius: 12px;
1289    padding: 14px 10px;
1290    color: inherit;
1291  }
1292  /* El envoltorio existe solo para anclar el badge: la foto es un <img> y no
1293     puede tener hijos. Se lleva el margen que antes tenía la foto. */
1294  #re-rec-overlay .re-rec-foto-wrap {
1295    position: relative;
1296    display: block;
1297    width: 96px;
1298    margin: 0 auto 10px;
1299  }
1300  /* La foto crece dentro de la misma tarjeta: es lo único de la ficha que se
1301     reconoce de un vistazo, y a 64px competía con el texto en vez de mandar.
1302     Cuadrada y no circular: a igual ancho, el círculo recorta los laterales y
1303     se come parte de la cara. Las esquinas van a 8px, el mismo radio que los
1304     botones — no a 12px, que es el de la tarjeta que la contiene. */
1305  #re-rec-overlay .re-rec-foto {
1306    width: 100%;
1307    height: 96px;
1308    border-radius: 8px;
1309    object-fit: cover;
1310    display: block;
1311    background: #F3F3F3;
1312  }
1313
1314  /* Badge "Solo online" encima de la foto, arriba a la izquierda, como en el
1315     resto de tarjetas del sitio. Solo el icono: a 96px —y a 64px en móvil— el
1316     literal no cabe, y ya sale escrito como primer dato de la ficha, justo
1317     debajo. El texto lo llevan title y aria-label. Blanco con borde y sombra,
1318     no verde: informa de la modalidad, no premia nada (§50). */
1319  #re-rec-overlay .re-rec-online {
1320    position: absolute;
1321    top: 6px;
1322    left: 6px;
1323    z-index: 2;
1324    display: inline-flex;
1325    align-items: center;
1326    justify-content: center;
1327    width: 22px;
1328    height: 22px;
1329    box-sizing: border-box;
1330    background: #fff;
1331    border: 1px solid #d9dde2;
1332    border-radius: 999px;
1333    box-shadow: 0 2px 6px rgba(0,0,0,.18);
1334    color: #4B5563;
1335  }
1336  #re-rec-overlay .re-rec-online svg { width: 13px; height: 13px; display: block; }
1337  #re-rec-overlay .re-rec-ficha-nombre {
1338    font-family: 'Barlow', sans-serif;
1339    font-size: 15px;
1340    font-weight: 700;
1341    color: #1B1E22;
1342    display: block;
1343    line-height: 1.25;
1344  }
1345  /* Píldora pública: radio 999px, nunca los 12px de la píldora de panel. */
1346  #re-rec-overlay .re-rec-pill {
1347    display: inline-block;
1348    margin-top: 6px;
1349    padding: 4px 10px;
1350    border-radius: 999px;
1351    background: #F3F4F6;
1352    color: #4B5563;
1353    font-size: 11px;
1354    font-weight: 600;
1355    line-height: 1.3;
1356  }
1357  #re-rec-overlay .re-rec-pill-match { background: #EAF3DE; color: #089F3B; }
1358  #re-rec-overlay .re-rec-ficha-meta {
1359    font-size: 12px;
1360    line-height: 1.4;
1361    color: #4B5563;
1362    display: block;
1363    margin-top: 5px;
1364  }
1365  #re-rec-overlay .re-rec-estrella { vertical-align: -1px; }
1366
1367  #re-rec-overlay .re-rec-p2 { margin-top: 22px; padding-top: 18px; border-top: 1px solid #d9dde2; }
1368  #re-rec-overlay .re-rec-p2 h4 {
1369    font-family: 'Barlow', sans-serif;
1370    font-size: 18px;
1371    font-weight: 700;
1372    color: #1B1E22;
1373    margin: 0 0 4px;
1374  }
1375  /* minmax(0, 1fr) y no 1fr: el mínimo "auto" de un <select> es el ancho de su
1376     opción más larga, así que con 1fr las columnas no encogen y desbordan. */
1377  #re-rec-overlay .re-rec-p2-grid {
1378    display: grid;
1379    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
1380    gap: 0 14px;
1381  }
1382  #re-rec-overlay .re-rec-p2-grid > div { min-width: 0; }
1383  #re-rec-overlay .re-rec-p2-full { grid-column: 1 / -1; }
1384  #re-rec-overlay .re-rec-campo select,
1385  #re-rec-overlay .re-rec-campo textarea {
1386    width: 100%;
1387    min-height: 48px;
1388    padding: 12px 14px;
1389    border: 1px solid #d9dde2;
1390    border-radius: 8px;
1391    font-family: inherit;
1392    font-size: 16px;
1393    color: #1B1E22;
1394    background: #fff;
1395    box-sizing: border-box;
1396  }
1397  #re-rec-overlay .re-rec-campo textarea { min-height: 70px; resize: vertical; }
1398  #re-rec-overlay .re-rec-acciones { display: flex; align-items: center; gap: 18px; margin-top: 8px; }
1399  #re-rec-overlay .re-rec-saltar {
1400    background: none;
1401    border: 0;
1402    padding: 12px 0;
1403    cursor: pointer;
1404    font-family: inherit;
1405    font-size: 14px;
1406    font-weight: 600;
1407    color: #4B5563;
1408    text-decoration: underline;
1409  }
1410
1411  @media (max-width: 560px) {
1412    #re-rec-overlay {
1413      padding: 12px;
1414      /* La barra de gestos del iPhone se come los últimos ~34px: sin esto el
1415         pie del modal queda por debajo de ella. */
1416      padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
1417      align-items: flex-end;
1418    }
1419    /* `100%` es el alto del overlay ya descontado su padding, o sea lo que de
1420       verdad se ve. Antes era `92vh`, que se mide contra el viewport grande y
1421       dejaba el pie del modal fuera de pantalla. */
1422    #re-rec-overlay .re-rec-modal { padding: 22px 18px; max-height: 100%; }
1423
1424    /* Con el teclado abierto el modal se queda en ~350px de alto y el
1425       contenido no cabe de ninguna manera: al escribir la ciudad, el botón
1426       quedaba por debajo del borde del modal. Se veía cortado y no había nada
1427       que indicara que el modal tiene scroll propio, así que el paso parecía
1428       roto. Pegado abajo, el botón está siempre a la vista y lo que scrolla es
1429       lo de arriba. Solo en móvil: en escritorio no hay teclado que robe media
1430       pantalla y el modal casi nunca desborda.
1431
1432       El fondo blanco es obligatorio —el contenido pasa por detrás— y el
1433       padding de abajo replica el del modal, porque `bottom: 0` ancla contra
1434       el borde del scrollport y no contra su padding; el margen negativo lo
1435       descuenta para que en reposo, sin pegar, nada se mueva de sitio. */
1436    #re-rec-overlay .re-rec-nav {
1437      position: sticky;
1438      bottom: 0;
1439      background: #fff;
1440      padding-bottom: 22px;
1441      margin-bottom: -22px;
1442      /* El padding de arriba se descuenta del margen para que en reposo la
1443         separación siga siendo la misma. La sombra es lo que hace que, cuando
1444         hay contenido pasando por detrás, la barra se lea como barra y no como
1445         un campo cortado a medias; a esta opacidad no se nota cuando la barra
1446         está en su sitio y no tapa nada. */
1447      padding-top: 10px;
1448      margin-top: 2px;
1449      box-shadow: 0 -6px 10px -8px rgba(27, 30, 34, 0.16);
1450    }
1451    #re-rec-overlay h3 { font-size: 21px; }
1452    #re-rec-overlay .re-rec-fichas { grid-template-columns: minmax(0, 1fr); }
1453    #re-rec-overlay .re-rec-ficha { display: flex; align-items: center; gap: 14px; text-align: left; }
1454    #re-rec-overlay .re-rec-foto-wrap { width: 64px; margin: 0; flex-shrink: 0; }
1455    #re-rec-overlay .re-rec-foto { height: 64px; }
1456    #re-rec-overlay .re-rec-online { top: 4px; left: 4px;
1456 width: 20px; height: 20px; }
1457    #re-rec-overlay .re-rec-ficha-datos { min-width: 0; }
1458    #re-rec-overlay .re-rec-p2-grid { grid-template-columns: minmax(0, 1fr); }
1459  }
1460</style></div><div class="w-embed w-script">
1460<script>
1461(function () {
1462  "use strict";
1463
1464  window.RE_RECOMENDADOR_VERSION = "1.0.0";
1465
1466  var SUPABASE_URL = "https://yrzanatzshdpvypazgeb.supabase.co";
1467  var SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlyemFuYXR6c2hkcHZ5cGF6Z2ViIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODQ0NjczNzcsImV4cCI6MjEwMDA0MzM3N30.WNRs7Bc8lA1JUw-KbOHYDBlEjTIPjwxx3cvlBmdpgHU";
1468  var PRIVACIDAD_URL = "https://www.redentrenadores.com/legal/politica-de-privacidad";
1469
1470  // Rutas donde el pop-up no debe salir nunca. El panel y el área de usuario
1471  // son zonas privadas de alguien que ya está dentro; /lp/ son las landings de
1472  // paid, que ya llevan este mismo flujo dentro de su propio widget y verían
1473  // dos modales peleándose; /crear-cuenta y /crea-tu-perfil son el alta y el
1474  // onboarding del entrenador, a quien ofrecerle buscar entrenador no le sirve
1475  // de nada y encima le tapa el formulario a medio rellenar; en
1476  // /pregunta-al-experto es donde contesta preguntas. /donde-entrenar queda
1477  // fuera por decisión de Esteve (20/08/2026).
1478  var RUTAS_EXCLUIDAS = [
1479    "/panel-entrenador", "/usuarios/", "/mi-cuenta", "/admin", "/lp/",
1480    "/crear-cuenta", "/crea-tu-perfil", "/pregunta-al-experto", "/donde-entrenar"
1481  ];
1482
1483  var SEGUNDOS_ESPERA = 25;
1484  var SCROLL_DISPARO = 0.5;
1485
1486  var CLAVE_APAGADO = "re_recomendador_off";     // localStorage: no volver a salir
1487  var CLAVE_SESION = "re_recomendador_visto";    // sessionStorage: ya salió hoy
1488
1489  // Los `value` son las etiquetas exactas de `entrenadores.tipo_atencion`, no
1490  // slugs, y tienen que seguir siéndolo: es lo que ya hay guardado en
1491  // `solicitudes_contacto.modalidad` y lo que el panel del entrenador pinta
1492  // sin traducir. El label es lo único redactable para el usuario.
1493  //
1494  // El orden importa: se enseñan solo las dos primeras y el resto se destapa
1495  // con "Ver más", igual que hace el wizard de /encuentra-entrenador. Online y
1496  // gimnasio son las dos que cubren a casi todo el mundo, y una pantalla con
1497  // cuatro opciones grandes pesa más de lo que aporta como primera impresión.
1498  var MODALIDADES = [
1499    { value: "Online", label: "Online, por videollamada", nota: "Desde donde quieras" },
1500    { value: "En un gimnasio", label: "En un gimnasio", nota: "En su centro o en el tuyo" },
1501    { value: "A domicilio", label: "En mi casa", nota: "El entrenador se desplaza hasta ti" },
1502    { value: "En exteriores", label: "Al aire libre", nota: "Parques y espacios abiertos" }
1503  ];
1504  // Cuántas opciones se enseñan de entrada en cada paso; el resto va tras
1505  // "Ver más opciones". Las listas están ordenadas para que las visibles sean
1506  // las que cubren a más gente: en objetivos, las tres primeras son las que
1507  // más entrenadores trabajan (53, 47 y 45 de 95).
1508  var VISIBLES = { donde: 2, objetivo: 3 };
1509
1510  // Son las especialidades tal cual están en `entrenadores.especialidades`,
1511  // porque la RPC filtra con `especialidades @> ARRAY[p_objetivo]`: cualquier
1512  // slug inventado aquí no casaría con nadie y devolvería lista vacía. Se
1513  // ofrecen las 6 con cobertura real (11 y 5 entrenadores las menos vistas de
1514  // esta lista); las de cola larga caen en "Aún no lo sé", que no filtra.
1515  var OBJETIVOS = [
1516    { value: "Pérdida de peso", label: "Perder peso" },
1517    { value: "Ganar masa muscular", label: "Ganar masa muscular" },
1518    { value: "Mejora de forma física", label: "Estar en forma" },
1519    { value: "Entrenamiento funcional", label: "Entrenamiento funcional" },
1520    { value: "Recuperación de lesiones", label: "Recuperarme de una lesión" },
1521    { value: "Preparación para deportistas", label: "Preparar una prueba o competición" },
1522    { value: "", label: "Aún no lo tengo claro", nota: "Te ayudarán a definirlo" }
1523  ];
1524
1525  // `cuando_empieza` no tiene hoy ni una fila rellena en toda la tabla (el
1526  // campo existe en el formulario de la ficha pero no llega), así que estos
1527  // valores no chocan con nada previo. Son texto legible a propósito: el
1528  // entrenador los lee tal cual en su panel, sin traducción.
1529  var CUANDOS = [
1530    { value: "Lo antes posible", label: "Lo antes posible" },
1531    { value: "En 2-4 semanas", label: "En 2-4 semanas" },
1532    { value: "En 1-2 meses", label: "En 1 o 2 meses" },
1533    { value: "Solo me estoy informando", label: "Solo me estoy informando" }
1534  ];
1535
1536  var NIVELES = [
1537    { value: "sedentario", label: "Sedentario, casi no me muevo" },
1538    { value: "poco-activo", label: "Poco activo, 1-2 días" },
1539    { value: "activo", label: "Activo, 3-4 días" },
1540    { value: "muy-activo", label: "Muy activo, 5 días o más" }
1541  ];
1542
1543  var estado = {
1544    indice: 0,                // posición dentro de pasos()
1545    fase: "preguntas",        // "preguntas" | "confirmacion" | "cierre"
1546    modalidad: "",
1547    abiertas: {},             // pasos cuyo "Ver más" ya se ha pulsado
1548    ciudad: "",
1549    ciudadDePagina: false,    // en las 60 páginas de ciudad tampoco se pregunta
1550    objetivo: "",
1551    objetivoDePagina: false,  // en /objetivo/* no se pregunta, ya lo sabemos
1552    objetivoRespondido: false,
1553    cuando: "",
1554    nombre: "",
1555    email: "",
1556    telefono: "",
1557    enviando: false,
1558    error: "",
1559    top3: [],
1560    top3Clave: null,          // respuestas con las que se busco ese reparto
1561    claimToken: null,
1562
1563    // Solo para la medición: qué pantallas se han contado ya en esta apertura,
1564    // cuándo se abrió y si esta apertura cuenta. Las previsualizaciones y el
1565    // ?recomendador=1 no cuentan: si no, cada vez que se revisa el copy se
1566    // ensucia el embudo con una sesión que no es de nadie.
1567    vistas: {},
1568    abiertoEn: 0,
1569    midiendo: true
1570  };
1571
1572  // La secuencia de pantallas no es fija: quien entrena online no tiene que
1573  // decir su ciudad, y en una página de ciudad o de objetivo esa respuesta ya
1574  // la da la propia página. Se calcula en cada render en vez de encadenar
1575  // condiciones dentro de cada paso — un contador fijo de "paso N de 4" con
1576  // ramas de distinta longitud acaba pintando 5 pantallas bajo 4 puntos.
1577  //
1578  // Nunca se pregunta algo que ya sabemos: quien entra por
1579  // /entrenadores-personales/valencia ya ha dicho dónde busca, y volver a
1580  // preguntárselo se lee como que no le hemos escuchado.
1581  function pasos() {
1582    var lista = ["donde"];
1583    if (!estado.objetivoDePagina) lista.push("objetivo");
1584    lista.push("cuando", "datos");
1585    return lista;
1586  }
1587
1588  function pasoActual() {
1589    return pasos()[estado.indice];
1590  }
1591
1592  var ciudades = [];
1593
1594  // ------------------------------------------------------------------ utils
1595
1596  function esc(str) {
1597    var d = document.createElement("div");
1598    d.textContent = str == null ? "" : String(str);
1599    return d.innerHTML;
1600  }
1601
1602  function leer(clave, almacen) {
1603    try { return almacen.getItem(clave); } catch (e) { return null; }
1604  }
1605
1606  function escribir(clave, valor, almacen) {
1607    try { almacen.setItem(clave, valor); } catch (e) {}
1608  }
1609
1610  // La ciudad de las 60 páginas de ciudad sale de #re-ciudad-nombre, el
1611  // elemento oculto bindeado al campo Nombre de la colección Ciudades que ya
1612  // usa el glue del listado. Se usa el nombre y no el slug porque la RPC casa
1613  // por `ciudad_nombre ILIKE` y hay ciudades cuyo slug no coincide con su
1614  // nombre ("la-sagra-toledo" contra "La sagra  Toledo").
1615  function ciudadDeLaPagina() {
1616    var el = document.getElementById("re-ciudad-nombre");
1617    if (el && el.textContent.trim()) return el.textContent.trim();
1618    var param = new URLSearchParams(location.search).get("ciudad");
1619    return param ? param.trim() : "";
1620  }
1621
1622  // Las marcas que deja NFD al separar las tildes (U+0300-U+036F). Va con
1623  // fromCharCode y no con un escape unicode porque este bloque se sube al
1624  // embed dentro del JSON de una llamada MCP y esa secuencia no sobrevive:
1625  // queda el carácter combinante suelto, invisible en el editor. El rango es
1626  // exactamente el mismo. La propiedad Diacritic no vale como atajo: deja
1627  // fuera 19 puntos de este bloque.
1628  var MARCAS_COMBINANTES = new RegExp(
1629    "[" + String.fromCharCode(0x300) + "-" + String.fromCharCode(0x36f) + "]", "g");
1630
1631  // En /objetivo/* el objetivo ya lo dice la propia página: preguntarlo otra
1632  // vez es hacerle repetir al usuario lo que acaba de elegir al entrar.
1633  function aSlug(texto) {
1634    return texto.toLowerCase()
1635      .normalize("NFD").replace(MARCAS_COMBINANTES, "")
1636      .replace(/[^a-z0-9]+/g, "-")
1637      .replace(/^-|-$/g, "");
1638  }
1639
1640  function objetivoDeLaPagina() {
1641    var m = location.pathname.match(/^\/objetivo\/([^/]+)/);
1642    if (!m) return null;
1643    var encontrado = OBJETIVOS.filter(function (o) { return o.value; })
1644      .find(function (o) { return aSlug(o.value) === m[1]; });
1645    return encontrado ? encontrado.value : null;
1646  }
1647
1648  // ------------------------------------------------------------------ datos
1649
1650  function rest(ruta, opciones) {
1651    var o = opciones || {};
1652    return fetch(SUPABASE_URL + ruta, {
1653      method: o.method || "GET",
1654      headers: {
1655        apikey: SUPABASE_ANON_KEY,
1656        Authorization: "Bearer " + SUPABASE_ANON_KEY,
1657        "Content-Type": "application/json"
1658      },
1659      body: o.body ? JSON.stringify(o.body) : undefined
1660    }).then(function (res) {
1661      if (!res.ok) throw new Error(ruta + " -> " + res.status);
1662      return res.json();
1663    });
1664  }
1665
1666  function cargarCiudades() {
1667    if (ciudades.length) return Promise.resolve(ciudades);
1668    return rest("/rest/v1/entrenadores_publico?select=ciudad_nombre&tipo_entrenador=eq.personal&ciudad_nombre=not.is.null")
1669      .then(function (filas) {
1670        ciudades = Array.from(new Set(filas.map(function (f) { return f.ciudad_nombre; })))
1671          .sort(function (a, b) { return a.localeCompare(b, "es"); });
1672        var dl = document.getElementById("re-rec-ciudades");
1673        if (dl) dl.innerHTML = ciudades.map(function (c) { return '<option value="' + esc(c) + '">'; }).join("");
1674        return ciudades;
1675      })
1676      .catch(function (err) {
1677        // Sin la lista el campo sigue siendo un input de texto libre y la RPC
1678        // casa por ILIKE, así que se puede enviar igual: solo se pierde el
1679        // autocompletado. No es motivo para bloquear el formulario.
1680        console.warn("[re-recomendador] no se pudieron cargar las ciudades", err);
1681        return [];
1682      });
1683  }
1684
1685  // A quién se le manda. Se pide a la MISMA RPC que pinta el listado público,
1686  // así que el orden es el mismo que ve el usuario si navega él solo — no un
1687  // ranking paralelo que dijera otra cosa.
1688  //
1689  // NO se fuerza a que salgan tres. Si con lo que ha pedido solo encaja uno, se
1690  // le manda a ese uno y ya: rellenar el hueco con gente que no encaja es un
1691  // mal reparto para el usuario y un contacto quemado para el entrenador.
1692  // Valladolid es el caso real: un único entrenador en toda la ciudad, y la
1693  // versión vieja acababa soltando la ciudad para llegar a tres, así que
1694  // devolvía tres de cualquier sitio menos el de Valladolid.
1695  //
1696  // Los filtros solo se sueltan cuando no encaja NADIE, de menos a más
1697  // importante: primero el objetivo (casi cualquier entrenador sirve para un
1698  // objetivo que no tenga marcado), luego la modalidad, y la ciudad la última
1699  // porque quien no cubre la zona no puede atender a este cliente y punto.
1700  function buscarTop3() {
1701    var intentos = [
1702      { objetivo: estado.objetivo, modalidad: estado.modalidad, ciudad: estado.ciudad },
1703      { objetivo: "", modalidad: estado.modalidad, ciudad: estado.ciudad },
1704      { objetivo: "", modalidad: "", ciudad: estado.ciudad },
1705      { objetivo: "", modalidad: estado.modalidad, ciudad: "" }
1706    ];
1707
1708    function intentar(i) {
1709      if (i >= intentos.length) return Promise.resolve([]);
1710      var f = intentos[i];
1711      return rest("/rest/v1/rpc/buscar_entrenadores_listado", {
1712        method: "POST",
1713        body: {
1714          p_objetivo: f.objetivo || null,
1715          p_modalidad: f.modalidad || null,
1716          p_ciudad: f.ciudad || null,
1717          p_perfil: null,
1718          p_precio_min: null,
1719          p_precio_max: null,
1720          p_orden: "relevancia",
1721          p_limite: 3,
1722          p_offset: 0
1723        }
1724      }).then(function (filas) {
1725        // Basta con uno: el siguiente intento no sería "mejor", sería menos
1726        // fiel a lo que ha pedido.
1727        if (filas && filas.length) return filas;
1728        return intentar(i + 1);
1729      });
1730    }
1731
1732    return intentar(0);
1733  }
1734
1735  // ------------------------------------------------------------------ vistas
1736
1737  function dotsHTML() {
1738    var total = pasos().length;
1739    var html = "";
1740    for (var i = 0; i < total; i++) {
1741      html += '<span class="' + (i <= estado.indice ? "re-rec-hecho" : "") + '"></span>';
1742    }
1743    return '<div class="re-rec-dots">' + html + "</div>";
1744  }
1745
1746  function cabeceraHTML(eyebrow, titulo, sub) {
1747    return '<button type="button" class="re-rec-cerrar" data-accion="cerrar" aria-label="Cerrar">&#10005;</button>' +
1748      dotsHTML() +
1749      (eyebrow ? '<p class="re-rec-eyebrow">' + esc(eyebrow) + "</p>" : "") +
1750      "<h3>" + esc(titulo) + "</h3>" +
1751      (sub ? '<p class="re-rec-sub">' + esc(sub) + "</p>" : "");
1752  }
1753
1754  // Las opciones se identifican por su valor y no por su posición: con la
1755  // lista de modalidades plegada, un índice apuntaría a la posición dentro de
1756  // lo que se está pintando y no dentro de la lista real.
1757  // Pinta las opciones de un paso ya recortadas a las visibles, y decide sola
1758  // si hace falta el "Ver más": así ningún paso puede quedarse con el enlace
1759  // puesto pero sin nada que destapar.
1760  function opcionesDelPaso(paso, lista, seleccionado) {
1761    var tope = VISIBLES[paso];
1762    var plegado = tope && !estado.abiertas[paso] && lista.length > tope;
1763    return opcionesHTML(plegado ? lista.slice(0, tope) : lista, seleccionado, plegado);
1764  }
1765
1766  function opcionesHTML(lista, seleccionado, verMas) {
1767    return '<div class="re-rec-opciones">' + lista.map(function (o) {
1768      var on = o.value === seleccionado;
1769      return '<button type="button" class="re-rec-op' + (on ? " re-rec-op-on" : "") + '"' +
1770        ' data-accion="opcion" data-valor="' + esc(o.value) + '">' +
1771        "<span>" + esc(o.label) +
1772        (o.nota ? '<span class="re-rec-op-nota">' + esc(o.nota) + "</span>" : "") +
1773        "</span></button>";
1774    }).join("") +
1775      (verMas ? '<button type="button" class="re-rec-vermas" data-accion="ver-mas">Ver más opciones</button>' : "") +
1776      "</div>";
1777  }
1778
1779  function navHTML(conAtras, textoBoton) {
1780    return '<div class="re-rec-nav">' +
1781      (conAtras ? '<button type="button" class="re-rec-atras" data-accion="atras">Atrás</button>' : "") +
1782      (textoBoton ? '<button type="button" class="re-rec-btn" data-accion="siguiente">' + esc(textoBoton) + "</button>" : "") +
1783      "</div>";
1784  }
1785
1786  function errorHTML() {
1787    return '<div class="re-rec-error"' + (estado.error ? ' style="display:block"' : "") + ">" + esc(estado.error) + "</div>";
1788  }
1789
1790  // ¿Hace falta preguntar la ciudad? Solo si ya ha dicho que quiere entrenar
1791  // presencialmente y la página no nos la ha dado hecha. Entrenar por
1792  // videollamada no depende de dónde viva, y filtrar por su ciudad ahí solo
1793  // serviría para dejar fuera a entrenadores que le valdrían igual.
1794  function pideCiudad() {
1795    return !!estado.modalidad && estado.modalidad !== "Online" && !estado.ciudadDePagina;
1796  }
1797
1798  // Modalidad y ciudad comparten pantalla: son la misma pregunta ("¿dónde?")
1799  // partida en dos, y separarlas metía un paso entero para un solo campo. El
1800  // campo de ciudad no está desde el principio — aparece al elegir una
1801  // modalidad presencial, porque hasta entonces no se sabe si hace falta.
1802  function pasoDondeHTML() {
1803    // El número va escrito y se cuenta solo. Dos motivos: el recorrido no
1804    // siempre tiene los mismos pasos (el objetivo se salta en /objetivo/*), y
1805    // la última pantalla no es una pregunta sino el formulario de contacto —
1806    // por eso se descuenta uno y no se anuncian cuatro preguntas.
1807    var PALABRAS = { 1: "Una", 2: "Dos", 3: "Tres" };
1808    return cabeceraHTML(
1809      "Te ayudamos a decidir",
1810      "¿Todavía no lo tienes claro? Te recomendamos un entrenador.",
1811      (PALABRAS[pasos().length - 1] || "Unas") + " preguntas rápidas" +
1812        " y te ponemos en contacto con los entrenadores" +
1813        (estado.ciudad ? " de " + estado.ciudad : "") +
1814        " que mejor encajan contigo. Gratis y sin compromiso."
1815    ) +
1816      // Al dejar de ser el título, la primera pregunta necesita su propia
1817      // etiqueta: sin ella las cuatro opciones quedan colgando del subtítulo
1818      // y no se entiende qué se está eligiendo.
1819      '<p class="re-rec-pregunta">¿Dónde te gustaría entrenar?</p>' +
1820      opcionesDelPaso("donde", MODALIDADES, estado.modalidad) +
1821      (pideCiudad()
1822        ? '<div class="re-rec-campo re-rec-campo-ciudad">' +
1823            '<label for="re-rec-ciudad">¿En qué ciudad?</label>' +
1824            '<input type="text" id="re-rec-ciudad" list="re-rec-ciudades" autocomplete="address-level2" placeholder="Empieza a escribir…" value="' + esc(estado.ciudad) + '">' +
1825            '<datalist id="re-rec-ciudades"></datalist>' +
1826          "</div>"
1827        : "") +
1828      errorHTML() +
1829      (pideCiudad() ? navHTML(false, "Siguiente") : "");
1830  }
1831
1832  function pasoObjetivoHTML() {
1833    return cabeceraHTML("", "¿Cuál es tu objetivo?", "Con esto sabemos qué entrenadores están especializados en lo que buscas.") +
1834      // El centinela es `null` y no "": "" es una respuesta valida ("aun no lo
1835      // tengo claro"), y sin distinguirla esa opcion saldria ya marcada antes
1836      // de que el usuario hubiera contestado nada.
1837      opcionesDelPaso("objetivo", OBJETIVOS, estado.objetivoRespondido ? estado.objetivo : null) +
1838      errorHTML() +
1839      navHTML(true, "");
1840  }
1841
1842  function pasoCuandoHTML() {
1843    return cabeceraHTML("", "¿Cuándo te gustaría empezar?", "Así te proponen un plan realista desde el primer mensaje.") +
1844      opcionesHTML(CUANDOS, estado.cuando) +
1845      errorHTML() +
1846      navHTML(true, "");
1847  }
1848
1849  // "Ana, Marcos y Juanma" — coma entre todos menos el último, que va con "y".
1850  function enumerar(nombres) {
1851    if (nombres.length === 1) return nombres[0];
1852    return nombres.slice(0, -1).join(", ") + " y " + nombres[nombres.length - 1];
1853  }
1854
1855  // El subtítulo de la última pantalla nombra a los tres entrenadores en
1856  // cuanto se sabe quiénes son. Mientras la búsqueda va en camino (o si
1857  // falla) se cae a la versión sin nombres: pedir el email es el momento de
1858  // menos paciencia del recorrido y no puede quedarse esperando a nada.
1859  function subtituloDatosHTML() {
1860    if (!estado.top3.length) {
1861      return "Enviaremos tu solicitud a los entrenadores que mejor encajan contigo. Te escribirán ellos directamente.";
1862    }
1863    var nombres = estado.top3.map(function (e) { return "<strong>" + esc(e.nombre) + "</strong>"; });
1864    return enumerar(nombres) +
1865      (estado.top3.length === 1 ? " es el que mejor encaja" : " son los que mejor encajan") +
1866      " contigo. Te " + (estado.top3.length === 1 ? "escribirá" : "escribirán") + " directamente.";
1867  }
1868
1869  // El aviso legal tiene que decir con cuánta gente se comparte el dato, y
1870  // ya no siempre son tres: si en la ciudad solo encaja uno, es uno. Si la
1871  // búsqueda aún no ha vuelto no se inventa un número.
1872  function conQuienHTML() {
1873    var cuantos = estado.top3.length;
1874    if (cuantos === 1) return "este entrenador";
1875    if (cuantos) return "estos " + cuantos + " entrenadores";
1876    return "los entrenadores que mejor encajen contigo";
1877  }
1878
1879  function pasoDatosHTML() {
1880    return cabeceraHTML("", "Último paso: ¿cómo te contactamos?", null) +
1881      '<p class="re-rec-sub">' + subtituloDatosHTML() + "</p>" +
1882      '<div class="re-rec-campo">' +
1883        '<label for="re-rec-nombre">Nombre</label>' +
1884        '<input type="text" id="re-rec-nombre" autocomplete="given-name" value="' + esc(estado.nombre) + '">' +
1885      "</div>" +
1886      // El email vuelve al paso 1 y es obligatorio: sin él no hay chat, porque
1887      // no hay a quién avisar de un mensaje nuevo ni con qué entrar al área
1888      // (la cuenta es passwordless por email). Ver specs/spec-chat-plataforma.md.
1889      '<div class="re-rec-campo">' +
1890        '<label for="re-rec-email">Email</label>' +
1891        '<input type="email" id="re-rec-email" autocomplete="email" inputmode="email" placeholder="[email protected]" value="' + esc(estado.email) + '">' +
1892      "</div>" +
1893      // El teléfono se queda, pero pasa a opcional. No se quita: es el dato con
1894      // el que el entrenador llama, y en los 90 días previos las solicitudes
1895      // que solo lo traían se gestionaron casi todas, frente a dos tercios de
1896      // las que solo traían email.
1897      '<div class="re-rec-campo">' +
1898        '<label for="re-rec-telefono">Teléfono <span class="re-rec-opcional">(opcional)</span></label>' +
1899        '<input type="tel" id="re-rec-telefono" autocomplete="tel" inputmode="tel" placeholder="600 11 22 33" value="' + esc(estado.telefono) + '">' +
1900      "</div>" +
1901      errorHTML() +
1902      '<div class="re-rec-nav">' +
1903        '<button type="button" class="re-rec-atras" data-accion="atras">Atrás</button>' +
1904        '<button type="button" class="re-rec-btn" data-accion="enviar"' + (estado.enviando ? " disabled" : "") + ">" +
1905          (estado.enviando ? "Enviando…" : "Enviar mi solicitud") +
1906        "</button>" +
1907      "</div>" +
1908      '<p class="re-rec-legal">Al enviar aceptas nuestra <a href="' + PRIVACIDAD_URL + '" target="_blank" rel="noopener">política de privacidad</a>. Compartiremos tu nombre y tus datos de contacto con ' + conQuienHTML() + ' para que puedan responderte.</p>';
1909  }
1910
1911  // Estrella siempre en el verde de marca, con independencia de la nota: es la
1912  // convención del listado público (§48 del design system), no un color de
1913  // estado que deba variar con el resultado.
1914  var ESTRELLA_SVG = '<svg class="re-rec-estrella" width="13" height="13" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0l2.47 5.01 5.53.8-4 3.9.94 5.5L8 12.6l-4.94 2.6.94-5.5-4-3.9 5.53-.8L8 0z" fill="#089F3B"/></svg>';
1915
1916  // La píldora solo lleva el check cuando ese entrenador trabaja de verdad el
1917  // objetivo que ha pedido el usuario. Cuando el reparto ha tenido que soltar
1918  // el filtro de objetivo para completar los tres (o cuando ha contestado "aún
1919  // no lo tengo claro"), se enseña su especialidad principal sin check: decir
1920  // "coincide con lo que buscas" de alguien que no lo trabaja es mentir en la
1921  // pantalla donde más confianza hace falta.
1922  function especialidadHTML(e) {
1923    var suyas = e.especialidades || [];
1924    if (!suyas.length) return "";
1925
1926    var coincide = estado.objetivo && suyas.indexOf(estado.objetivo) !== -1;
1927    var texto = coincide ? etiquetaObjetivo(estado.objetivo) : suyas[0];
1928    return '<span class="re-rec-pill' + (coincide ? " re-rec-pill-match" : "") + '">' +
1929      (coincide ? "✓ " : "") + esc(texto) +
1930      "</span>";
1931  }
1932
1933  // "Pérdida de peso" es como está guardado; "Perder peso" es como se lo hemos
1934  // preguntado. Se le devuelve su propia palabra.
1935  function etiquetaObjetivo(valor) {
1936    var op = OBJETIVOS.find(function (o) { return o.value === valor; });
1937    return op ? op.label : valor;
1938  }
1939
1940  function fichaHTML(e) {
1941    // Sin `loading="lazy"`: son tres fotos que se pintan ya visibles y en el
1942    // momento en que se muestra el modal. Diferirlas solo consigue que la
1943    // confirmación aparezca un instante con tres huecos grises.
1944    var foto = e.foto_url
1945      ? '<img class="re-rec-foto" src="' + esc(e.foto_url) + '" alt="' + esc(e.nombre) + '">'
1946      : '<div class="re-rec-foto"></div>';
1947
1948    // Nota y experiencia, y nada más: el precio se probó como relleno y se
1949    // descartó (decisión de Esteve del 17/08/2026). Esta pantalla va de si el
1950    // entrenador encaja, y meter una cifra en euros justo cuando el usuario
1951    // acaba de dar sus datos convierte una confirmación en un escaparate.
1952    //
1953    // La consecuencia asumida es que muchas fichas se quedan solo con nombre y
1954    // especialidad: de los 93 entrenadores personales, 22 tienen años de
1955    // experiencia y 15 tienen opiniones. Eso no se arregla aquí sino haciendo
1956    // que rellenen esos campos.
1957    //
1958    // Nunca se pinta un cero: un "0 opiniones" o un "0 años" en la pantalla
1959    // que tiene que dar confianza juega en contra del entrenador.
1960    var datos = [];
1961    if (e.total_reviews > 0) {
1962      datos.push(ESTRELLA_SVG + " " + e.rating_promedio + " (" + e.total_reviews +
1963        (e.total_reviews === 1 ? " opinión)" : " opiniones)"));
1964    }
1965    if (e.anos_experiencia > 0) {
1966      // "de experiencia" completo y no solo "2 años": suelto, se lee como la
1967      // edad del entrenador.
1968      datos.push(e.anos_experiencia + (e.anos_experiencia === 1 ? " año" : " años") + " de experiencia");
1969    }
1970
1971    // La ficha NO es un enlace, a propósito. Debajo de estas tres tarjetas
1972    // está el paso de completar el perfil, que es lo único que queda por
1973    // hacer y lo que da valor a la solicitud recién enviada; un enlace al
1974    // perfil aquí arriba se lleva al usuario a otra pestaña justo antes de
1975    // llegar a él. Las fotos y los datos ya cumplen su función, que es
1976    // demostrar que la solicitud ha ido a gente real.
1977    return '<div class="re-rec-ficha">' +
1978      foto +
1979      '<span class="re-rec-ficha-datos">' +
1980        '<span class="re-rec-ficha-nombre">' + esc(e.nombre) + "</span>" +
1981        especialidadHTML(e) +
1982        // Cada dato en su línea y no separados por "·": en una tarjeta de
1983        // 180px, "5 (12 opiniones) · 8 años de experiencia" se parte por donde
1984        // le toca y deja huérfano medio dato en la línea siguiente.
1985        datos.map(function (d) { return '<span class="re-rec-ficha-meta">' + d + "</span>"; }).join("") +
1986      "</span>" +
1987      "</div>";
1988  }
1989
1990  function opcionesSelectHTML(lista, placeholder) {
1991    return '<option value="">' + esc(placeholder) + "</option>" + lista.map(function (o) {
1992      return '<option value="' + esc(o.value) + '">
1992' + esc(o.label) + "</option>";
1993    }).join("");
1994  }
1995
1996  // Paso 2 del journey de usuario. Va SIEMPRE después de la solicitud, nunca
1997  // antes: es opcional y no puede convertirse en un peaje para escribir a un
1998  // entrenador. `claim_token` es lo que devuelve solicitar-contacto-entrenador
1999  // y lo único que prueba que quien rellena esto es quien acaba de enviar.
2000  function confirmacionHTML() {
2001    var cuantos = estado.top3.length;
2002    var p2 = estado.claimToken
2003      ? '<div class="re-rec-p2">' +
2004          "<h4>Cuéntanos algo más</h4>" +
2005          '<p class="re-rec-sub" style="font-size:14px;margin-bottom:14px">Opcional y son 20 segundos, pero con esto te preparan una propuesta a tu medida.</p>' +
2006          // El nivel de actividad va a fila completa porque su etiqueta no
2007          // cabe a media columna; edad y peso comparten fila, que son dos
2008          // campos de tres caracteres.
2009          '<div class="re-rec-p2-grid">' +
2010            // El email ya no se pide aquí: lo trae el paso 1, obligatorio.
2011            // Pedirlo dos veces es peor que pedirlo una.
2012            '<div class="re-rec-campo re-rec-p2-full"><label for="re-rec-actividad">¿Cuánto ejercicio haces ahora?</label><select id="re-rec-actividad">' + opcionesSelectHTML(NIVELES, "Selecciona tu nivel") + "</select></div>" +
2013            '<div class="re-rec-campo"><label for="re-rec-edad">Edad</label><input type="number" id="re-rec-edad" min="10" max="120" inputmode="numeric" placeholder="Años"></div>' +
2014            '<div class="re-rec-campo"><label for="re-rec-peso">Peso</label><input type="number" id="re-rec-peso" min="20" max="400" step="0.1" inputmode="decimal" placeholder="Kg"></div>' +
2015            // Altura y disponibilidad los pide la tarjeta de "Solicitud inicial"
2016            // del panel del entrenador, que enseña ocho datos del cliente.
2017            // Están aquí y no en el paso 1 por lo mismo que el resto: el paso 1
2018            // pide lo mínimo para poder responder, esto es lo que ayuda a
2019            // preparar la propuesta y se pregunta cuando ya no cuesta conversión.
2020            '<div class="re-rec-campo"><label for="re-rec-altura">Altura</label><input type="number" id="re-rec-altura" min="100" max="250" inputmode="numeric" placeholder="Cm"></div>' +
2021            '<div class="re-rec-campo"><label for="re-rec-disponibilidad">¿Cuándo puedes entrenar?</label><input type="text" id="re-rec-disponibilidad" placeholder="Tardes entre semana"></div>' +
2022            '<div class="re-rec-campo re-rec-p2-full"><label for="re-rec-notas">¿Algo más que debamos saber?</label><textarea id="re-rec-notas" placeholder="Lesiones, horarios que te van bien, experiencia previa…"></textarea></div>' +
2023          "</div>" +
2024          '<div class="re-rec-error" id="re-rec-p2-error"></div>' +
2025          '<div class="re-rec-acciones">' +
2026            '<button type="button" class="re-rec-btn" data-accion="guardar-perfil">Enviar mis datos</button>' +
2027            '<button type="button" class="re-rec-saltar" data-accion="cerrar">Ahora no</button>' +
2028          "</div>" +
2029        "</div>"
2030      : "";
2031
2032    return '<button type="button" class="re-rec-cerrar" data-accion="cerrar" aria-label="Cerrar">&#10005;</button>' +
2033      '<p class="re-rec-eyebrow">Solicitud enviada</p>' +
2034      "<h3>" + (cuantos === 1 ? "Este es tu entrenador" : "Estos son tus " + cuantos + " entrenadores") + "</h3>" +
2035      '<p class="re-rec-sub">Ya tienen tu solicitud y te escribirán directamente.</p>' +
2036      '<div class="re-rec-fichas">' + estado.top3.map(fichaHTML).join("") + "</div>" +
2037      p2;
2038  }
2039
2040  // `ofreceCuenta` sigue siendo un parámetro y no un `true` fijo aunque el
2041  // email vuelva a ser obligatorio en el paso 1: este cierre también lo pinta
2042  // el camino en el que el paso 2 falla, y ahí no se da nada por hecho.
2043  function cierreHTML(guardado, ofreceCuenta) {
2044    var corto = estado.nombre ? estado.nombre.trim().split(" ")[0] : "";
2045    return '<button type="button" class="re-rec-cerrar" data-accion="cerrar" aria-label="Cerrar">&#10005;</button>' +
2046      "<h3>" + esc(guardado ? "¡Listo!" : (corto ? "¡Gracias, " + corto + "!" : "¡Gracias!")) + "</h3>" +
2047      '<p class="re-rec-sub">' +
2048        (guardado
2049          ? "Tus datos ya van con la solicitud. Se pondrán en contacto contigo muy pronto."
2050          : "Tu solicitud está enviada. Se pondrán en contacto contigo muy pronto.") +
2051      "</p>" +
2052      (estado.claimToken && ofreceCuenta
2053        ? '<div class="re-rec-p2">' +
2054            "<h4>¿Quieres seguir tus solicitudes?</h4>" +
2055            '<p class="re-rec-sub" style="font-size:14px;margin-bottom:14px">Crea tu cuenta y consulta cuándo las ve cada entrenador. Sin contraseña y sin salir de aquí.</p>' +
2056            '<div class="re-rec-error" id="re-rec-cuenta-error"></div>' +
2057            '<button type="button" class="re-rec-btn" data-accion="crear-cuenta">Crear mi cuenta</button>' +
2058            '<p class="re-rec-legal">Al crear la cuenta aceptas nuestra <a href="' + PRIVACIDAD_URL + '" target="_blank" rel="noopener">política de privacidad</a>.</p>' +
2059          "</div>"
2060        : "");
2061  }
2062
2063  // ------------------------------------------------------------------ puente
2064
2065  // El widget no cabe en un solo Embed de Webflow (tope de 50.000 caracteres),
2066  // asi que va partido en dos bloques. Esto es TODO lo que cruza de este
2067  // bloque al siguiente; es interno y no es la API publica, que sigue siendo
2068  // window.reRecomendador y la monta el bloque de comportamiento.
2069  //
2070  // `estado` cruza como referencia, no como copia: los dos bloques escriben
2071  // sobre el mismo objeto y esa es justo la idea.
2072  window.__reRecNucleo = {
2073    RUTAS_EXCLUIDAS: RUTAS_EXCLUIDAS,
2074    SEGUNDOS_ESPERA: SEGUNDOS_ESPERA,
2075    SCROLL_DISPARO: SCROLL_DISPARO,
2076    CLAVE_APAGADO: CLAVE_APAGADO,
2077    CLAVE_SESION: CLAVE_SESION,
2078    MODALIDADES: MODALIDADES,
2079    OBJETIVOS: OBJETIVOS,
2080    CUANDOS: CUANDOS,
2081    estado: estado,
2082    pasos: pasos,
2083    pasoActual: pasoActual,
2084    pideCiudad: pideCiudad,
2085    leer: leer,
2086    escribir: escribir,
2087    ciudadDeLaPagina: ciudadDeLaPagina,
2088    objetivoDeLaPagina: objetivoDeLaPagina,
2089    rest: rest,
2090    cargarCiudades: cargarCiudades,
2091    buscarTop3: buscarTop3,
2092    pasoDondeHTML: pasoDondeHTML,
2093    pasoObjetivoHTML: pasoObjetivoHTML,
2094    pasoCuandoHTML: pasoCuandoHTML,
2095    pasoDatosHTML: pasoDatosHTML,
2096    subtituloDatosHTML: subtituloDatosHTML,
2097    confirmacionHTML: confirmacionHTML,
2098    cierreHTML: cierreHTML
2099  };
2100})();
2101</script>
2101
2102</div><div class="w-embed w-script"><!-- RedEntrenadores - Parche del pop-up recomendador: a quien se reparte -->
2103<!-- Va en el Footer V1, ENTRE el bloque 2 (JS nucleo) y el bloque 3 (JS
2104     comportamiento), y tiene que seguir ahi: el bloque 3 se lleva `buscarTop3`
2105     a una variable local nada mas cargar, asi que quien quiera cambiarla tiene
2106     que hacerlo antes.
2107
2108     Que cambia. El nucleo reparte con `buscar_entrenadores_listado`, que es la
2109     RPC del listado publico y ordena por un score de cinco criterios (plan 35,
2110     puja 25, recomendaciones 20, calidad 12, valoracion 8). Eso vale para
2111     ordenar un listado, pero repartir no es ordenar: con 180 entrenadores el
2112     score le daba casi todos los contactos a los mismos seis.
2113
2114     Desde el 12/09/2026 el reparto son dos criterios y ni uno mas
2115     (`buscar_entrenadores_reparto`, migracion 20260913030000):
2116
2117       1. primero los de plan de pago, que pagan por eso;
2118       2. despues todos los demas, empezando por quien menos leads ha recibido.
2119
2120     Y sigue haciendo lo de antes: `p_solo_contactables` deja fuera del reparto
2121     a los perfiles de escaparate (`entrenadores.recibe_contactos = false`), que
2122     tienen que seguir saliendo en el listado y no pueden salir aqui, porque
2123     aqui no se enseña, se reparte el contacto. El servidor ya los descartaba
2124     (`solicitar-contacto-entrenador` no les manda nada), pero el pop-up
2125     enseñaba su tarjeta diciendo "ya tienen tu solicitud", que de ellos no era
2126     verdad.
2127
2128     Por que un parche y no una linea en el bloque 2. Escribir ese embed por
2129     MCP obliga a reproducir sus 34 KB enteros a mano, sobre el flujo que trae
2130     los leads. Esto son 30 lineas y se puede tirar entero el dia que se toque
2131     el nucleo: entonces, cambia alli la RPC y borra este embed.
2132
2133     La cascada de intentos es copia literal del nucleo, con el corte en cuanto
2134     hay UNO (23/08/2026, cambiado en los dos sitios a la vez). Si algun dia
2135     cambia la del nucleo, el numero de version de abajo apaga el parche en vez
2136     de imponer la version vieja en silencio.
2137
2138     ID del embed: e4e348ca-b183-eafe-444a-cd8dbed588ae (componente Footer V1). -->
2139<script>
2140(function () {
2141  "use strict";
2142
2143  var N = window.__reRecNucleo;
2144  if (!N || !N.buscarTop3) return;
2145
2146  // Copiado del bloque 2 con esta version. Si el nucleo cambia de version, el
2147  // parche se aparta: mejor que el pop-up reparta con el score viejo (el
2148  // servidor sigue filtrando) a que se quede congelado con una busqueda vieja.
2149  if (window.RE_RECOMENDADOR_VERSION !== "1.0.0") {
2150    console.warn("[recomendador] parche de reparto desactivado: el nucleo es la version", window.RE_RECOMENDADOR_VERSION, "y el parche se escribio para la 1.0.0. Cambia la RPC a buscar_entrenadores_reparto en buscarTop3 y borra este embed.");
2151    return;
2152  }
2153
2154  var estado = N.estado;
2155  var rest = N.rest;
2156
2157  N.buscarTop3 = function buscarTop3() {
2158    var intentos = [
2159      { objetivo: estado.objetivo, modalidad: estado.modalidad, ciudad: estado.ciudad },
2160      { objetivo: "", modalidad: estado.modalidad, ciudad: estado.ciudad },
2161      { objetivo: "", modalidad: "", ciudad: estado.ciudad },
2162      { objetivo: "", modalidad: estado.modalidad, ciudad: "" }
2163    ];
2164
2165    function intentar(i) {
2166      if (i >= intentos.length) return Promise.resolve([]);
2167      var f = intentos[i];
2168      return rest("/rest/v1/rpc/buscar_entrenadores_reparto", {
2169        method: "POST",
2170        body: {
2171          p_objetivo: f.objetivo || null,
2172          p_modalidad: f.modalidad || null,
2173          p_ciudad: f.ciudad || null,
2174          p_limite: 3,
2175          // Va dentro de la busqueda y no filtrando el resultado porque el
2176          // LIMIT ya se ha aplicado: quitar a uno aqui fuera dejaria dos, y un
2177          // lead repartido de menos es un entrenador que no recibe nada.
2178          p_solo_contactables: true
2179        }
2180      }).then(function (filas) {
2181        // Basta con uno: el siguiente intento no seria "mejor", seria menos
2182        // fiel a lo que ha pedido.
2183        if (filas && filas.length) return filas;
2184        return intentar(i + 1);
2185      });
2186    }
2187
2188    return intentar(0);
2189  };
2190
2191  window.RE_RECOMENDADOR_ESCAPARATE = "2026-09-12-1";
2192})();
2193</script>
2193</div><div class="w-embed w-script"><!-- RedEntrenadores - Parche del pop-up recomendador: no preguntar lo que ya nos han dicho -->
2194<!-- Va en el Footer V1, ENTRE el parche 2b (escaparate) y el bloque 3 (JS
2195     comportamiento), y tiene que seguir ahi: el bloque 3 se lleva `pasos`,
2196     `pasoActual` y las cuatro funciones de pantalla a variables locales nada
2197     mas cargar, asi que quien quiera cambiarlas tiene que hacerlo antes.
2198
2199     Que arregla. El buscador de la home (widget-home-hero-buscar.html) ya le ha
2200     sacado a la persona el objetivo, la modalidad, la ciudad y cuando quiere
2201     empezar. Sin esto, el pop-up se abria igualmente por la primera pantalla
2202     ("¿donde quieres entrenar?") a preguntarle lo que acababa de escribir.
2203
2204     Como se le dice lo que ya sabemos. Quien abra el pop-up con respuestas
2205     puestas marca ademas `estado.sabidos`:
2206
2207       estado.sabidos = { donde: true, objetivo: true, cuando: true }
2208
2209     Solo se salta lo que este marcado Y ademas tenga valor de verdad. "Donde"
2210     es la unica con truco: esa pantalla pregunta modalidad y, si es presencial,
2211     tambien ciudad, asi que saltarla exige tener las dos.
2212
2213     El paso "datos" no se salta nunca: es donde se piden nombre y email, que es
2214     lo unico que no se puede adivinar de una frase. Con las cuatro respuestas
2215     puestas, el pop-up abre directamente ahi.
2216
2217     Sin `estado.sabidos` la lista es exactamente la del nucleo (["donde",
2218     "objetivo", "cuando", "datos"], con "objetivo" fuera en /objetivo/*). Quien
2219     llega por el pop-up automatico de los 25 segundos no nota ningun cambio y
2220     el embudo que ya se esta midiendo sigue igual.
2221
2222     Por que hay que parchear CUATRO cosas y no solo `pasos`:
2223
2224       - `pasoActual` la calcula el nucleo con su `pasos` interna, no con la
2225         exportada. Sin parchearla, el bloque 3 pintaria la pantalla de la lista
2226         vieja: la lista diria "datos" y la pantalla seguiria siendo "donde".
2227       - Los puntos de progreso y el "Tres preguntas rapidas" tambien salen de
2228         esa `pasos` interna, y estan dentro de `cabeceraHTML`, que no se
2229         exporta. Se corrigen sobre el HTML ya generado, que es feo pero cabe
2230         aqui; la alternativa era reescribir los 30 KB del nucleo a mano.
2231       - El boton "Atras" de la pantalla de datos esta escrito a pelo (no pasa
2232         por `navHTML`), asi que cuando datos es la unica pantalla se queda un
2233         boton que no lleva a ningun sitio. Se quita cuando no hay nada detras.
2234
2235     Desde el 23/08/2026 corrige ademas las tres frases del nucleo que daban por
2236     hecho que el reparto siempre son tres entrenadores; ya no lo es.
2237
2238     Todo esto se cae solo el dia que el nucleo incorpore `sabidos`: sube su
2239     RE_RECOMENDADOR_VERSION y este embed se apaga entero. Mismo criterio y
2240     mismo guardia de version que el parche 2b. -->
2241<script>
2242(function () {
2243  "use strict";
2244
2245  var N = window.__reRecNucleo;
2246  if (!N || !N.pasos || !N.estado) return;
2247
2248  // Igual que en el parche 2b: si el nucleo cambia de version, este parche se
2249  // aparta y vuelve el recorrido completo. Preguntar de mas es recuperable;
2250  // saltarse una pantalla que ha cambiado de contenido, no.
2251  if (window.RE_RECOMENDADOR_VERSION !== "1.0.0") {
2252    console.warn("[recomendador] parche de pasos desactivado: el nucleo es la version", window.RE_RECOMENDADOR_VERSION, "y el parche se escribio para la 1.0.0. Metele `sabidos` a pasos() y borra este embed.");
2253    return;
2254  }
2255
2256  var estado = N.estado;
2257
2258  // La pantalla "donde" son dos preguntas en una. Saltarla solo vale si ninguna
2259  // de las dos queda en el aire: online no necesita ciudad, presencial si.
2260  function sabemosDonde() {
2261    if (!estado.modalidad) return false;
2262    if (estado.modalidad === "Online") return true;
2263    return !!estado.ciudad || !!estado.ciudadDePagina;
2264  }
2265
2266  N.pasos = function pasos() {
2267    var sabidos = estado.sabidos || {};
2268    var lista = [];
2269    if (!sabidos.donde || !sabemosDonde()) lista.push("donde");
2270    if (!estado.objetivoDePagina && !(sabidos.objetivo && estado.objetivo)) lista.push("objetivo");
2271    if (!(sabidos.cuando && estado.cuando)) lista.push("cuando");
2272    lista.push("datos");
2273    return lista;
2274  };
2275
2276  N.pasoActual = function pasoActual() {
2277    return N.pasos()[estado.indice];
2278  };
2279
2280  var PALABRAS = { 1: "Una", 2: "Dos", 3: "Tres" };
2281
2282  function dotsHTML() {
2283    var total = N.pasos().length;
2284    var html = "";
2285    for (var i = 0; i < total; i++) {
2286      html += '<span class="' + (i <= estado.indice ? "re-rec-hecho" : "") + '"></span>';
2287    }
2288    return '<div class="re-rec-dots">' + html + "</div>";
2289  }
2290
2291  // Cuantos entrenadores van a recibir la solicitud de verdad. En la primera
2292  // pintada de la pantalla de datos todavia no se sabe (la busqueda va en
2293  // camino), y ahi no se inventa un numero.
2294  function conQuien() {
2295    var n = (estado.top3 || []).length;
2296    if (n === 1) return "este entrenador";
2297    if (n) return "estos " + n + " entrenadores";
2298    return "los entrenadores que mejor encajen contigo";
2299  }
2300
2301  // Cirugia sobre el HTML que devuelve el nucleo. Los reemplazos son literales
2302  // suyos: si alguno deja de aparecer, no rompe nada (el replace no encuentra
2303  // y devuelve el mismo HTML), solo deja de corregir esa parte.
2304  function corregir(html) {
2305    html = html.replace(/<div class="re-rec-dots">[\s\S]*?<\/div>/, dotsHTML());
2306
2307    var preguntas = N.pasos().length - 1;   // la ultima pantalla no es pregunta
2308    html = html.replace(
2309      /(?:Una|Dos|Tres|Unas) preguntas rápidas/,
2310      preguntas === 1 ? "Una pregunta rápida" : (PALABRAS[preguntas] || "Unas") + " preguntas rápidas"
2311    );
2312
2313    // El reparto dejo de forzar tres (parche 2b, 23/08/2026): si en la ciudad
2314    // solo encaja uno, se le manda a uno. Estas tres frases del nucleo daban
2315    // por hecho que siempre eran tres, asi que la primera pantalla prometia
2316    // algo que no se iba a cumplir y el aviso legal decia con cuanta gente se
2317    // comparte el dato sin que fuera verdad.
2318    html = html.replace("con los 3 entrenadores", "con los entrenadores");
2319    html = html.replace("Enviaremos tu solicitud a los 3 entrenadores", "Enviaremos tu solicitud a los entrenadores");
2320    html = html.replace("con estos 3 entrenadores", "con " + conQuien());
2321
2322    if (estado.indice === 0) {
2323      html = html.replace('<button type="button" class="re-rec-atras" data-accion="atras">Atrás</button>', "");
2324    }
2325    return html;
2326  }
2327
2328  ["pasoDondeHTML", "pasoObjetivoHTML", "pasoCuandoHTML", "pasoDatosHTML"].forEach(function (nombre) {
2329    var original = N[nombre];
2330    if (typeof original !== "function") return;
2331    N[nombre] = function () { return corregir(original.apply(null, arguments)); };
2332  });
2333})();
2334</script>
2334</div><div class="w-embed w-script"><!-- ============================================================
2335     RedEntrenadores — Parche del pop-up recomendador: gancho manual en una ruta excluida
2336
2337     QUÉ ARREGLA: en /pregunta-al-experto el recomendador está en `RUTAS_EXCLUIDAS`, y
2338     el bloque 3 entiende "excluida" como "ni siquiera arranques": sale de `init()` antes
2339     de montar nada, así que allí no existe `window.reRecomendador` y no hay forma de
2340     ofrecerlo a mano. Lo queremos ofrecer justo después de enviar la pregunta, que es
2341     cuando quien pregunta tiene el problema fresco y toda la atención puesta.
2342
2343     QUÉ HACE: en esa ruta, y solo en esa,
2344
2345       1. saca `/pregunta-al-experto` de `RUTAS_EXCLUIDAS` para que el bloque 3 monte el
2346          pop-up y deje el gancho `window.reRecomendador.abrir()` disponible;
2347       2. le hace creer que ya salió en esta sesión, envolviendo `leer` para la clave de
2348          sesión. `puedeSalir()` la consulta antes de cada disparo por tiempo o scroll,
2349          así que el pop-up no sale por su cuenta; `abrir()` no la mira, así que el
2350          gancho manual sigue funcionando.
2351
2352     Quien lo abre es el pop-up de confirmación de esa landing:
2353     `widget-pregunta-al-experto-2-popup.html`.
2354
2355     POR QUÉ UN PARCHE Y NO TOCAR EL BLOQUE 3: aquel embed son 28 KB que habría que
2356     reproducir enteros para cambiarle dos líneas. Es el mismo motivo por el que ya
2357     existen `-escaparate` y `-saltar-sabidos`. El día que se reescriba el bloque 3,
2358     el cambio limpio es que `excluida()` apague solo los disparos automáticos y deje
2359     el resto del `init()` en pie; entonces este embed se borra.
2360
2361     DÓNDE VA: cuarto Embed del componente Footer V1, ENTRE el núcleo (bloque 2, del que
2362     lee `window.__reRecNucleo`) y el comportamiento (bloque 3, al que tiene que
2363     adelantarse). Junto a los otros dos parches, delante del bloque 3.
2364     ============================================================ -->
2365
2366<script>
2367(function () {
2368  var N = window.__reRecNucleo;
2369  if (!N) return;   // sin el núcleo no hay nada que parchear; el bloque 3 ya avisa por consola
2370
2371  // Guardia de versión, como los otros parches: si el núcleo cambia de versión, este
2372  // bloque se calla en vez de operar sobre algo que ya no es lo que espera.
2373  if (window.RE_RECOMENDADOR_VERSION !== "1.0.0") {
2374    console.warn("[re-recomendador] parche del gancho manual: version distinta, no se aplica");
2375    return;
2376  }
2377
2378  var RUTA = "/pregunta-al-experto";
2379  if (location.pathname.indexOf(RUTA) !== 0) return;
2380
2381  N.RUTAS_EXCLUIDAS = N.RUTAS_EXCLUIDAS.filter(function (r) { return r !== RUTA; });
2382
2383  // El bloque 3 copia `N.leer` a una variable local en cuanto arranca, así que esto
2384  // tiene que estar puesto antes que él. Solo miente sobre la clave de sesión: todo lo
2385  // demás (el apagado permanente, entre otros) sigue leyéndose de verdad.
2386  var leerOriginal = N.leer;
2387  N.leer = function (clave, almacen) {
2388    if (clave === N.CLAVE_SESION) return "1";
2389    return leerOriginal(clave, almacen);
2390  };
2391
2392  window.RE_RECOMENDADOR_GANCHO_MANUAL = true;
2393})();
2394</script>
2394
2395</div><div class="w-embed w-script">
2395<script>
2396(function () {
2397  "use strict";
2398
2399  // Segunda mitad del pop-up: cuando se abre, que pasa al pulsar y que se
2400  // mide. Depende del bloque anterior; si por lo que sea no ha cargado, aqui
2401  // no se hace nada en vez de reventar con un TypeError por cada pagina.
2402  var N = window.__reRecNucleo;
2403  if (!N) {
2404    console.error("[re-recomendador] falta el bloque 1, el pop-up no arranca");
2405    return;
2406  }
2407
2408  // Se sacan a variables locales para que el resto del bloque se lea igual
2409  // que cuando todo esto era un unico fichero.
2410  var RUTAS_EXCLUIDAS = N.RUTAS_EXCLUIDAS;
2411  var SEGUNDOS_ESPERA = N.SEGUNDOS_ESPERA;
2412  var SCROLL_DISPARO = N.SCROLL_DISPARO;
2413  var CLAVE_APAGADO = N.CLAVE_APAGADO;
2414  var CLAVE_SESION = N.CLAVE_SESION;
2415  var MODALIDADES = N.MODALIDADES;
2416  var OBJETIVOS = N.OBJETIVOS;
2417  var CUANDOS = N.CUANDOS;
2418  var estado = N.estado;
2419  var pasos = N.pasos;
2420  var pasoActual = N.pasoActual;
2421  var pideCiudad = N.pideCiudad;
2422  var leer = N.leer;
2423  var escribir = N.escribir;
2424  var ciudadDeLaPagina = N.ciudadDeLaPagina;
2425  var objetivoDeLaPagina = N.objetivoDeLaPagina;
2426  var rest = N.rest;
2427  var cargarCiudades = N.cargarCiudades;
2428  var buscarTop3 = N.buscarTop3;
2429  var pasoDondeHTML = N.pasoDondeHTML;
2430  var pasoObjetivoHTML = N.pasoObjetivoHTML;
2431  var pasoCuandoHTML = N.pasoCuandoHTML;
2432  var pasoDatosHTML = N.pasoDatosHTML;
2433  var subtituloDatosHTML = N.subtituloDatosHTML;
2434  var confirmacionHTML = N.confirmacionHTML;
2435  var cierreHTML = N.cierreHTML;
2436
2437  var overlay = null;
2438  var caja = null;
2439
2440  // ------------------------------------------------ alto real de la pantalla
2441
2442  // Lo que el CSS no puede arreglar solo: ni `100dvh` ni un `position: fixed`
2443  // se enteran de que ha subido el teclado. El modal se sigue dibujando contra
2444  // el viewport de siempre y el teclado le tapa la parte de abajo, que en el
2445  // paso de datos es justo donde están el último campo y el botón de enviar.
2446  // La única medida que sí encoge con el teclado es `visualViewport`, así que
2447  // mientras el pop-up está abierto el overlay se pega a ella. Hace falta
2448  // colocarlo además de medirlo (`top`/`left`) porque iOS no encoge el
2449  // viewport visible: lo desplaza.
2450  var vv = window.visualViewport || null;
2451  var ultimoEncaje = "";
2452
2453  function ajustarAlViewport() {
2454    if (!overlay || !vv) return;
2455    var encaje = [vv.height, vv.width, vv.offsetTop, vv.offsetLeft].join("|");
2456    if (encaje === ultimoEncaje) return;
2457    ultimoEncaje = encaje;
2458    // Con `top` + `height` y `left` + `width` puestos, el `inset: 0` del CSS
2459    // deja de mandar en el borde de abajo y en el de la derecha.
2460    overlay.style.top = vv.offsetTop + "px";
2461    overlay.style.left = vv.offsetLeft + "px";
2462    overlay.style.height = vv.height + "px";
2463    overlay.style.width = vv.width + "px";
2464    // Si estaba escribiendo, que su campo quede en el centro de lo que queda
2465    // visible. `center` y no `nearest`: `nearest` lo deja pegado al borde de
2466    // abajo, que es justo donde está la barra de botones pegada, y el campo
2467    // acaba detrás de ella.
2468    var foco = document.activeElement;
2469    if (foco && caja && foco !== caja && caja.contains(foco)) {
2470      try { foco.scrollIntoView({ block: "center" }); } catch (e) {}
2471    }
2472  }
2473
2474  function soltarViewport() {
2475    if (!overlay) return;
2476    ultimoEncaje = "";
2477    overlay.style.top = "";
2478    overlay.style.left = "";
2479    overlay.style.height = "";
2480    overlay.style.width = "";
2481  }
2482
2483  // ----------------------------------------------------------------- render
2484
2485  var VISTAS = {
2486    donde: pasoDondeHTML,
2487    objetivo: pasoObjetivoHTML,
2488    cuando: pasoCuandoHTML,
2489    datos: pasoDatosHTML
2490  };
2491
2492  var LISTAS = {
2493    donde: MODALIDADES,
2494    objetivo: OBJETIVOS,
2495    cuando: CUANDOS
2496  };
2497
2498  function pintar() {
2499    var html;
2500    if (estado.fase === "confirmacion") html = confirmacionHTML();
2501    else html = VISTAS[pasoActual()]();
2502
2503    caja.innerHTML = html;
2504    caja.classList.toggle("re-rec-ancho", estado.fase !== "preguntas");
2505    caja.scrollTop = 0;
2506
2507    if (document.getElementById("re-rec-ciudad")) cargarCiudades();
2508    if (estado.fase === "preguntas" && pasoActual() === "datos") precargarTop3();
2509    // El foco automático solo en escritorio: en móvil abre el teclado y tapa
2510    // media pantalla justo cuando el usuario aún está leyendo la pregunta.
2511    var primerInput = caja.querySelector("input");
2512    if (primerInput && window.innerWidth > 560) primerInput.focus();
2513
2514    medirPantalla();
2515  }
2516
2517  function claveBusqueda() {
2518    return [estado.ciudad, estado.objetivo, estado.modalidad].join("|");
2519  }
2520
2521  // Se busca a los tres al llegar a la pantalla de datos, no al enviarla, para
2522  // poder nombrarlos antes de pedir el email. El resultado se guarda con la
2523  // clave de las respuestas que lo produjeron: si vuelve atrás y cambia una,
2524  // la caché deja de valer sola y `enviar` no manda la solicitud a los tres
2525  // entrenadores de una búsqueda que ya no es la suya.
2526  function precargarTop3() {
2527    var clave = claveBusqueda();
2528    if (estado.top3Clave === clave) return;
2529    estado.top3Clave = clave;
2530    estado.top3 = [];
2531
2532    buscarTop3().then(function (top3) {
2533      if (estado.top3Clave !== clave) return;   // llegó tarde: ya cambió algo
2534      estado.top3 = top3;
2535      // Se reescribe solo el subtítulo en vez de repintar la pantalla: un
2536      // `pintar()` aquí borraría lo que el usuario ya estuviera escribiendo
2537      // en el nombre o el email.
2538      var sub = caja.querySelector(".re-rec-sub");
2539      if (sub && estado.fase === "preguntas" && pasoActual() === "datos") {
2540        sub.innerHTML = subtituloDatosHTML();
2541      }
2542    }).catch(function (err) {
2543      console.warn("[re-recomendador] no se pudo precargar el reparto", err);
2544      estado.top3Clave = null;   // que `enviar` lo reintente
2545    });
2546  }
2547
2548  function avanzar() {
2549    estado.error = "";
2550    estado.indice += 1;
2551    pintar();
2552  }
2553
2554  function elegirOpcion(valor) {
2555    var paso = pasoActual();
2556    var lista = LISTAS[paso];
2557    if (!lista) return;
2558    var op = lista.find(function (o) { return o.value === valor; });
2559    if (!op) return;
2560    guardarCampos();
2561
2562    if (paso === "donde") {
2563      estado.modalidad = op.value;
2564      medir("recomendador_respuesta", { paso: "modalidad", valor: op.value });
2565      // Elegir modalidad avanza solo, salvo cuando eso destapa el campo de
2566      // ciudad: ahí hay que quedarse en la pantalla para que lo rellene.
2567      if (pideCiudad()) { estado.error = ""; return pintar(); }
2568    } else if (paso === "objetivo") {
2569      estado.objetivo = op.value;
2570      estado.objetivoRespondido = true;
2571      // El objetivo vacío es la opción "Aún no lo tengo claro", no un fallo:
2572      // se le pone nombre para poder contarla como una respuesta más.
2573      medir("recomendador_respuesta", { paso: "objetivo", valor: op.value || "Aun no lo tengo claro" });
2574    } else if (paso === "cuando") {
2575      estado.cuando = op.value;
2576      medir("recomendador_respuesta", { paso: "cuando", valor: op.value });
2577    }
2578
2579    avanzar();
2580  }
2581
2582  function validarYAvanzar() {
2583    var input = document.getElementById("re-rec-ciudad");
2584    if (!input) return;
2585    var ciudad = input.value.trim();
2586    if (!ciudad) return mostrarError("Dinos en qué ciudad buscas.");
2587    estado.ciudad = ciudad;
2588    // La ciudad va tal cual la escribe: saber que piden Albacete y que ahí no
2589    // hay nadie vale más que el propio embudo.
2590    medir("recomendador_respuesta", { paso: "ciudad", valor: ciudad });
2591    avanzar();
2592  }
2593
2594  function mostrarError(texto) {
2595    estado.error = texto;
2596    var el = caja.querySelector(".re-rec-error");
2597    if (el) { el.textContent = texto; el.style.display = "block"; }
2598  }
2599
2600  // Cualquier repintado tira el contenido de los campos, así que antes de
2601  // movernos se guarda lo que haya escrito. Sin esto, quien vuelve atrás a
2602  // cambiar el objetivo se encuentra el nombre y el email en blanco al
2603  // regresar, y eso es un abandono servido en bandeja.
2604  function guardarCampos() {
2605    var campos = { nombre: "re-rec-nombre", email: "re-rec-email", telefono: "re-rec-telefono", ciudad: "re-rec-ciudad" };
2606    Object.keys(campos).forEach(function (clave) {
2607      var el = document.getElementById(campos[clave]);
2608      if (el) estado[clave] = el.value.trim();
2609    });
2610  }
2611
2612  function retroceder() {
2613    guardarCampos();
2614    medir("recomendador_atras", { desde: pasoActual() });
2615    estado.error = "";
2616    if (estado.indice > 0) estado.indice -= 1;
2617    pintar();
2618  }
2619
2620  // ----------------------------------------------------------------- envío
2621
2622  // re-validar: copia inline de `_shared/validacion-contacto.ts`. Los embeds de
2623  // Webflow son sueltos y no pueden importar nada, así que cuando se toque
2624  // aquel fichero hay que tocar también esto. Deja el número en E.164 o
2625  // devuelve null: 9 dígitos se asumen españoles, 00 pasa a +, y lo que no
2626  // encaje se rechaza en vez de guardarse en crudo.
2627  function reNormalizarTelefono(valor) {
2628    if (typeof valor !== "string") return null;
2629    var limpio = valor.replace(/[\s\-.()]/g, "");
2630    if (!limpio) return null;
2631    var candidato;
2632    if (limpio.charAt(0) === "+") candidato = limpio;
2633    else if (limpio.slice(0, 2) === "00") candidato = "+" + limpio.slice(2);
2634    else if (/^\d{9}$/.test(limpio)) candidato = "+34" + limpio;
2635    else return null;
2636    return /^\+\d{6,15}$/.test(candidato) ? candidato : null;
2637  }
2638
2639  function enviar() {
2640    var nombre = (document.getElementById("re-rec-nombre").value || "").trim();
2641    var email = (document.getElementById("re-rec-email").value || "").trim();
2642    var telefono = (document.getElementById("re-rec-telefono").value || "").trim();
2643
2644    estado.nombre = nombre;
2645    estado.email = email;
2646    estado.telefono = telefono;
2647
2648    if (!nombre) {
2649      medir("recomendador_datos_invalidos", { campo: "nombre" });
2650      return mostrarError("Introduce tu nombre.");
2651    }
2652    // El email es el campo obligatorio desde que existe el chat: es lo que
2653    // permite avisar de un mensaje y lo que abre la cuenta.
2654    if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
2655      medir("recomendador_datos_invalidos", { campo: "email" });
2656      return mostrarError(email
2657        ? "Revisa el email, parece que falta algo."
2658        : "Introduce tu email para que puedan escribirte.");
2659    }
2660    // El teléfono es opcional, pero si lo escribe tiene que servir para llamar.
2661    // Se manda ya normalizado para que el backend guarde la misma forma.
2662    var telefonoE164 = telefono ? reNormalizarTelefono(telefono) : null;
2663    if (telefono && !telefonoE164) {
2664      medir("recomendador_datos_invalidos", { campo: "telefono" });
2665      return mostrarError("Revisa el teléfono: 9 dígitos, o con prefijo internacional (+34…).");
2666    }
2667
2668    estado.enviando = true;
2669    estado.error = "";
2670    pintar();
2671
2672    // Si la precarga de esta misma búsqueda ya trajo el reparto, se manda a
2673    // esos: son exactamente los que se le acaban de nombrar en pantalla.
2674    var yaEstan = estado.top3.length && estado.top3Clave === claveBusqueda();
2675    (yaEstan ? Promise.resolve(estado.top3) : buscarTop3()).then(function (top3) {
2676      if (!top3.length) throw new Error("sin_entrenadores");
2677      estado.top3 = top3;
2678
2679      return rest("/functions/v1/solicitar-contacto-entrenador", {
2680        method: "POST",
2681        body: {
2682          entrenador_slugs: top3.map(function (e) { return e.slug; }),
2683          nombre: nombre,
2684          email: email,
2685          telefono: telefonoE164,
2686          zona: estado.ciudad || null,
2687          objetivo: estado.objetivo || null,
2688          modalidad: estado.modalidad || null,
2689          cuando_empieza: estado.cuando || null,
2690          tipo_solicitud: "multiple",
2691          origen: "popup-recomendador"
2692        }
2693      });
2694    }).then(function (datos) {
2695      escribir(CLAVE_APAGADO, "enviado", localStorage);
2696      estado.claimToken = (datos && datos.claim_token) || null;
2697      estado.enviando = false;
2698      estado.fase = "confirmacion";
2699      pintar();
2700      medir("recomendador_solicitud_enviada", {
2701        entrenadores: estado.top3.length,
2702        ciudad: estado.ciudad || null,
2703        objetivo: estado.objetivo || "Aun no lo tengo claro",
2704        modalidad: estado.modalidad || null,
2705        cuando: estado.cuando || null,
2706        // El email es obligatorio, así que no se mide: sería siempre true. El
2707        // teléfono sí, que ahora es el opcional y su tasa es justo lo que dice
2708        // si el cambio ha costado el dato con el que el entrenador llama.
2709        con_telefono: !!telefonoE164,
2710        segundos: estado.abiertoEn ? Math.round((Date.now() - estado.abiertoEn) / 1000) : null
2711      });
2712    }).catch(function (err) {
2713      console.error("[re-recomendador] envío", err);
2714      // "sin_entrenadores" no es una avería: es demanda en una ciudad sin
2715      // cobertura. Separarlo del fallo de red es la diferencia entre arreglar
2716      // el widget y salir a captar entrenadores en esa ciudad.
2717      medir("recomendador_envio_fallido", {
2718        motivo: err.message === "sin_entrenadores" ? "sin_entrenadores" : "error",
2719        ciudad: estado.ciudad || null
2720      });
2721      estado.enviando = false;
2722      estado.error = err.message === "sin_entrenadores"
2723        ? "No hemos encontrado entrenadores para esa búsqueda. Prueba con otra ciudad."
2724        : "No hemos podido enviar tu solicitud. Inténtalo de nuevo en un momento.";
2725      pintar();
2726    });
2727  }
2728
2729  function guardarPerfil() {
2730    var btn = caja.querySelector('[data-accion="guardar-perfil"]');
2731    var errEl = document.getElementById("re-rec-p2-error");
2732    var numero = function (id) {
2733      var v = (document.getElementById(id).value || "").trim();
2734      return v === "" ? null : Number(v);
2735    };
2736
2737    btn.disabled = true;
2738    btn.textContent = "Guardando…";
2739    if (errEl) errEl.style.display = "none";
2740
2741    rest("/functions/v1/completar-perfil-usuario", {
2742      method: "POST",
2743      body: {
2744        claim_token: estado.claimToken,
2745        // El email ya viajó con la solicitud en el paso 1. Mandarlo otra vez
2746        // no aportaría nada: el RPC respeta el que la ficha ya tiene.
2747        email: null,
2748        objetivo: estado.objetivo || null,
2749        modalidad: estado.modalidad || null,
2750        zona: estado.ciudad || null,
2751        edad: numero("re-rec-edad"),
2752        peso_kg: numero("re-rec-peso"),
2753        altura_cm: numero("re-rec-altura"),
2754        disponibilidad: (document.getElementById("re-rec-disponibilidad").value || "").trim() || null,
2755        nivel_actividad: document.getElementById("re-rec-actividad").value || null,
2756        comentarios: (document.getElementById("re-rec-notas").value || "").trim() || null
2757      }
2758    }).then(function (datos) {
2759      // La cuenta se ofrece porque la ficha tiene email: lo dio en el paso 1 y
2760      // la solicitud se creó con él. Ya no se mira `email_guardado`, que desde
2761      // este cambio viene siempre a false —significa "no he escrito nada nuevo
2762      // porque la ficha ya tenía email", que es exactamente lo que pasa ahora—
2763      // y usarlo escondía la oferta de cuenta a todo el mundo.
2764      var ofreceCuenta = !!estado.email;
2765      medir("recomendador_perfil_guardado", {});
2766      caja.innerHTML = cierreHTML(true, ofreceCuenta);
2767    }).catch(function (err) {
2768      console.error("[re-recomendador] paso 2", err);
2769      if (errEl) {
2770        errEl.textContent = "No hemos podido guardarlo. Tu solicitud sí se envió, no te preocupes.";
2771        errEl.style.display = "block";
2772      }
2773      btn.disabled = false;
2774      btn.textContent = "Enviar mis datos";
2775    });
2776  }
2777
2778  function crearCuenta() {
2779    var btn = caja.querySelector('[data-accion="crear-cuenta"]');
2780    var errEl = document.getElementById("re-rec-cuenta-error");
2781    btn.disabled = true;
2782    btn.textContent = "Creando…";
2783    if (errEl) errEl.style.display = "none";
2784
2785    rest("/functions/v1/crear-cuenta-usuario", {
2786      method: "POST",
2787      body: { claim_token: estado.claimToken }
2788    }).then(function (datos) {
2789      medir("recomendador_cuenta_creada", { requiere_email: !!datos.requiere_email });
2790      // Cuenta nueva: se entra en el acto. Si ya existía, la función manda el
2791      // enlace al buzón, que ahí es lo único que prueba que el email es suyo.
2792      if (datos.requiere_email) {
2793        btn.parentElement.innerHTML =
2794          "<h4>Ya tienes cuenta</h4>" +
2795          '<p class="re-rec-sub" style="font-size:14px;margin:4px 0 0">Te hemos mandado un enlace a tu email para entrar y ver esta solicitud junto a las anteriores.</p>';
2796      } else {
2797        btn.textContent = "Entrando…";
2798        window.location.href = datos.url;
2799      }
2800    }).catch(function (err) {
2801      console.error("[re-recomendador] crear cuenta", err);
2802      if (errEl) {
2803        errEl.textContent = "No hemos podido crear la cuenta. Tu solicitud sí se envió; inténtalo de nuevo en un momento.";
2804        errEl.style.display = "block";
2805      }
2806      btn.disabled = false;
2807      btn.textContent = "Crear mi cuenta";
2808    });
2809  }
2810
2811  // Amplitude no envía nada hasta que cookieyes acepte analytics; hasta
2812  // entonces los eventos se acumulan en su cola. Nunca se cuelga una conversión de
2813  // Ads de este pop-up: el activador de GTM de las landings de paid va por el
2814  // selector `.re-modal-success`, y por eso aquí no se usa esa clase.
2815  //
2816  // No hace falta encolar nada aquí: el bloque de Amplitude del head del sitio
2817  // deja un stub `{ _q: [], track }` desde el primer momento y vacía esa cola
2818  // cuando el módulo arranca, así que un `track` disparado antes del
2819  // consentimiento no se pierde. Si el consentimiento no llega nunca, la cola
2820  // se descarta sin enviarse, que es justo lo que debe pasar.
2821  function medir(evento, props) {
2822    if (!estado.midiendo) return;
2823    var datos = props || {};
2824    // Segmentar por página es lo que distingue "el pop-up funciona" de "el
2825    // pop-up funciona en las páginas de ciudad y en la home no".
2826    datos.pagina = location.pathname;
2827    try {
2828      if (window.amplitude && window.amplitude.track) window.amplitude.track(evento, datos);
2829    } catch (e) {}
2830  }
2831
2832  // Una misma pantalla se repinta varias veces —al destapar "Ver más", al
2833  // abrirse el campo de ciudad, al enseñar un error— y ninguno de esos
2834  // repintados es una pantalla nueva. Contando solo la primera, el embudo mide
2835  // personas y no renders.
2836  function medirPantalla() {
2837    var lista = pasos();
2838    var clave = estado.fase === "confirmacion" ? "confirmacion" : pasoActual();
2839    if (estado.vistas[clave]) return;
2840    estado.vistas[clave] = true;
2841    medir("recomendador_paso_visto", {
2842      paso: clave,
2843      numero: estado.fase === "confirmacion" ? lista.length + 1 : estado.indice + 1,
2844      total: lista.length
2845    });
2846  }
2847
2848  // ------------------------------------------------------------ abrir/cerrar
2849
2850  function abrir(motivo) {
2851    if (!overlay || overlay.classList.contains("re-rec-abierto")) return;
2852    escribir(CLAVE_SESION, "1", sessionStorage);
2853    estado.indice = 0;
2854    estado.fase = "preguntas";
2855    estado.vistas = {};
2856    estado.abiertoEn = Date.now();
2857    estado.midiendo = motivo !== "preview" && motivo !== "debug";
2858    // Va antes de `pintar()` para que la apertura quede por delante del primer
2859    // paso en el orden de eventos, y no detrás.
2860    medir("recomendador_popup_abierto", {
2861      motivo: motivo || "manual",
2862      ciudad_de_pagina: estado.ciudadDePagina,
2863      objetivo_de_pagina: estado.objetivoDePagina,
2864      pasos: pasos().length
2865    });
2866    pintar();
2867    overlay.classList.add("re-rec-abierto");
2868    document.body.style.overflow = "hidden";
2869    ajustarAlViewport();
2870    if (vv) {
2871      vv.addEventListener("resize", ajustarAlViewport);
2872      vv.addEventListener("scroll", ajustarAlViewport);
2873    }
2874  }
2875
2876  function cerrar() {
2877    if (!overlay) return;
2878    var estabaAbierto = overlay.classList.contains("re-rec-abierto");
2879    overlay.classList.remove("re-rec-abierto");
2880    document.body.style.overflow = "";
2881    if (vv) {
2882      vv.removeEventListener("resize", ajustarAlViewport);
2883      vv.removeEventListener("scroll", ajustarAlViewport);
2884    }
2885    soltarViewport();
2886    // El evento más útil de todos: dice en qué pantalla exacta se cae la gente.
2887    if (estabaAbierto) {
2888      medir("recomendador_cerrado", {
2889        paso: estado.fase === "preguntas" ? pasoActual() : estado.fase,
2890        habia_enviado: estado.fase !== "preguntas",
2891        segundos: estado.abiertoEn ? Math.round((Date.now() - estado.abiertoEn) / 1000) : null
2892      });
2893    }
2894    // Solo se apaga para siempre si ya había enviado; cerrar en la primera
2895    // pregunta no debería castigarle a no volver a verlo nunca en el sitio.
2896    if (estado.fase !== "preguntas") escribir(CLAVE_APAGADO, "enviado", localStorage);
2897  }
2898
2899  // Con otro modal abierto no se dispara: en la ficha del entrenador convive
2900  // con el pop-up de contacto ("Nuevo contacto v2"), y dos modales apilados
2901  // es la forma más rápida de perder el lead que ya estaba escribiendo.
2902  function hayOtroModalAbierto() {
2903    // El banner de cookies entra en la lista: a un visitante nuevo le sale
2904    // CookieYes nada más entrar, y taparlo con este pop-up es a la vez una
2905    // mala experiencia y un problema de consentimiento.
2906    var candidatos = [
2907      ".pop-up-contacto",
2908      "#re-overlay.re-open-modal-overlay",
2909      ".re-modal-overlay",
2910      "[data-re-modal-abierto]",
2911      ".cky-consent-container",
2912      ".cky-modal"
2913    ];
2914    return candidatos.some(function (sel) {
2915      var el = document.querySelector(sel);
2916      if (!el) return false;
2917      // No basta con mirar `display`: los pop-ups de Webflow los abre y cierra
2918      // IX2, que muchas veces deja el elemento en el flujo y solo le anima la
2919      // opacidad. Dando por abierto todo lo que no sea `display:none`, este
2920      // pop-up no volvería a salir nunca en las 95 fichas de entrenador — y
2921      // sin un error en consola que lo delate. Se mide lo que de verdad se ve.
2922      var estilo = window.getComputedStyle(el);
2923      if (estilo.display === "none" || estilo.visibility === "hidden") return false;
2924      if (Number(estilo.opacity) === 0) return false;
2925      var caja = el.getBoundingClientRect();
2926      return caja.width > 0 && caja.height > 0;
2927    });
2928  }
2929
2930  function puedeSalir() {
2931    if (leer(CLAVE_APAGADO, localStorage)) return false;
2932    if (leer(CLAVE_SESION, sessionStorage)) return false;
2933    if (hayOtroModalAbierto()) return false;
2934    // Si está escribiendo en cualquier formulario de la página, no se le
2935    // interrumpe: quien ya está rellenando algo no necesita que le recomienden nada.
2936    var activo = document.activeElement;
2937    if (activo && /^(INPUT|TEXTAREA|SELECT)$/.test(activo.tagName)) return false;
2938    return true;
2939  }
2940
2941  // ------------------------------------------------------------------ init
2942
2943  function excluida() {
2944    var ruta = location.pathname;
2945    return RUTAS_EXCLUIDAS.some(function (r) { return ruta.indexOf(r) === 0; });
2946  }
2947
2948  function init() {
2949    if (excluida()) return;
2950
2951    overlay = document.createElement("div");
2952    overlay.id = "re-rec-overlay";
2953    overlay.innerHTML = '<div class="re-rec-modal" role="dialog" aria-modal="true" aria-label="Te ayudamos a decidir"></div>';
2954    document.body.appendChild(overlay);
2955    caja = overlay.querySelector(".re-rec-modal");
2956
2957    estado.ciudad = ciudadDeLaPagina();
2958    estado.ciudadDePagina = !!estado.ciudad;
2959    var obj = objetivoDeLaPagina();
2960    if (obj) { estado.objetivo = obj; estado.objetivoDePagina = true; }
2961
2962    overlay.addEventListener("click", function (ev) {
2963      if (ev.target === overlay) return cerrar();
2964      var btn = ev.target.closest("[data-accion]");
2965      if (!btn) return;
2966      var accion = btn.getAttribute("data-accion");
2967      if (accion === "cerrar") cerrar();
2968      else if (accion === "opcion") elegirOpcion(btn.getAttribute("data-valor"));
2969      else if (accion === "ver-mas") {
2970        // Si mucha gente lo pulsa, las opciones visibles que fija `VISIBLES` no
2971        // son las que cubren a la gente que llega de verdad.
2972        medir("recomendador_ver_mas", { paso: pasoActual() });
2973        estado.abiertas[pasoActual()] = true;
2974        pintar();
2975      }
2976      else if (accion === "siguiente") validarYAvanzar();
2977      else if (accion === "atras") retroceder();
2978      else if (accion === "enviar") enviar();
2979      else if (accion === "guardar-perfil") guardarPerfil();
2980      else if (accion === "crear-cuenta") crearCuenta();
2981    });
2982
2983    document.addEventListener("keydown", function (ev) {
2984      if (ev.key === "Escape" && overlay.classList.contains("re-rec-abierto")) cerrar
2984();
2985    });
2986
2987    // Disparo por tiempo o por scroll, lo que llegue antes.
2988    var disparado = false;
2989    var entrada = Date.now();
2990
2991    function disparar(motivo) {
2992      if (disparado) return false;
2993      if (!puedeSalir()) return false;
2994      disparado = true;
2995      abrir(motivo);
2996      return true;
2997    }
2998
2999    // 30/08/2026: sale nada más entrar, no a los 25 segundos. Lo único que se
3000    // espera es a que CookieYes pinte y despache su banner —`puedeSalir` ya lo
3001    // cuenta como modal abierto, pero tarda unas décimas en aparecer y sin esa
3002    // espera mínima el pop-up se le adelantaba y se quedaba debajo—, así que la
3003    // espera baja de 25 segundos a segundo y medio.
3004    //
3005    // El disparo se reintenta en vez de perderse: en el primer intento puede
3006    // haber un motivo temporal para no salir (el banner de cookies sin
3007    // responder, otro modal abierto, el usuario escribiendo en un campo) y con
3008    // un `setTimeout` suelto esa única oportunidad se gastaba ahí. El sondeo
3009    // baja a 400 ms —antes 5 s— para que en cuanto se aceptan las cookies el
3010    // pop-up salga acto seguido y no hasta cinco segundos después.
3011    //
3012    // `SEGUNDOS_ESPERA` del bloque 2 se queda sin usar a propósito: reescribir
3013    // aquel embed de 34 KB solo para bajar una constante no compensa.
3014    var ESPERA_MS = 1500;
3015    var reintento = setInterval(function () {
3016      var transcurrido = Date.now() - entrada;
3017      if (disparado || transcurrido > 180000) return clearInterval(reintento);
3018      if (transcurrido >= ESPERA_MS) disparar("tiempo");
3019    }, 400);
3020
3021    window.addEventListener("scroll", function () {
3022      var alto = document.documentElement.scrollHeight - window.innerHeight;
3023      if (alto <= 0) return;
3024      if (window.scrollY / alto >= SCROLL_DISPARO) disparar("scroll");
3025    }, { passive: true });
3026
3027    // Gancho manual: para un botón "¿No lo tienes claro?" en cualquier sitio
3028    // de la página, y para poder probarlo sin esperar al disparo automático.
3029    window.reRecomendador = {
3030      abrir: function () { abrir("manual"); },
3031      cerrar: cerrar,
3032      reset: function () {
3033        try { localStorage.removeItem(CLAVE_APAGADO); sessionStorage.removeItem(CLAVE_SESION); } catch (e) {}
3034      },
3035      // Pinta la confirmación con el reparto real de una búsqueda, pero SIN
3036      // enviar nada: sirve para revisar el copy y las fichas sin avisar a tres
3037      // entrenadores de verdad cada vez que se quiere mirar esa pantalla.
3038      // `tokenFalso` sirve para ver el bloque de perfil y el de crear cuenta,
3039      // que solo se pintan cuando hay `claim_token`. Guardar desde ahí falla
3040      // (el token no existe), que es justo lo que debe pasar.
3041      previsualizarConfirmacion: function (ciudad, objetivo, modalidad, tokenFalso) {
3042        estado.ciudad = ciudad || estado.ciudad;
3043        estado.objetivo = objetivo || "";
3044        estado.modalidad = modalidad || "En un gimnasio";
3045        return buscarTop3().then(function (top3) {
3046          // abrir() reinicia el estado al primer paso, así que va antes de
3047          // dejar la fase en "confirmacion" y no después.
3048          abrir("preview");
3049          estado.top3 = top3;
3050          estado.claimToken = tokenFalso ? "token-de-prueba" : null;
3051          estado.fase = "confirmacion";
3052          pintar();
3053          return top3.map(function (e) { return e.slug; });
3054        });
3055      }
3056    };
3057
3058    if (new URLSearchParams(location.search).get("recomendador") === "1") abrir("debug");
3059  }
3060
3061  // Si en consola hay VERSION pero no LISTO, es que falta el segundo embed.
3062  window.RE_RECOMENDADOR_LISTO = true;
3063
3064  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init);
3065  else init();
3066})();
3067</script>
3067
3068</div><div class="w-embed w-script"><!-- RedEntrenadores - Origen de campana (embed en el componente Footer V1) -->
3069<!-- Va en el Footer V1 porque tiene que correr en las 115 paginas: el custom
3070     code de sitio haria lo mismo, pero ese campo son 20 KB y reescribirlo
3071     entero por API es justo el tamano que se corta a medias.
3072
3073     El problema que resuelve: cada formulario leia `document.referrer` en el
3074     momento de enviarse, y para entonces ya era interno. Quien llegaba por un
3075     enlace de campana, aterrizaba en una landing y contactaba desde la ficha
3076     de un entrenador nacia en `usuarios` sin origen ninguno. Y las utm de la
3077     URL de llegada se perdian en el primer salto.
3078
3079     Hace tres cosas:
3080     1. Guarda la primera llegada de la sesion y la sirve en `window.rtOrigen()`.
3081     2. Envuelve `fetch` para que toda solicitud a `solicitar-contacto-entrenador`
3082        lleve el origen. Asi el formulario de la ficha (22 KB) y el de la landing
3083        de paid quedan cubiertos sin tocarles una linea.
3084     3. Arrastra las utm a los enlaces que apuntan a /encuentra-entrenador, que
3085        es el unico sitio del que el wizard las lee.
3086
3087     Por que (3) en vez de envolver tambien la RPC del wizard: supabase-js
3088     resuelve `fetch` al construir el cliente, y ese cliente nace al parsearse
3089     su embed, antes que este. La envoltura no le llegaria.
3090
3091     Fuente versionada: widgets/widget-origen-campana.html -->
3092<script>
3093(function () {
3094  var CLAVE = 're-origen';
3095  var ENDPOINT_CONTACTO = '/functions/v1/solicitar-contacto-entrenador';
3096  var DESTINO_WIZARD = '/encuentra-entrenador';
3097
3098  // Copia en memoria: en navegacion privada o con el almacenamiento capado,
3099  // sessionStorage lanza al escribir. Sin esto el origen se perderia en el
3100  // primer salto interno justo en los navegadores mas restrictivos.
3101  var enMemoria = null;
3102
3103  function leer() {
3104    try {
3105      var crudo = sessionStorage.getItem(CLAVE);
3106      if (crudo) return JSON.parse(crudo);
3107    } catch (e) { /* almacenamiento capado: tiramos de la copia en memoria */ }
3108    return enMemoria;
3109  }
3110
3111  function guardar(o) {
3112    enMemoria = o;
3113    try { sessionStorage.setItem(CLAVE, JSON.stringify(o)); } catch (e) {}
3114  }
3115
3116  function referenteExterno() {
3117    try {
3118      if (!document.referrer) return null;
3119      var r = new URL(document.referrer);
3120      if (r.hostname === location.hostname) return null;
3121      // Sin query: el referente de un buscador lleva la consulta del usuario.
3122      return r.origin + r.pathname;
3123    } catch (e) { return null; }
3124  }
3125
3126  var params = new URLSearchParams(location.search);
3127  var llegada = {
3128    pagina: location.pathname,
3129    referrer: referenteExterno(),
3130    utm_source: params.get('utm_source'),
3131    utm_medium: params.get('utm_medium'),
3132    utm_campaign: params.get('utm_campaign')
3133  };
3134
3135  // Manda el primer toque de la sesion, con una excepcion: una llegada nueva
3136  // CON utm_source la pisa. Asi quien vuelve desde un enlace de campana se
3137  // atribuye a esa campana y no al referente con el que abrio el navegador
3138  // hace media hora.
3139  if (!leer() || llegada.utm_source) guardar(llegada);
3140
3141  // Devuelve siempre las cinco claves: quien lo consume puede volcarlo en el
3142  // payload sin comprobar nada.
3143  window.rtOrigen = function () {
3144    var o = leer() || {};
3145    return {
3146      pagina: o.pagina || location.pathname,
3147      referrer: o.referrer || null,
3148      utm_source: o.utm_source || null,
3149      utm_medium: o.utm_medium || null,
3150      utm_campaign: o.utm_campaign || null
3151    };
3152  };
3153
3154  // 2. El origen viaja con cada solicitud de contacto
3155  //
3156  // Envolver `fetch` en vez de editar los formularios: son embeds de 22 y 40
3157  // KB y reescribirlos por API no cabe. La envoltura es defensiva de arriba
3158  // abajo: cualquier fallo cae al fetch original con los argumentos tal cual,
3159  // porque aqui pasa el envio del formulario de contacto y romperlo significa
3160  // perder leads.
3161  var fetchOriginal = window.fetch;
3162
3163  if (typeof fetchOriginal === 'function') {
3164    window.fetch = function (entrada, opciones) {
3165      try {
3166        var url = typeof entrada === 'string' ? entrada : (entrada && entrada.url) || '';
3167        if (url.indexOf(ENDPOINT_CONTACTO) !== -1 && opciones && typeof opciones.body === 'string') {
3168          var cuerpo = JSON.parse(opciones.body);
3169          if (cuerpo && typeof cuerpo === 'object' && !Array.isArray(cuerpo)) {
3170            var o = window.rtOrigen();
3171            // Solo se rellena lo que falta: un formulario que ya mande su
3172            // origen manda el.
3173            if (!cuerpo.pagina_origen) cuerpo.pagina_origen = location.pathname;
3174            if (!cuerpo.referrer_externo && o.referrer) cuerpo.referrer_externo = o.referrer;
3175            if (!cuerpo.utm_source && o.utm_source) cuerpo.utm_source = o.utm_source;
3176            if (!cuerpo.utm_medium && o.utm_medium) cuerpo.utm_medium = o.utm_medium;
3177            if (!cuerpo.utm_campaign && o.utm_campaign) cuerpo.utm_campaign = o.utm_campaign;
3178
3179            var copia = {};
3180            for (var k in opciones) { if (Object.prototype.hasOwnProperty.call(opciones, k)) copia[k] = opciones[k]; }
3181            copia.body = JSON.stringify(cuerpo);
3182            return fetchOriginal.call(this, entrada, copia);
3183          }
3184        }
3185      } catch (e) { /* cuerpo ilegible o lo que sea: va el original */ }
3186      return fetchOriginal.apply(this, arguments);
3187    };
3188  }
3189
3190  // 3. Las utm llegan al wizard
3191  //
3192  // El wizard de /encuentra-entrenador lee las utm de SU url. Quien llega
3193  // desde una landing interna las habria perdido por el camino, asi que se
3194  // las pegamos a los enlaces que apuntan alli. Solo a esos: llenar de utm
3195  // todos los enlaces del sitio ensuciaria las URLs indexables y contaria
3196  // cada pagina interna como una visita de campana.
3197  function propagarAlWizard() {
3198    var o = window.rtOrigen();
3199    if (!o.utm_source && !o.utm_medium && !o.utm_campaign) return;
3200
3201    var enlaces = document.querySelectorAll('a[href*="' + DESTINO_WIZARD + '"]');
3202    Array.prototype.forEach.call(enlaces, function (a) {
3203      try {
3204        if (a.getAttribute('data-re-origen')) return;
3205        var u = new URL(a.href, location.origin);
3206        if (u.hostname !== location.hostname) return;
3207        if (u.pathname.indexOf(DESTINO_WIZARD) !== 0) return;
3208        if (o.utm_source && !u.searchParams.get('utm_source')) u.searchParams.set('utm_source', o.utm_source);
3209        if (o.utm_medium && !u.searchParams.get('utm_medium')) u.searchParams.set('utm_medium', o.utm_medium);
3210        if (o.utm_campaign && !u.searchParams.get('utm_campaign')) u.searchParams.set('utm_campaign', o.utm_campaign);
3211        a.setAttribute('href', u.pathname + u.search + u.hash);
3212        a.setAttribute('data-re-origen', '1');
3213      } catch (e) {}
3214    });
3215  }
3216
3217  if (document.readyState === 'loading') {
3218    document.addEventListener('DOMContentLoaded', propagarAlWizard);
3219  } else {
3220    propagarAlWizard();
3221  }
3222  // Los listados y el pop-up recomendador pintan enlaces despues de cargar.
3223  setTimeout(propagarAlWizard, 1500);
3224})();
3225</script>
3225</div></footer>
3225<script src="https://d3e54v103j8qbb.cloudfront.net/js/jquery-3.5.1.min.dc5e7f18c8.js?site=68c5a729c71fa3f37858fbbe" type="text/javascript" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
3225<script src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe/js/webflow.975777c1.283252e848e5c0e3.js" type="text/javascript" integrity="sha384-Zo/MMcWLLsMN+SlQPBtgBQDPWtbRCmp8ijx9WnkKyzy4q4eYV7FVR0+rAXA6bZmH" crossorigin="anonymous"></script>
3225<!-- Google Tag Manager (noscript) -->
vendor: 68 bytes, lines 3225-3226
3225
3226<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
3226GTM-KBD83QKS
vendor: 83 bytes, lines 3226-3227
3226"
3227height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
3228<!-- End Google Tag Manager (noscript) -->
3229
3230<!-- ══════════════════════════════════════════════════════════
3231     PREGUNTA AL EXPERTO — filtro por categoria + contador
3232     ══════════════════════════════════════════════════════════ -->
3233<style>
3234  .re-cats__link.is-active { background:#089F3B !important; border-color:#089F3B !important; color:#fff !important; }
3235  .re-cats__link.is-active [class*="re-cats__icon-"] { filter: brightness(0) invert(1); }
3236  .re-cats__check { position:absolute; top:6px; right:8px; width:18px; height:18px; border-radius:50%; background:#fff; color:#089F3B; font-size:12px; font-weight:800; line-height:1; display:flex; align-items:center; justify-content:center; }
3237  .re-cats__reset { display:block; width:fit-content; margin:16px auto 0; padding:10px 20px; border:1.5px solid #1B1E22; border-radius:999px; font-size:13px; font-weight:700; color:#1B1E22; text-decoration:none; cursor:pointer; }
3238  .re-cats__reset:hover { background:#1B1E22; color:#fff; }
3239  .re-oq-title.is-filtered { font-size:20px !important; text-transform:none !important; line-height:1.25 !important; text-align:center; }
3240  .otras_preguntas-header:has(.re-oq-title.is-filtered) { display:block !important; }
3241  #re-oq-clear { display:block !important; width:100% !important; text-align:center !important; margin-top:8px !important; float:none !important; }
3242  /* Boton de continuidad al final del bloque */
3243  .re-oq-explorar { display:block; width:fit-content; margin:32px auto 0; padding:14px 28px; border-radius:8px; background:#089F3B; color:#fff !important; font-size:13px; font-weight:700; text-transform:uppercase; letter-spacing:.02em; text-decoration:none; text-align:center; }
3244  .re-oq-explorar:hover { background:#067A2D; }
3245</style>
3246<script>
3247(function () {
3248  if (window.location.pathname.indexOf('/pregunta-al-experto') !== 0) return;
3249
3250  var CAT = new URLSearchParams(window.location.search).get('categoria');
3251  var contadoresPintados = false;
3252  var yaColocado = false;
3253
3254  function textoVer(n) {
3255    if (!n) return 'Leer respuesta del entrenador →';
3256    if (n === 1) return 'Leer respuesta del entrenador →';
3257    return 'Leer las ' + n + ' respuestas →';
3258  }
3259
3260  function quitarFiltro(e) {
3261    if (e) { e.preventDefault(); e.stopPropagation(); }
3262    CAT = null;
3263    try { history.replaceState(null, '', window.location.pathname); } catch (err) {}
3264
3265    Array.prototype.forEach.call(document.querySelectorAll('.re-cats__link'), function (chip) {
3266      chip.classList.remove('is-active');
3267      var c = chip.querySelector('.re-cats__check');
3268      if (c) c.parentNode.removeChild(c);
3269      var orig = chip.getAttribute('data-re-href');
3270      if (orig) chip.setAttribute('href', orig);
3271    });
3272
3273    Array.prototype.forEach.call(document.querySelectorAll('.re-oq-card'), function (card) {
3274      var wrap = card.closest ? (card.closest('.w-dyn-item') || card) : card;
3275      wrap.style.setProperty('display', '', 'important');
3276    });
3277
3278    var header = document.querySelector('.re-oq-title');
3279    if (header) {
3280      header.removeAttribute('data-re-done');
3281      header.classList.remove('is-filtered');
3282      header.textContent = '¿Tienes prisa? Encuentra tu respuesta al instante';
3283    }
3284    var clear = document.getElementById('re-oq-clear');
3285    if (clear) clear.parentNode.removeChild(clear);
3286    var reset = document.getElementById('re-cats-reset');
3287    if (reset) reset.parentNode.removeChild(reset);
3288    var expl = document.getElementById('re-oq-explorar');
3289    if (expl) expl.style.display = 'none';
3290  }
3291
3292  function aplicar() {
3293    var chips = document.querySelectorAll('.re-cats__link');
3294    var cards = document.querySelectorAll('.re-oq-card');
3295
3296    Array.prototype.forEach.call(chips, function (chip) {
3297      if (!chip.getAttribute('data-re-href')) {
3298        chip.setAttribute('data-re-href', chip.getAttribute('href') || '');
3299      }
3300    });
3301
3302    if (CAT) {
3303      Array.prototype.forEach.call(chips, function (chip) {
3304        var orig = chip.getAttribute('data-re-href') || '';
3305        if (orig.indexOf('categoria=' + CAT) !== -1) {
3306          chip.classList.add('is-active');
3307          chip.setAttribute('title', 'Quitar filtro');
3308          if (!chip.getAttribute('data-re-toggle')) {
3309            chip.setAttribute('data-re-toggle', '1');
3310            chip.addEventListener('click', function (e) {
3311              if (chip.classList.contains('is-active')) quitarFiltro(e);
3312            });
3313          }
3314          if (!chip.querySelector('.re-cats__check')) {
3315            var c = document.createElement('span');
3316            c.className = 're-cats__check';
3317            c.textContent = '✕';
3318            chip.appendChild(c);
3319          }
3320        }
3321      });
3322
3323      var listaCats = document.querySelector('.re-cats__list');
3324      if (listaCats && !document.getElementById('re-cats-reset')) {
3325        var r = document.createElement('a');
3326        r.id = 're-cats-reset';
3327        r.className = 're-cats__reset';
3328        r.href = '#';
3329        r.textContent = 'Ver todas las categorias';
3330        r.addEventListener('click', quitarFiltro);
3331        listaCats.insertAdjacentElement('afterend', r);
3332      }
3333
3334      if (!yaColocado) {
3335        yaColocado = true;
3336        var seccionCats = document.querySelector('.re-cats');
3337        if (seccionCats && window.scrollY < 40) {
3338          var top = seccionCats.getBoundingClientRect().top + window.scrollY - 70;
3339          window.scrollTo(0, top > 0 ? top : 0);
3340        }
3341      }
3342    }
3343
3344    if (!cards.length) return false;
3345
3346    // Boton de continuidad al final del bloque de preguntas
3347    var seccionOQ = document.querySelector('.re-oq-section');
3348    var listaOQ = document.querySelector('.re-oq-section .w-dyn-list');
3349    if (seccionOQ && listaOQ && !document.getElementById('re-oq-explorar')) {
3350      var ex = document.createElement('a');
3351      ex.id = 're-oq-explorar';
3352      ex.className = 're-oq-explorar';
3353      ex.href = '/pregunta-al-experto';
3354      ex.textContent = 'Explorar toda la biblioteca de preguntas';
3355      ex.addEventListener('click', function (e) {
3356        if (CAT) { quitarFiltro(e); }
3357      });
3358      listaOQ.insertAdjacentElement('afterend', ex);
3359    }
3360    var explBtn = document.getElementById('re-oq-explorar');
3361    if (explBtn) explBtn.style.display = CAT ? 'block' : 'none';
3362
3363    if (CAT) {
3364      var filtro = CAT.trim().toLowerCase();
3365      var visibles = 0;
3366      Array.prototype.forEach.call(cards, function (card) {
3367        var h = card.querySelector('.re-oq-hidden');
3368        var cat = h ? (h.textContent || '').trim().toLowerCase() : '';
3369        var ok = (cat === filtro);
3370        var wrap = card.closest ? (card.closest('.w-dyn-item') || card) : card;
3371        wrap.style.setProperty('display', ok ? '' : 'none', 'important');
3372        if (ok) visibles++;
3373      });
3374
3375      var header = document.querySelector('.re-oq-title');
3376      if (header && !header.getAttribute('data-re-done')) {
3377        header.setAttribute('data-re-done', '1');
3378        header.classList.add('is-filtered');
3379        var nombre = filtro.replace(/-/g, ' ');
3380        nombre = nombre.charAt(0).toUpperCase() + nombre.slice(1);
3381        header.textContent = visibles > 0
3382          ? nombre + ' (' + visibles + ')'
3383          : 'Todavia no hay preguntas sobre ' + nombre.toLowerCase();
3384
3385        if (!document.getElementById('re-oq-clear')) {
3386          var a = document.createElement('a');
3387          a.id = 're-oq-clear';
3388          a.href = '#';
3389          a.textContent = 'Ver todas las preguntas →';
3390          a.style.cssText = 'display:block;width:100%;text-align:center;margin-top:8px;font-size:13px;font-weight:700;color:#089F3B;text-decoration:none;';
3391          a.addEventListener('click', quitarFiltro);
3392          var contenedor = header.parentElement || header;
3393          contenedor.insertAdjacentElement('afterend', a);
3394        }
3395      }
3396    }
3397
3398    Array.prototype.forEach.call(cards, function (card) {
3399      var detail = card.querySelector('.re-oq-detail');
3400      var toggle = card.querySelector('[data-oq-toggle]');
3401      if (!detail || !toggle || toggle.getAttribute('data-re-bound')) return;
3402      toggle.setAttribute('data-re-bound', '1');
3403      toggle.style.display = (detail.scrollHeight > detail.clientHeight + 2) ? 'block' : 'none';
3404      toggle.addEventListener('click', function (e) {
3405        e.preventDefault();
3406        e.stopPropagation();
3407        var exp = detail.classList.toggle('re-oq-detail--expanded');
3408        toggle.textContent = exp ? 'Ver menos' : 'Ver mas';
3409      });
3410    });
3411
3412    if (!contadoresPintados) {
3413      contadoresPintados = true;
3414      cargarSupabase(function (db) {
3415        // Nada de respuestas(count): PostgREST lo traduce a una referencia de
3416        // fila entera y la clave publicable tiene los permisos de `respuestas`
3417        // por columna (migracion 20260819150000), asi que devuelve 403. Se
3418        // piden los id de una columna concedida y se cuentan aqui.
3419        db.from('preguntas').select('slug, respuestas(id)').then(function (res) {
3420          if (res.error) { console.warn('[PAE] contador de respuestas:', res.error.message); return; }
3421          var mapa = {};
3422          (res.data || []).forEach(function (r) {
3423            mapa[r.slug] = (r.respuestas || []).length;
3424          });
3425          Array.prototype.forEach.call(document.querySelectorAll('.re-oq-card'), function (card) {
3426            var slug = (card.getAttribute('href') || '').split('/').filter(Boolean).pop();
3427            var v = card.querySelector('[data-oq-ver]');
3428            if (!v) return;
3429            v.textContent = textoVer(mapa[slug]);
3430          });
3431        });
3432      });
3433    }
3434
3435    return true;
3436  }
3437
3438  function cargarSupabase(cb) {
3439    function go() {
3440      try {
3441        cb(window.supabase.createClient(
3442          'https://yrzanatzshdpvypazgeb.supabase.co',
3443          'sb_publishable_O4cIYIJcDs3_GcbjNHdd9g_e2cQKOUD'
3444        ));
3445      } catch (e) {}
3446    }
3447    if (window.supabase && window.supabase.createClient) { go(); return; }
3448    var s = document.createElement('script');
3449    s.src = 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2';
3450    s.onload = go;
3451    document.head.appendChild(s);
3452  }
3453
3454  function arrancar() {
3455    aplicar();
3456    var intentos = 0;
3457    var t = setInterval(function () {
3458      intentos++;
3459      aplicar();
3460      if (intentos > 20) clearInterval(t);
3461    }, 300);
3462
3463    var lista = document.querySelector('.w-dyn-list');
3464    if (lista && window.MutationObserver) {
3465      new MutationObserver(function () { aplicar(); }).observe(lista, { childList: true, subtree: true });
3466    }
3467  }
3468
3469  if (document.readyState === 'loading') {
3470    document.addEventListener('DOMContentLoaded', arrancar);
3471  } else {
3472    arrancar();
3473  }
3474})();
3475</script>
3475
3476
3477<!-- ══════════════════════════════════════════════════════════
3478     BOTÓN FLOTANTE DE WHATSAPP
3479     ══════════════════════════════════════════════════════════ -->
3480
3481<a
3482  href="https://wa.me/34633590498?text=Hola%2C%20me%20gustar%C3%ADa%20m%C3%A1s%20informaci%C3%B3n"
3483  target="_blank"
3484  rel="noopener noreferrer"
3485  class="wa-float-btn"
3486  aria-label="Contactar por WhatsApp"
3487>
3488  <svg viewBox="0 0 24 24" width="30" height="30" fill="#FFFFFF" xmlns="http://www.w3.org/2000/svg">
3489    <path d="M12 2a10 10 0 0 0-8.61 15.06L2 22l5.06-1.33A10 10 0 1 0 12 2zm0 18.13a8.1 8.1 0 0 1-4.14-1.14l-.3-.18-3.08.81.82-3-.2-.31A8.13 8.13 0 1 1 12 20.13z"/>
3490    <path d="M16.57 13.72c-.25-.13-1.47-.72-1.7-.81s-.4-.13-.57.13-.65.81-.8.97-.3.19-.55.06a6.76 6.76 0 0 1-2-1.23 7.47 7.47 0 0 1-1.38-1.72c-.14-.25 0-.38.11-.5s.25-.3.37-.44a1.7 1.7 0 0 0 .25-.42.46.46 0 0 0 0-.44c-.06-.13-.57-1.37-.78-1.87s-.42-.43-.57-.44h-.49a.94.94 0 0 0-.68.32 2.87 2.87 0 0 0-.9 2.13 5 5 0 0 0 1.05 2.64 11.4 11.4 0 0 0 4.36 3.85c.61.26 1.08.42 1.45.54a3.48 3.48 0 0 0 1.6.1 2.62 2.62 0 0 0 1.72-1.21 2.13 2.13 0 0 0 .15-1.21c-.06-.11-.23-.17-.48-.3z"/>
3491  </svg>
3492</a>
3493
3494<style>
3495  .wa-float-btn {
3496    position: fixed;
3497    bottom: 24px;
3498    right: 24px;
3499    width: 60px;
3500    height: 60px;
3501    background-color: #25D366;
3502    border-radius: 50%;
3503    display: flex;
3504    align-items: center;
3505    justify-content: center;
3506    box-shadow: 0 4px 14px rgba(0,0,0,0.25);
3507    z-index: 9999;
3508    text-decoration: none;
3509    transition: transform 0.2s ease, box-shadow 0.2s ease;
3510  }
3511  .wa-float-btn:hover {
3512    transform: scale(1.08);
3513    box-shadow: 0 6px 18px rgba(0,0,0,0.3);
3514  }
3515  @media (max-width: 480px) {
3516    .wa-float-btn {
3517      width: 52px;
3518      height: 52px;
3519      bottom: 16px;
3520      right: 16px;
3521    }
3522  }
3523</style>
3524
3525<!-- ══════════════════════════════════════════════════════════
3526     OCULTAR SECCIONES CON LISTAS CMS VACÍAS (ciudad + objetivo)
3527     Solo listas de primer nivel: una lista anidada vacía dentro
3528     de una tarjeta (p. ej. las especialidades de un entrenador)
3529     no debe esconder la sección entera.
3530     ══════════════════════════════════════════════════════════ -->
3531<script>
3532document.querySelectorAll('.w-dyn-empty').forEach(function (emptyEl) {
3533  var lista = emptyEl.closest('.w-dyn-list');
3534  if (!lista) return;
3535
3536  // Lista anidada dentro de otra lista CMS (una tarjeta): no decide por la sección.
3537  if (lista.parentElement && lista.parentElement.closest('.w-dyn-list')) return;
3538
3539  // Webflow deja el estado vacio en el HTML aunque la lista tenga elementos:
3540  // lo tapa con w-dyn-hide. Sin esta comprobacion se escondia un bloque lleno.
3541  if (lista.querySelector('.w-dyn-item')) return;
3542
3543  // En la ficha del entrenador cada bloque es un .entrenador_sobre-m dentro de
3544  // una unica <section> que ademas lleva el formulario de contacto, asi que la
3545  // regla de abajo no llegaba y se quedaba a la vista el "No items found." de
3546  // Modalidad (Ismael Diaz, 20/08/2026). Aqui manda el bloque, no la seccion.
3547  var bloque = emptyEl.closest('.entrenador_sobre-m');
3548  if (bloque) {
3549    bloque.style.display = 'none';
3550    return;
3551  }
3552
3553  var section = emptyEl.closest('section');
3554  if (section && !section.querySelector('form')) {
3555    section.style.display = 'none';
3556  }
3557});
3558</script>
3558
3559
3560<!-- ══════════════════════════════════════════════════════════
3561     EMAIL — aviso de errata en el dominio (sugiere, no bloquea)
3562     Vale para TODOS los formularios del sitio, presentes y futuros:
3563     engancha por delegacion a cualquier <input> de email, no hay
3564     que tocar los widgets uno a uno.
3565     La regla vive en supabase/functions/_shared/validacion-contacto.ts
3566     (`sugerenciaDominioEmail`); esto es su copia de navegador.
3567     ══════════════════════════════════════════════════════════ -->
3568<style>
3569  .re-mailerrata { margin-top:6px; padding:8px 10px; border-radius:8px; background:#FEF3C7; color:#92400E; font-size:12.5px; line-height:1.35; }
3570  .re-mailerrata__fix { padding:0; border:0; background:none; font:inherit; font-weight:700; color:#92400E; text-decoration:underline; cursor:pointer; }
3571</style>
3572<script>
3573(function () {
3574  /* Dominios que damos por buenos. Un dominio de esta lista no se marca nunca,
3575     y un dominio a 1-2 letras de uno de ellos es el que se sugiere. */
3576  var DOMINIOS = [
3577    'gmail.com','googlemail.com',
3578    'hotmail.com','hotmail.es','hotmail.fr','hotmail.it','hotmail.co.uk','hotmail.com.ar','hotmail.com.mx',
3579    'outlook.com','outlook.es','outlook.fr','outlook.it',
3580    'live.com','live.com.mx','live.co.uk','msn.com',
3581    'yahoo.com','yahoo.es','yahoo.it','yahoo.fr','yahoo.co.uk','yahoo.com.ar','yahoo.com.mx',
3582    'icloud.com','me.com','mac.com',
3583    'aol.com','protonmail.com','proton.me','gmx.es','gmx.com','gmx.net','zoho.com','mail.com',
3584    'terra.es','telefonica.net','movistar.es','orange.es','wanadoo.es','ya.com','arrakis.es','euskalnet.net'
3585  ];
3586
3587  function distancia(a, b) {
3588    var m = a.length, n = b.length, i, j, fila = [], ant, tmp;
3589    for (j = 0; j <= n; j++) fila[j] = j;
3590    for (i = 1; i <= m; i++) {
3591      ant = fila[0]; fila[0] = i;
3592      for (j = 1; j <= n; j++) {
3593        tmp = fila[j];
3594        fila[j] = Math.min(fila[j] + 1, fila[j - 1] + 1, ant + (a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1));
3595        ant = tmp;
3596      }
3597    }
3598    return fila[n];
3599  }
3600
3601  /* Devuelve el email corregido o null. Solo mira el dominio, y solo cuando el
3602     email ya tiene forma valida: el "falta algo" lo dice el widget, no esto.
3603     El tope de 2 letras se reserva a dominios largos; en uno corto (ya.com,
3604     me.com) dos cambios ya son otro dominio distinto, no una errata. */
3605  function sugerencia(valor) {
3606    var limpio = String(valor == null ? '' : valor).trim().toLowerCase();
3607    if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(limpio)) return null;
3608    var corte = limpio.lastIndexOf('@');
3609    var local = limpio.slice(0, corte), dominio = limpio.slice(corte + 1);
3610    if (DOMINIOS.indexOf(dominio) !== -1) return null;
3611    var tope = dominio.length >= 8 ? 2 : 1;
3612    var mejor = null, mejorD = 3, i, d;
3613    for (i = 0; i < DOMINIOS.length; i++) {
3614      d = distancia(dominio, DOMINIOS[i]);
3615      if (d <= tope && d < mejorD) { mejorD = d; mejor = DOMINIOS[i]; }
3616    }
3617    return mejor ? local + '@' + mejor : null;
3618  }
3619
3620  function esCampoEmail(el) {
3621    if (!el || el.tagName !== 'INPUT') return false;
3622    var t = (el.getAttribute('type') || '').toLowerCase();
3623    if (t === 'email') return true;
3624    if (t && t !== 'text') return false;
3625    if ((el.getAttribute('inputmode') || '').toLowerCase() === 'email') return true;
3626    return /e-?mail|correo/i.test((el.name || '') + ' ' + (el.id || '') + ' ' + (el.getAttribute('autocomplete') || ''));
3627  }
3628
3629  function quitar(input) {
3630    var sig = input.nextElementSibling;
3631    if (sig && sig.classList && sig.classList.contains('re-mailerrata')) sig.parentNode.removeChild(sig);
3632  }
3633
3634  function medir(evento, dominio) {
3635    try { if (window.amplitude && window.amplitude.track) window.amplitude.track(evento, { dominio: dominio }); } catch (e) {}
3636  }
3637
3638  function revisar(input) {
3639    quitar(input);
3640    if (!input.isConnected) return;
3641    var propuesta = sugerencia(input.value);
3642    if (!propuesta) return;
3643
3644    var caja = document.createElement('div');
3645    caja.className = 're-mailerrata';
3646    caja.setAttribute('role', 'status');
3647    caja.appendChild(document.createTextNode('¿Quisiste decir '));
3648
3649    var btn = document.createElement('button');
3650    btn.type = 'button';
3651    btn.className = 're-mailerrata__fix';
3652    btn.textContent = propuesta;
3653    btn.addEventListener('click', function () {
3654      input.value = propuesta;
3655      try {
3656        input.dispatchEvent(new Event('input', { bubbles: true }));
3657        input.dispatchEvent(new Event('change', { bubbles: true }));
3658      } catch (e) {}
3659      quitar(input);
3660      input.focus();
3661      medir('email_errata_corregida', propuesta.split('@')[1]);
3662    });
3663
3664    caja.appendChild(btn);
3665    caja.appendChild(document.createTextNode('?'));
3666    input.insertAdjacentElement('afterend', caja);
3667    medir('email_errata_sugerida', input.value.split('@').pop().toLowerCase());
3668  }
3669
3670  /* Al escribir se propone con retardo (asi el aviso ya esta en pantalla antes
3671     de que llegue al boton de enviar) y al salir del campo, de inmediato. */
3672  var reloj = null;
3673  document.addEventListener('input', function (e) {
3674    if (!esCampoEmail(e.target)) return;
3675    quitar(e.target);
3676    clearTimeout(reloj);
3677    var input = e.target;
3678    reloj = setTimeout(function () { revisar(input); }, 500);
3679  });
3680  document.addEventListener('focusout', function (e) {
3681    if (!esCampoEmail(e.target)) return;
3682    clearTimeout(reloj);
3683    revisar(e.target);
3684  });
3685})();
3686</script>
3686
3687
3688<!-- Master Tag add just before the closing </body> tag -->
3689                    
3689<script src="https://www.dwin1.com/130189.js" type="text/javascript" defer="defer"></script>
vendor: 1 bytes, line 3689
3689
3690<script src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe%2F689e5ba67671442434f3ca35%2F6a815b2ecfc98b6d18a9afab%2Fvisitas_ficha_publica-1.0.0.js" type="text/javascript"></script>
3690<script src="https://cdn.prod.website-files.com/68c5a729c71fa3f37858fbbe%2F689e5ba67671442434f3ca35%2F6a9562a243b6dd3757592da5%2Flimpiarcamposcontacto-1.0.0.js" type="text/javascript"></script>
3690</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.