/*
 * Secciones de la portada, en el mismo orden en que aparecen en el Figma:
 * hero, franja de datos, banner de preventa, espacios, carrusel de actividades,
 * calendario, planes y galería.
 */

/* --- Hero ---------------------------------------------------------------- */

/* En escritorio el hero es medio texto y medio foto a sangre; en móvil la foto
   baja debajo del texto y deja de estar pegada al borde. */
.fr-hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  min-height: 640px;
  background: var(--republic-black);
}
/* El texto del hero tiene que arrancar en la misma vertical que el resto de las
   secciones. La columna mide la mitad del ancho de la página, así que el margen
   que le falta para llegar al borde del contenido de 1280 es
   (ancho de la columna - 1280/2); por debajo de ese ancho se cae al gutter
   normal. Se calcula con 100% y no con 100vw porque 100vw incluye la barra de
   desplazamiento y dejaría el hero unos píxeles corrido respecto del resto. */
.fr-hero__texto {
  padding: var(--space-8) var(--gutter) var(--space-8)
    max(var(--gutter), calc(100% - var(--container) / 2));
  justify-self: stretch;
}
/* El tamaño está atado al ancho real de la columna, no elegido a ojo: la
   palabra más larga ("REPUBLIC.") mide 6,85 veces el font-size en Phonk, así
   que por encima de ~79px con la columna en su máximo de 640px el punto final
   se cortaría. La fórmula deja el renglón justo dentro del ancho útil en todo
   el rango, que es como está dibujado en el Figma. */
.fr-hero__titulo {
  font-family: var(--font-display);
  font-size: clamp(44px, calc(7.2vw - 14px), 78px);
  line-height: 0.95;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
}
.fr-hero__firma {
  display: block;
  font-family: var(--font-script);
  color: var(--accent);
  font-size: clamp(26px, 3.4vw, 44px);
  line-height: 1.4;
  text-transform: none;
  letter-spacing: 0;
  margin-top: var(--space-1);
}
.fr-hero__bajada {
  margin-top: var(--space-4);
  font-size: var(--text-body-lg);
  color: var(--republic-white);
  max-width: 56ch;
  text-wrap: pretty;
}
.fr-hero__lema {
  margin-top: var(--space-4);
  font-weight: 700;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  font-size: var(--text-body-md);
}
.fr-hero__cta { margin-top: var(--space-5); }

/* overflow: hidden recorta el leve acercamiento de la entrada de la foto, que si
   no se metería sobre la columna de texto durante un segundo. */
.fr-hero__foto { position: relative; height: 100%; min-height: 420px; overflow: hidden; }
.fr-hero__foto img { width: 100%; height: 100%; object-fit: cover; }

/* Sello circular sobre la foto: texto curvo "FIT REPUBLIC" alrededor del rayo. */
.fr-hero__sello {
  position: absolute;
  right: clamp(12px, 3vw, 40px);
  bottom: clamp(12px, 3vw, 40px);
  width: clamp(96px, 12vw, 150px);
  opacity: 0.85;
  pointer-events: none;
}

@media (max-width: 900px) {
  .fr-hero {
    grid-template-columns: 1fr;
    min-height: 0;
  }
  /* A una sola columna el ancho útil es toda la pantalla menos los márgenes,
     así que el título vuelve a crecer respecto de la fórmula de escritorio. */
  .fr-hero__titulo { font-size: clamp(40px, 12.6vw, 56px); }
  .fr-hero__texto {
    padding: var(--space-6) var(--gutter) var(--space-5);
  }
  .fr-hero__foto {
    min-height: 0;
    margin: 0 var(--gutter) var(--space-6);
    border-radius: var(--radius-card);
    overflow: hidden;
  }
  .fr-hero__foto img { aspect-ratio: 16 / 11; height: auto; }
}

/* --- Franja de datos ----------------------------------------------------- */

.fr-datos { background: var(--concrete-raised); padding-block: var(--space-4); }
.fr-datos__grid { display: grid; gap: var(--space-5); }

/* Dos repartos a elección desde el panel (fr_local_reparto).

   Con las cuatro columnas del mismo ancho, el dato más largo —el horario— llena la
   suya y el hueco que le sigue se queda en el gap pelado, mientras que a los demás
   les sobra lugar y se ven al doble. Con el reparto por espacios, cada columna mide
   su contenido y el sobrante se parte en partes iguales entre las tres
   separaciones; el gap pasa a ser el mínimo. */
.fr-datos__grid.is-reparto-columnas { grid-template-columns: repeat(4, 1fr); }
.fr-datos__grid.is-reparto-espacios {
  grid-template-columns: repeat(4, auto);
  justify-content: space-between;
}
.fr-dato { display: flex; align-items: flex-start; gap: var(--space-3); }
.fr-dato svg { flex-shrink: 0; margin-top: 2px; color: var(--republic-white); }
.fr-dato__label {
  font-size: var(--text-label);
  font-weight: 700;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text-secondary);
}
.fr-dato__valor { font-size: var(--text-body-sm); font-weight: 600; }
.fr-dato__valor a:hover { color: var(--accent); }

/* Cuatro datos no entran en una línea por debajo del escritorio, pero de a uno
   estiran la franja de más: el paso intermedio son dos columnas. */
/* Por debajo del escritorio manda el ancho y no el ajuste: con dos columnas,
   repartir por espacios abriría un hueco enorme en el medio. Los selectores llevan
   las dos clases para empatarle en especificidad a las reglas de arriba. */
@media (max-width: 1100px) {
  .fr-datos__grid.is-reparto-columnas,
  .fr-datos__grid.is-reparto-espacios {
    grid-template-columns: repeat(2, 1fr);
    justify-content: stretch;
    gap: var(--space-4);
  }
}
@media (max-width: 600px) {
  .fr-datos__grid.is-reparto-columnas,
  .fr-datos__grid.is-reparto-espacios { grid-template-columns: 1fr; }
}

/* --- Banner de preventa -------------------------------------------------- */

/* Reconstruye el componente "Preventa Banner" del Figma: las sílabas PRE/VEN/TA
   se muestran una a una y después se resuelve en el logotipo completo. */
.fr-preventa {
  position: relative;
  background: var(--republic-black);
  border-top: 1px solid var(--border-default);
  border-bottom: 1px solid var(--border-default);
  overflow: hidden;
  padding: var(--space-4) var(--gutter);
}
.fr-preventa__inner {
  position: relative;
  /* Acá el gutter ya lo pone la sección, así que el tope es el ancho de
     contenido a secas. */
  max-width: var(--container);
  margin-inline: auto;
  min-height: 200px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(24px, 4vw, 56px);
}
.fr-preventa__silabas {
  position: relative;
  flex: 0 0 auto;
  width: min(340px, 100%);
  height: 200px;
  overflow: hidden;
}
.fr-preventa__silabas > div {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
}
.fr-preventa__silaba {
  font-family: var(--font-display);
  font-size: clamp(34px, 4.2vw, 52px);
  line-height: 0.92;
  letter-spacing: var(--ls-display);
  display: block;
}
/* Cada sílaba entra escalonada y las tres desaparecen cuando aparece el
   logotipo; steps(1) evita cualquier fundido intermedio. */
.fr-preventa__silaba:nth-child(1) { animation: fr-syl1 3.4s steps(1, end) infinite; }
.fr-preventa__silaba:nth-child(2) {
  color: var(--accent);
  padding-left: 0.85em;
  animation: fr-syl2 3.4s steps(1, end) infinite;
}
.fr-preventa__silaba:nth-child(3) {
  padding-left: 3.1em;
  animation: fr-syl3 3.4s steps(1, end) infinite;
}
.fr-preventa__logo {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  animation: fr-wordfull 3.4s ease-out infinite;
}
/* El tope es el ancho, pero manda el que se alcance primero: una foto vertical se
   reduce por el alto en vez de recortarse contra el `overflow: hidden` del padre, y sin
   `object-fit` no hay bandas ni deformación. Efecto buscado: una imagen más chica que el
   tope ya no se estira, se dibuja a su tamaño. */
.fr-preventa__logo img {
  width: auto;
  max-width: min(290px, 100%);
  max-height: 100%;
  height: auto;
}

/* El lema, cuando el cuadro final es texto. Lleva su propio tamaño y no el de las
   sílabas: aquellas son tres trozos de tres letras y esto son palabras, y con el `clamp`
   de las sílabas el cuarto renglón ya no entraba en la caja de 150 px del teléfono. */
.fr-preventa__final-texto {
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100%;
  gap: 10px;
}
.fr-preventa__final-linea {
  font-family: var(--font-display);
  font-size: clamp(22px, 3.2vw, 34px);
  line-height: 0.95;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  display: block;
}

@keyframes fr-syl1 { 0%, 74% { opacity: 1; } 75%, 100% { opacity: 0; } }
@keyframes fr-syl2 { 0%, 14% { opacity: 0; } 15%, 74% { opacity: 1; } 75%, 100% { opacity: 0; } }
@keyframes fr-syl3 { 0%, 29% { opacity: 0; } 30%, 74% { opacity: 1; } 75%, 100% { opacity: 0; } }
@keyframes fr-wordfull {
  0%, 74% { opacity: 0; transform: scale(0.96); }
  80%, 96% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; }
}

.fr-preventa__texto {
  flex: 1 1 300px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-start;
}
.fr-preventa__titulo {
  font-family: var(--font-display);
  font-size: clamp(24px, 2.8vw, 34px);
  line-height: var(--lh-display-lg);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
}
.fr-preventa__bajada {
  font-size: 15px;
  line-height: var(--lh-body-sm);
  color: var(--text-secondary);
  max-width: 400px;
  text-wrap: pretty;
}
.fr-preventa__cta { flex: 0 0 auto; }

@media (max-width: 900px) {
  .fr-preventa__silabas { height: 150px; width: 100%; }
  .fr-preventa__cta { width: 100%; }
  .fr-preventa__cta .fr-btn { width: 100%; }
}

/* Con «reducir movimiento», el bloque global de base.css recorta las animaciones a
   0,001 ms y una sola vuelta, pero no fija `animation-fill-mode`: al terminar, cada capa
   vuelve a su estilo base —donde nadie declara `opacity`— y se veían las cuatro
   encimadas. Acá se resuelve a mano lo que la animación resolvía con el tiempo: una sola
   capa, la final. */
@media (prefers-reduced-motion: reduce) {
  /* `:not()` y no `> div` a secas: la capa final también es un hijo directo, y sin el
     recorte esta regla la escondía junto con las sílabas. */
  .fr-preventa__silabas > div:not(.fr-preventa__logo) { display: none; }
  .fr-preventa__logo {
    animation: none;
    opacity: 1;
    transform: none;
  }
}

/* --- Espacios ------------------------------------------------------------ */

.fr-espacios__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}
/* Tarjeta con foto de fondo y degradado para que el texto se lea sin importar
   qué tan clara sea la imagen. */
.fr-espacio {
  position: relative;
  border-radius: var(--radius-card);
  overflow: hidden;
  min-height: 280px;
  display: flex;
  align-items: flex-end;
  isolation: isolate;
}
.fr-espacio img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
  transition: transform var(--dur-larga) var(--ease-out);
}
.fr-espacio::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, rgba(0,0,0,0.1) 30%, rgba(0,0,0,0.88) 100%);
}
/* Los espacios no son botones: el hover no puede prometer un clic. Nada de subir,
   ni borde rojo, ni cambio de cursor. Solo profundidad: la foto se acerca despacio,
   el velo oscuro se aclara para que se vea más el lugar, y la tarjeta proyecta
   una sombra sobre el fondo. */
.fr-espacio { transition: box-shadow var(--dur-larga) var(--ease-out); }
.fr-espacio::after { transition: opacity var(--dur-larga) var(--ease-out); }
@media (hover: hover) {
  .fr-espacio:hover {
    box-shadow:
      0 28px 56px -24px rgba(0, 0, 0, 0.95),
      0 0 0 1px rgba(242, 242, 242, 0.08);
  }
  .fr-espacio:hover img { transform: scale(1.06); }
  .fr-espacio:hover::after { opacity: 0.8; }
}
.fr-espacio__cuerpo { padding: var(--space-4); }
.fr-espacio__titulo {
  font-family: var(--font-display);
  font-size: var(--text-display-md);
  line-height: var(--lh-display-md);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
}
.fr-espacio__desc {
  margin-top: var(--space-2);
  font-size: var(--text-body-sm);
  color: var(--republic-white);
  text-wrap: pretty;
}

@media (max-width: 900px) {
  .fr-espacios__grid { grid-template-columns: 1fr; }
}

/* --- Carrusel de actividades --------------------------------------------- */

.fr-actividades { position: relative; overflow: hidden; isolation: isolate; }

/* Luz roja detrás de las tarjetas. Es un círculo desenfocado y quieto, igual que en
   el componente del Figma: da profundidad sin competir con la foto. */
.fr-actividades__luz {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(900px, 120vw);
  aspect-ratio: 1;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(circle, var(--accent), transparent 70%);
  opacity: 0.28;
  filter: blur(40px);
  pointer-events: none;
  z-index: -1;
}

/* El carrusel ocupa el ancho de la ventana: el recorte al pasar de una tarjeta a la
   siguiente tiene que verse en el borde de la pantalla, no a media página. */
.fr-actividades__viewport { overflow: hidden; width: 100%; }
.fr-actividades__track {
  display: flex;
  transition: transform var(--dur-larga) var(--ease-out);
}
.fr-actividades__track[data-arrastrando="true"] { transition: none; }

.fr-slide { flex: 0 0 100%; min-width: 0; }

/* Lo acotado es el contenido, no la diapositiva. */
.fr-slide__inner {
  max-width: 1100px;
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid;
  grid-template-columns: minmax(0, 420px) 1fr;
  gap: clamp(24px, 5vw, 72px);
  align-items: center;
}
.fr-slide__foto {
  border-radius: var(--radius-card);
  overflow: hidden;
  /* La inclinación es del diseño: las fotos del carrusel están giradas unos
     grados, como una foto apoyada sobre la mesa. */
  transform: rotate(-3deg);
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5);
}
.fr-slide__foto img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; }
/* Al pasar el puntero la actividad entera (foto y texto) se levanta un poco. Sin
   borde ni sombra nueva: es solo el gesto de levantar. El hover se toma en la
   diapositiva, que no se mueve, y lo que sube es el contenido: si subiera el mismo
   elemento que tiene el hover, se escaparía del puntero por el borde inferior y la
   animación temblaría. */
.fr-slide__inner { transition: translate var(--dur-larga) var(--ease-out); }
@media (hover: hover) {
  .fr-slide:hover .fr-slide__inner { translate: 0 -10px; }
}
@media (prefers-reduced-motion: reduce) {
  .fr-slide:hover .fr-slide__inner { translate: none; }
}
.fr-slide__intensidad {
  font-size: var(--text-caption);
  font-weight: 700;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
/* 6px y no un token de la escala: las llamas tenían aire propio dentro de su
   caja de 24 (unos 2 px por lado a 14 px de alto), así que con 2 px de gap
   quedaban a ~6 px una de otra. El rayo ocupa su caja entera, y con el mismo gap
   se pegaban. Esto devuelve el ritmo que tenían. */
.fr-slide__rayos { display: inline-flex; gap: 6px; }
.fr-slide__rayos span { opacity: 0.25; }
.fr-slide__rayos span[data-on="1"] { opacity: 1; }
.fr-slide__titulo {
  margin-top: var(--space-3);
  font-family: var(--font-display);
  font-size: var(--text-display-lg);
  line-height: var(--lh-display-lg);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
}
.fr-slide__desc {
  margin-top: var(--space-4);
  color: var(--republic-white);
  font-size: var(--text-body-lg);
  max-width: 54ch;
  text-wrap: pretty;
}
.fr-slide__caracteristicas { margin-top: var(--space-5); }
.fr-slide__caracteristicas .fr-label { display: block; margin-bottom: var(--space-3); }
.fr-slide__chips { display: flex; flex-wrap: wrap; gap: var(--space-3); }

.fr-actividades__nav {
  position: absolute;
  top: var(--space-8);
  z-index: 2;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-default);
  background: none;
  color: var(--republic-white);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--dur-rapida) var(--ease-suave), border-color var(--dur-rapida) var(--ease-suave);
}
.fr-actividades__nav:hover { background: var(--concrete-grey); border-color: var(--republic-white); }
.fr-actividades__nav--prev { left: var(--gutter); }
.fr-actividades__nav--next { right: var(--gutter); }

/* Progreso: la barra roja, el par de números 01 · 06 y la barra gris, como en el
   Figma. La barra roja además hace de reloj: se va llenando mientras dura cada
   tarjeta y, cuando se completa, el carrusel pasa a la siguiente. carrusel.js
   escucha el final de esta misma animación, así que lo que se ve y el avance no
   pueden desfasarse. La duración de cada tarjeta se ajusta acá, en --fr-autoplay. */
.fr-actividades { --fr-autoplay: 7s; }

.fr-actividades__progreso {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
  font-size: var(--text-body-sm);
  font-weight: 700;
  letter-spacing: var(--ls-label);
}
.fr-actividades__barra { width: 60px; height: 2px; background: var(--steel); }
.fr-actividades__barra--activa { background: var(--accent); }

/* La de la izquierda es la que carga: pista gris con el relleno rojo encima. Sin
   avance automático (reducir movimiento) el relleno queda completo y la barra se
   ve roja, igual que en el diseño. */
.fr-actividades__barra--carga { position: relative; overflow: hidden; }
.fr-actividades__barra-relleno {
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform-origin: left center;
}
.fr-actividades[data-autoplay="true"] .fr-actividades__barra-relleno { transform: scaleX(0); }
.fr-actividades__barra--carga.is-cargando .fr-actividades__barra-relleno {
  animation: fr-carga var(--fr-autoplay) linear forwards;
}
.fr-actividades[data-pausado="true"] .fr-actividades__barra-relleno { animation-play-state: paused; }

@keyframes fr-carga {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

@media (max-width: 900px) {
  .fr-slide__inner {
    grid-template-columns: 1fr;
    gap: var(--space-5);
  }
  .fr-slide__foto { max-width: 280px; margin-inline: auto; }
  .fr-actividades__nav { display: none; }

  /* "ENTRENAMIENTO FUNCIONAL" es el título más largo de las seis actividades y
     a 32px no entra en 375 px de ancho: se cortaba. El tamaño se ata al ancho
     de la pantalla y la palabra que no entra pasa entera al renglón siguiente.

     Sin `hyphens`: partir con guión se veía mal en un nombre corto y en
     mayúsculas, que es lo que son estos títulos. `overflow-wrap` se queda como
     última red: solo actúa si una palabra sola es más ancha que el renglón, y
     ahí parte sin guión en vez de desbordar la tarjeta. */
  .fr-slide__titulo {
    font-size: clamp(22px, 6.8vw, 30px);
    overflow-wrap: break-word;
  }
  .fr-slide__desc { font-size: var(--text-body-md); }

  /* En móvil las tarjetas van una por fila y el título entra en un renglón:
     reservar dos dejaría un hueco visible. */
  .fr-clase__titulo { min-height: 0; }
}

/* --- Calendario ---------------------------------------------------------- */

.fr-calendario__controles {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
.fr-calendario__filtro { min-width: 280px; }
.fr-calendario__semana {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill);
  padding: 4px 6px;
}
.fr-calendario__semana button {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-pill);
  border: 0;
  background: none;
  color: var(--republic-white);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.fr-calendario__semana button:hover:not(:disabled) { background: var(--concrete-raised); }
/* Con la agenda de EVO no se puede ir a semanas sin clases guardadas: hacia atrás,
   la semana pasada; hacia adelante, hasta donde EVO haya publicado. */
.fr-calendario__semana button:disabled { opacity: 0.3; cursor: default; }
.fr-calendario__rango { font-size: var(--text-body-sm); font-weight: 600; min-width: 130px; text-align: center; }

.fr-calendario__dias {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}
/* Rótulo de cada día. No es un botón: en escritorio se ven los seis días a la vez
   y no hay nada que elegir. */
.fr-dia {
  border: 0;
  border-radius: var(--radius-card);
  background: var(--concrete-raised);
  color: var(--republic-white);
  padding: 12px 8px;
  font-size: var(--text-caption);
  font-weight: 700;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-align: center;
}
/* Rojo solo para hoy. */
.fr-dia.is-hoy { background: var(--accent); color: var(--text-on-magenta); }

/* Los días que ya pasaron se apagan, encabezado y clases: siguen ahí como
   referencia, pero no compiten con lo que todavía se puede hacer. */
.fr-dia.is-pasado,
.fr-clases__col.is-pasado,
.fr-calendario__movil.is-pasado .fr-dia-actual {
  opacity: 0.6;
  filter: saturate(0.55);
}

.fr-clases {
  display: grid;
  /* minmax(0, 1fr) y no 1fr a secas: el mínimo automático de una columna de
     grid es el ancho de su contenido, así que con nombres largos la fila se
     desbordaba hacia la derecha en vez de repartirse. */
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-3);
  align-items: start;
}
/* Una columna por día: sus clases se apilan debajo del rótulo correspondiente.
   Cada columna crece por su cuenta, así un día con seis clases no estira los
   huecos de los demás. */
.fr-clases__col {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
}
/* Tarjeta de clase: el borde izquierdo rojo es la marca del diseño. */
.fr-clase {
  background: var(--surface-card);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-card);
  padding: var(--space-3);
  display: flex;
  flex-direction: column;
}
/* Obligatorio junto al display:flex de arriba. El navegador oculta [hidden]
   con una regla propia de menor prioridad que cualquier regla del tema, así
   que sin esto el display:flex la pisaba y el calendario mostraba las clases
   de todos los días a la vez. */
.fr-clase[hidden] { display: none; }
/* Profundidad: en reposo una sombra corta separa la tarjeta del fondo; al pasar
   el puntero se levanta y el borde rojo proyecta su luz hacia la izquierda. */
.fr-clase {
  box-shadow: 0 6px 16px -10px rgba(0, 0, 0, 0.9);
  transition:
    transform var(--dur-larga) var(--ease-out),
    box-shadow var(--dur-larga) var(--ease-out),
    background var(--dur-larga) var(--ease-out);
}
@media (hover: hover) {
  .fr-clase:hover {
    transform: translateY(-3px);
    background: var(--surface-raised);
    box-shadow:
      0 18px 34px -16px rgba(0, 0, 0, 0.95),
      -10px 0 28px -14px rgba(221, 7, 7, 0.6);
  }
}
@media (prefers-reduced-motion: reduce) {
  .fr-clase:hover { transform: none; }
}
/* Empuja los cupos al pie: así esa línea queda alineada entre tarjetas aunque
   el título de una ocupe dos renglones y el de la de al lado uno solo. */
.fr-clase__cupos { margin-top: auto; padding-top: var(--space-2); }
.fr-clase__titulo {
  font-family: var(--font-display);
  /* 17px es lo que mide en el Figma. Con el --text-heading del sistema (24px)
     un nombre como "STRETCHING" no entra en una columna de 185 px y se salía
     de la tarjeta. */
  font-size: 17px;
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  /* Los nombres largos parten en varias líneas en vez de desbordar. */
  overflow-wrap: break-word;
  /* Reserva dos renglones siempre: es lo que ocupa el nombre más largo de la
     grilla ("Full body y movilidad") y evita que la altura del título cambie
     de una tarjeta a otra. */
  min-height: calc(2em * var(--lh-heading));
}
.fr-clase__hora {
  color: var(--accent);
  font-size: var(--text-body-sm);
  font-weight: 700;
  margin-top: 2px;
}
.fr-clase__fila {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-2);
  font-size: var(--text-body-sm);
}
.fr-clase__fila dt { color: var(--text-secondary); }
.fr-clase__fila dd { margin: 0; font-weight: 700; }
.fr-clase__cupos { margin-top: var(--space-2); font-size: var(--text-body-sm); color: var(--text-secondary); }

/* Aviso de día sin clases. Va en letra chica a propósito: es un estado, no un
   mensaje para leer, y en escritorio comparte ancho con una columna angosta. */
.fr-calendario__vacio {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--text-secondary);
  font-size: var(--text-caption);
  line-height: var(--lh-body);
  padding: var(--space-4) 0;
}

/* Flecha circular reutilizable. Es una clase propia y no la del carrusel:
   aquélla se oculta en móvil, que es justo donde estas flechas hacen falta. */
.fr-flecha {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--accent);
  background: none;
  color: var(--republic-white);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--dur-rapida) var(--ease-suave);
}
.fr-flecha:hover { background: var(--concrete-raised); }

@media (max-width: 900px) {
  /* En móvil el diseño cambia de grilla semanal a un solo día navegable, y el
     filtro de actividad baja por debajo del selector de día. */
  .fr-calendario__controles {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
  }
  .fr-calendario__filtro { min-width: 0; order: 2; }
  .fr-calendario__semana { order: 1; justify-content: space-between; }
  .fr-calendario__dias { display: none; }
  .fr-calendario__movil {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
  }
  .fr-calendario__movil .fr-dia-actual {
    flex: 1;
    text-align: center;
    background: var(--accent);
    color: var(--text-on-magenta);
    border-radius: var(--radius-pill);
    padding: 12px;
    font-weight: 700;
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
    font-size: var(--text-body-sm);
  }
  /* En móvil se ve un día por vez: solo la columna elegida con las flechas. */
  .fr-clases { grid-template-columns: 1fr; }
  .fr-clases__col { display: none; }
  .fr-clases__col.is-activa { display: flex; }
  /* Como se navega día por día, repetir el día en cada tarjeta sobra. */
  .fr-clase__fila--dia { display: none; }
  /* La columna oculta se muestra con display: flex, así que el apagado de un día
     pasado tiene que volver a declararlo para no perder contra esa regla. */
  .fr-clases__col.is-activa.is-pasado { display: flex; opacity: 0.6; }
}
@media (min-width: 901px) {
  .fr-calendario__movil { display: none; }
  /* En escritorio las tarjetas van seis por fila y los nombres reales de EVO
     ("Entrenamiento Funcional", "Flex y Movilidad") son más largos que los del
     Figma: a 17px ocupaban tres renglones. En móvil queda en 17px, a ancho completo. */
  .fr-clase__titulo { font-size: 14px; }
}

/* --- Planes -------------------------------------------------------------- */

/* Flex y no grid, y el motivo es el hueco entre tarjetas.

   Con grid todas las columnas miden lo mismo, así que una tarjeta más angosta que su
   columna deja un sobrante a los costados que se lee como hueco: entre una grande y una
   chica había 62 px y entre dos chicas 100, contra los 24 del `gap`. En flex cada
   tarjeta ocupa lo que mide y el hueco es siempre el `gap`.

   Lo que se pierde es la celda compartida con la que el mazo superponía las tarjetas
   apiladas; en su lugar, la escondida va en una caja de ancho cero —ver más abajo—, que
   además es más simple: no hay que adivinar en qué columna cayó la tapa. */
.fr-planes__grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* Todas del mismo alto, a pedido del cliente. `--fr-escala` sigue achicando lo de
     adentro de las que no son del convenio; lo que ya no las achica es la caja.

     El sobrante que eso deja no se reparte: se lo come el margen automático del botón,
     así que los botones de todas las tarjetas quedan a la misma altura y el hueco cae
     entre los beneficios y el botón. Repartido, cada tarjeta separaría sus bloques de
     una manera distinta y la fila se leía desprolija. */
  align-items: stretch;
  /* 48 y no los 24 del resto de las rejillas: en el archivo de diseño el hueco mide 41
     px llevado a nuestra escala, y de los tokens que hay este es el que más se le
     acerca. */
  gap: var(--space-6);
  position: relative;
  /* El ancho de una tarjeta a tamaño completo. Lo pisa `is-ancha` cuando hay tres o
     más, que entonces se reparten el ancho disponible. */
  --fr-tarjeta: 520px;
}
.fr-plan {
  background: var(--surface-card);
  /* Sin esquinas redondeadas, a pedido del cliente. No es solo estética: de este 0
     dependen la cinta de arriba —que se apoya en las dos aristas— y el recorte de la
     tarjeta, así que redondearla otra vez pide revisar las dos cosas. */
  border-radius: 0;
  padding: var(--space-6) var(--space-5);
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  /* El borde ya está en reposo, transparente: si apareciera recién al pasar el
     puntero, la tarjeta daría un salto de 1 px. */
  border: 1px solid transparent;
  /* El cuerpo del importe. De acá salen el tamaño del precio, el alto mínimo de la
     pastilla y su redondeo: así la pastilla acompaña sola al precio cuando cambia de
     tamaño, en vez de quedar con un alto fijo que en el teléfono le sobraba.

     `--fr-escala` es el factor de las tarjetas que no son la del convenio. Va con
     reserva en cada uso y no declarada acá a propósito: declarada en `.fr-plan` le
     ganaría a la que llega heredada del envoltorio, que es quien la sabe. */
  --fr-monto: calc(clamp(28px, 2.5vw, 40px) * var(--fr-escala, 1));
  transition: transform var(--dur-media) var(--ease-out), box-shadow var(--dur-media) var(--ease-out), border-color var(--dur-media) var(--ease-out);
}

/* Al pasar el puntero, la tarjeta se levanta sobre un halo rojo difuso. Sin borde ni
   contorno: el halo alcanza para señalar la tarjeta, y el borde rojo la recortaba
   contra el fondo negro. */
/* El envoltorio también cuenta: los botones de abrir y de plegar el mazo son
   hermanos de la tarjeta, así que sin esto pasar el puntero por la × no encendía
   nada. Y con la tarjeta apilada, que va `inert`, su propio :hover no llega. */
.fr-plan:hover,
.fr-plan:focus-within,
.fr-planes__carta:hover .fr-plan,
.fr-planes__carta:has(.fr-planes__toque:focus-visible) .fr-plan,
.fr-planes__carta:has(.fr-planes__ocultar:focus-visible) .fr-plan {
  transform: translateY(-4px);
  box-shadow: 0 18px 50px -12px rgba(221, 7, 7, 0.55);
}

/* Quien pidió menos movimiento conserva el halo, pero sin el desplazamiento. */
@media (prefers-reduced-motion: reduce) {
  .fr-plan:hover,
  .fr-plan:focus-within,
  .fr-planes__carta:hover .fr-plan { transform: none; }
}
/* La cinta del plan destacado: un bloque apoyado en las dos aristas de arriba de la
   tarjeta, con el logo del banco y el texto a dos renglones.

   Va **en el flujo** y no absoluta como antes, y el cambio tiene motivo: medía 28 px y
   ahora mide el doble, así que fuera del flujo le pasaría por encima al nombre del plan.
   Acá empuja lo que viene abajo, que es lo que hace el diseño. Los márgenes negativos
   son los que la sacan del relleno de la tarjeta para tocar sus bordes.

   Lo único redondeado es la esquina de abajo a la derecha: es lo que la hace leer como
   una cinta y no como otra píldora. Las de arriba siguen la arista de la tarjeta, que
   ahora es recta. */
.fr-plan__cinta {
  /* Sin esto la tarjeta —columna flexible— la estiraría a lo ancho. */
  align-self: flex-start;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  /* Sin margen abajo: la separación con lo que sigue ya la pone el `gap` de la tarjeta. */
  margin: calc(-1 * var(--space-6)) 0 0 calc(-1 * var(--space-5));
  max-width: calc(100% + var(--space-5));
  background: var(--accent);
  color: var(--text-on-magenta);
  padding: var(--space-2) var(--space-5) var(--space-2) var(--space-3);
  border-radius: 0 0 36px 0;
  /* La sombra cae debajo del bloque y lo despega de la tarjeta. */
  box-shadow: 0 12px 28px -10px rgba(0, 0, 0, 0.85);
  font-size: calc(var(--text-body-sm) * var(--fr-escala, 1));
  line-height: 1.25;
  /* El texto de la tarjeta va centrado; el de la cinta, no. Y a diferencia de la cinta
     vieja no va en caja alta: ahora es una frase, no un rótulo. */
  text-align: left;
}
/* El logo entra tal cual: es blanco con la palabra calada, así que sobre el rojo de la
   cinta las letras toman ese rojo. Sin recolorear nada. */
/* El texto va limitado por medida y no por un ancho en píxeles: así cae en dos
   renglones, que es como está en el diseño, y una cinta corta —«El más elegido»— sigue
   entrando en uno solo sin que la caja quede ancha de más. */
.fr-plan__cinta-texto { max-width: 23ch; }
.fr-plan__cinta-logo {
  flex: none;
  width: calc(40px * var(--fr-escala, 1));
  height: calc(40px * var(--fr-escala, 1));
  object-fit: contain;
}
.fr-plan__badge { align-self: center; }
/* La tarjeta que no lleva **ni cinta ni píldora** arrancaba pegada al borde de arriba, y
   al lado de una que sí lleva algo el título quedaba bastante más alto: medido, 57 px
   contra 101. Se le reserva el lugar que ocuparía la píldora —su alto más el hueco de la
   tarjeta—, así las dos quedan parejas.

   Los 28 px son el alto de `.fr-badge`, y van como número porque no hay de dónde
   deducirlos: no es una medida del sistema sino la suma de su relleno y su cuerpo de
   letra. No lo toca `--fr-escala`, así que no cambia entre tarjetas.

   La que lleva cinta queda igual: la cinta ya llena ese espacio, y de hecho más. */
.fr-plan:not(:has(.fr-plan__cinta)):not(:has(.fr-plan__badge)) {
  padding-top: calc(var(--space-6) + 28px + var(--space-3));
}
/* El nombre y su bajada son un solo bloque: la rejilla de la tarjeta separa los bloques
   con 16 px, que entre un título y la línea que lo explica es demasiado. Acá adentro el
   espacio lo pone esta regla. */
.fr-plan__cabecera {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin-top: var(--space-2);
}
.fr-plan__nombre {
  font-family: var(--font-display);
  font-size: calc(var(--text-display-md) * var(--fr-escala, 1));
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
}
/* En blanco y no en gris: en el diseño nuevo la bajada explica de qué plan se trata
   —«pagando con Tarjetas del Banco Itaú»—, y en gris se leía como una nota al pie. */
.fr-plan__claim { color: var(--text-body); font-size: calc(var(--text-body-sm) * var(--fr-escala, 1)); }

/* El precio promocional vive dentro de una pastilla con borde rojo; el de lista va
   suelto debajo. El rojo es el del borde: el importe va en blanco, que es lo que
   hace que la pastilla se lea como una oferta y no como un precio tachado. */
.fr-plan__oferta {
  /* La pastilla se ajusta a lo que tiene adentro y no al ancho de la tarjeta: en el
     diseño queda holgada a los costados. */
  width: fit-content;
  max-width: 100%;
  margin: var(--space-3) auto 0;
  padding: var(--space-2) var(--space-5);
  /* Un alto mínimo atado al importe: así la pastilla de un solo precio y la que lleva
     la leyenda adentro no se leen como dos piezas distintas, y el importe queda
     centrado en la caja en vez de apoyado en el borde de arriba. */
  min-height: calc(var(--fr-monto) * 2);
  display: grid;
  justify-content: center;
  align-items: center;
  /* El rojo de marca a baja opacidad, no un color nuevo: así el relleno sigue al
     borde si alguna vez cambia el acento, y se apoya en el gris de la tarjeta. */
  background: rgba(221, 7, 7, 0.2);
  border: 2px solid var(--accent);
  /* Esquina redondeada, no pastilla. El radio sigue al importe por lo mismo que el
     alto: con un valor fijo, en el teléfono la curva se comía la caja. */
  border-radius: calc(var(--fr-monto) * 0.5);
}
.fr-plan__precio {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: var(--space-2);
}
/* El de lista está medido sobre el diseño, tomando la altura de caja alta del
   nombre del plan como regla: ahí es algo menor que él (28 contra 32 en el
   archivo). El promocional va por encima de lo que marca el archivo, a pedido del
   cliente: en el diseño apenas le saca al título y en pantalla se leía chico. */
.fr-plan__monto {
  font-family: var(--font-display);
  font-size: var(--fr-monto);
  line-height: 1;
  letter-spacing: var(--ls-display);
}
/* El «/mes» acompaña al importe, así que va del mismo color: en gris se despegaba del
   número y parecía una aclaración. */
.fr-plan__periodo {
  color: var(--text-body);
  font-size: calc(var(--text-body-sm) * var(--fr-escala, 1));
  align-self: flex-end;
}
.fr-plan__lista-precio {
  font-family: var(--font-display);
  font-size: calc(clamp(21px, 1.95vw, 28px) * var(--fr-escala, 1));
  line-height: 1;
  letter-spacing: var(--ls-display);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: var(--space-2);
}

.fr-plan__beneficios {
  display: grid;
  gap: var(--space-3);
  text-align: left;
  border-top: 1px solid var(--steel);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  font-size: calc(var(--text-body-sm) * var(--fr-escala, 1));
}
.fr-plan__beneficios li { display: flex; align-items: flex-start; gap: var(--space-3); }
.fr-plan__beneficios svg { flex-shrink: 0; color: var(--success); margin-top: 3px; }
/* `auto` y no una medida: se come el sobrante de las tarjetas que quedaron más cortas
   que la más alta, y así el botón —y la nota que lo sigue— quedan abajo de todo. La
   separación de siempre con los beneficios pasa al relleno, que `auto` no respeta. */
.fr-plan__cta { margin-top: auto; padding-top: var(--space-4); }
/* El logo del banco dentro del botón, donde antes iba al lado del precio. La separación
   con el texto la pone el `gap` que ya trae `.fr-btn`. */
.fr-plan__cta-logo {
  flex: none;
  width: 24px;
  height: 24px;
  object-fit: contain;
}
.fr-plan__nota { color: var(--text-secondary); font-size: var(--text-caption); }
/* Leyenda debajo de un precio ("Utilizando Tarjetas del Banco Itaú"). Va pegada al
   importe, no al bloque siguiente. */
.fr-plan__precio-nota {
  color: var(--text-secondary);
  font-size: calc(var(--text-body-sm) * var(--fr-escala, 1));
  margin-top: calc(-1 * var(--space-2));
}
/* --- Las tarjetas que no son la del convenio ------------------------------- */

/* Más chicas que la del banco, en el porcentaje que diga el panel. El factor llega como
   `--fr-escala` en el `style` del envoltorio y de ahí lo heredan todas las reglas de
   arriba, que lo usan con reserva de 1.

   Se achica el ancho y los cuerpos de letra, no con `transform: scale()`: escalada, la
   caja seguiría midiendo lo de antes —dejando un hueco— y el texto se vería borroso en
   las pantallas que no son de alta densidad. */
/* Los cuerpos de letra los resuelve `--fr-escala`, que llega en el `style` del
   envoltorio. En vertical no hace falta nada: desde que todas miden lo mismo de alto, no
   hay de qué colgarla. Llegó a tener `align-self: center`, y eso ahora le devolvería su
   alto natural —`align-self` le gana al `stretch` de la fila—. */
/* --- Una tarjeta con un solo precio --------------------------------------- */

/* Cuando el único precio es el que NO pasa por el banco, es ese el que toma la
   pastilla: el recuadro dice «este es el precio», y lo que cambia es el color. El
   gris sale de la misma receta que el rojo —el color de marca al 20 % sobre el gris
   de la tarjeta—, así que las dos pastillas pesan lo mismo y solo se distinguen por
   el tono. */
.fr-plan__oferta--sin-banco {
  background: rgba(242, 242, 242, 0.2);
  border-color: var(--republic-silver);
}

/* Con un solo precio la leyenda sale de la pastilla. Adentro estiraba el recuadro a
   lo ancho de la tarjeta —no hay un segundo precio del que distinguirse— y el importe
   perdía su marco; afuera queda sola, centrada bajo el importe. */
/* La leyenda suelta es la línea bajo el precio —«Plan de 12 meses»—, y en el diseño
   nuevo es algo más grande que los beneficios, no más chica. Medido sobre el archivo que
   mandaron: su caja alta le saca un 8 % a la de la lista de beneficios. */
.fr-plan__precio-nota--suelta {
  text-align: center;
  font-size: calc(var(--text-body-md) * var(--fr-escala, 1));
}
/* Dentro de la pastilla la leyenda no necesita trepar: el relleno ya la deja pegada al
   importe. (Hasta el rediseño llevaba el logo del banco al lado; ahora el logo vive en la
   cinta de arriba y en el botón, y acá quedó sola.) */
.fr-plan__oferta .fr-plan__precio-nota {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

/* Aclaraciones al pie de los beneficios: cuelgan de la lista, alineadas con su
   texto y sin tilde, porque no son beneficios sino condiciones del último. */
.fr-plan__aclaraciones {
  list-style: none;
  display: grid;
  gap: var(--space-1);
  text-align: left;
  margin-top: calc(-1 * var(--space-2));
  padding-left: calc(16px + var(--space-3));
  color: var(--text-secondary);
  font-size: var(--text-caption);
}
/* Lo que se muestra cuando el plan todavía no tiene precio: un candado grande donde
   iría el importe y, debajo, la línea que explica. El candado ocupa el lugar del
   precio para que las dos tarjetas se lean parejas, y el texto va chico y gris
   porque es un aviso, no una oferta. */
.fr-plan__sin-precio {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
  color: var(--text-secondary);
  font-size: var(--text-body-sm);
}
.fr-plan__candado { width: auto; height: 120px; flex-shrink: 0; }

@media (max-width: 600px) {
  .fr-plan__candado { height: 96px; }
}

/* --- Mazo de planes ------------------------------------------------------- */

/* El envoltorio existe para que el apilado no pise ni el hover de la tarjeta
   (transform) ni su animación de entrada (translate/scale): cada cosa anima su
   propia capa. */
/* El envoltorio existe para que el apilado no pise ni el hover de la tarjeta
   (transform) ni su animación de entrada (translate/scale): cada cosa anima su propia
   capa. Y es el que lleva el ancho: `--fr-escala` llega en su `style` cuando la tarjeta
   no es la del convenio, y vale 1 en todas las demás. */
.fr-planes__carta {
  position: relative;
  display: flex;
  flex: none;
  /* Todas del mismo ancho. `--fr-escala` —que llega en el `style` de las que no son del
     convenio— no toca esta medida: manda sobre el cuerpo de letra, y la tarjeta queda
     más baja sola, porque el mismo texto ocupa menos. Con anchos distintos el hueco
     entre tarjetas se desnivelaba: la más angosta dejaba un sobrante a los costados que
     se sumaba al `gap`. */
  width: var(--fr-tarjeta);
  /* En pantallas angostas la tarjeta no puede ser más ancha que la fila. */
  max-width: 100%;
}
.fr-planes__carta > .fr-plan { flex: 1; }

/* Estado apilado. La clase is-mazo la pone planes.js: sin JavaScript no habría forma
   de abrir nada, así que sin él se ven todas las tarjetas, una al lado de la otra.

   El estado vive en cada tarjeta escondida y no en la grilla: con varias apiladas,
   cada una se abre y se cierra por su cuenta. En qué celda se esconde —la de la
   última tarjeta a la venta, la que lleva `--tapa`— lo decide planes.js, que es el
   único que sabe cuántas columnas armó la grilla adaptativa. */
/* De tres planes en adelante las tarjetas se reparten el ancho disponible, porque tres
   de 520 no entran en el contenedor de 1280 y quedarían dos arriba y una sola abajo. El
   piso de 320 px es lo que mide una tarjeta sin que el precio y los beneficios se
   amontonen; el techo es el ancho de siempre, para que con mucho lugar no se estiren más
   de la cuenta. */
.fr-planes__grid.is-ancha {
  --fr-tarjeta: clamp(320px, calc((100% - var(--space-6) * 2) / 3), 520px);
}

/* La tarjeta escondida, encima de la tapa.

   El truco es la caja de **ancho cero**: el envoltorio sigue en la fila, justo después
   de la tapa —es su lugar en el documento— pero no ocupa nada, y el margen negativo le
   come su parte del hueco. La tarjeta de adentro se dibuja fuera de esa caja, pegada a
   su borde derecho, así que cae exactamente encima de la que quedó antes. Sin medir
   nada y sin importar en qué fila terminó la tapa.

   Asoma por el costado, no por abajo: es lo que se ve en el diseño y así la sección no
   crece de alto. El desplazamiento crece con el orden por si algún día hay más de una
   tarjeta atrás. */
.fr-planes__grid.is-mazo .fr-planes__carta--atras:not(.is-abierto) {
  /* Se corre hacia atrás todo su ancho **más el hueco**, así que su aporte a la fila es
     cero y queda exactamente encima de la tapa. Sigue siendo una caja normal del flujo:
     eso es lo que mantiene vivo el `100%` de su ancho y deja que el `translate` del
     abanico se aplique como siempre. */
  margin-left: calc(-1 * (var(--fr-tarjeta) + var(--space-6)));
  translate: calc(150px * var(--fr-orden, 1)) 0;
  scale: 0.96;
  /* Cuanto más atrás, más abajo: si no, la segunda escondida se dibuja encima de la
     primera y tapa justo la franja que la primera tenía que mostrar. Con esto las
     dos asoman, una detrás de la otra, como un mazo abierto en abanico. */
  z-index: calc(20 - var(--fr-orden, 1));
}
/* Sombra de la tarjeta de adelante hacia la derecha: es lo que hace leer el
   desfase como dos cartas apiladas y no como un borde. */
.fr-planes__grid.is-mazo:has(.fr-planes__carta--atras:not(.is-abierto)) .fr-planes__carta--tapa .fr-plan {
  box-shadow: 24px 0 48px -18px rgba(0, 0, 0, 0.95);
}
.fr-planes__grid.is-mazo .fr-planes__carta--tapa { z-index: 20; }

/* El abanico asoma hacia afuera de la grilla, que entre 1000 y 1150 px ya ocupa todo
   el ancho: sin esto la última escondida se va de la pantalla y aparece una barra
   horizontal. Se recorta en la sección y no se reserva sitio en la grilla, porque
   reservarlo angostaría las tarjetas en todos los anchos para arreglar una franja.
   `clip` y no `hidden`: `hidden` convertiría la sección en contenedor de scroll y se
   llevaría puesto el `position: sticky` de lo que venga arriba. */
.fr-planes { overflow-x: clip; }


/* Botón del tamaño de la tarjeta: la de atrás se toca en cualquier punto. Solo
   existe mientras está apilada. */
.fr-planes__toque {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  border: 0;
  padding: 0;
  background: none;
  border-radius: 20px;
  cursor: pointer;
  display: none;
}
.fr-planes__grid.is-mazo .fr-planes__carta--atras:not(.is-abierto) .fr-planes__toque { display: block; }
/* Aviso de la franja de abajo en el teléfono. Oculto por defecto: solo lo muestra la
   regla del media query cuando la apilada asoma por abajo. */
.fr-planes__aviso {
  position: absolute;
  right: var(--space-3);
  bottom: 20px;
  display: none;
  align-items: center;
  border-radius: var(--radius-pill);
  /* Discreto: es una referencia de lo que hay detrás, no un llamado a la acción. */
  background: var(--concrete-raised);
  color: var(--text-secondary);
  padding: 4px 12px;
  font-size: var(--text-caption);
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  white-space: nowrap;
}

/* El anillo de foco se dibuja alrededor de la tarjeta y no del botón invisible,
   como en las clases del calendario. */
.fr-planes__toque:focus-visible,
.fr-planes__ocultar:focus-visible { outline: none; }
.fr-planes__carta:has(.fr-planes__toque:focus-visible) .fr-plan { box-shadow: var(--focus-ring); }

/* La tarjeta apilada se ve apagada, como una carta que todavía no está en juego.
   Al pasar el puntero se enciende un poco: es lo que avisa que se puede tocar,
   ahora que no lleva rótulo. */
/* El apagado va en la tarjeta y no en el envoltorio: así el aviso de la franja, que
   es hermano de la tarjeta, se lee a brillo pleno. */
.fr-planes__grid.is-mazo .fr-planes__carta--atras:not(.is-abierto) .fr-plan {
  filter: brightness(0.6);
  transition: filter var(--dur-media) var(--ease-suave);
}
.fr-planes__grid.is-mazo .fr-planes__carta--atras:not(.is-abierto):hover .fr-plan,
.fr-planes__grid.is-mazo .fr-planes__carta--atras:not(.is-abierto):has(.fr-planes__toque:focus-visible) .fr-plan {
  filter: brightness(0.9);
}

/* Botón de plegar, visible solo con el mazo abierto. Va dentro de la tarjeta, así
   que se posiciona contra ella y la acompaña cuando se levanta en el hover. */
.fr-plan { position: relative; }
.fr-planes__ocultar {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  z-index: 2;
  width: 34px;
  height: 34px;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color var(--dur-rapida) var(--ease-suave), background var(--dur-rapida) var(--ease-suave);
}
.fr-planes__grid.is-mazo .fr-planes__carta--atras.is-abierto .fr-planes__ocultar { display: inline-flex; }
.fr-planes__ocultar:hover { color: var(--republic-white); background: var(--concrete-raised); }
.fr-planes__ocultar:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.fr-planes__grid.is-mazo .fr-planes__carta--atras:not(.is-abierto) .fr-plan { border-color: transparent; }

/* Abanico vertical. Lo enciende planes.js con `is-vertical`: en el teléfono siempre,
   porque a lo ancho no hay lugar y el gesto natural es desplegar hacia abajo, y en
   escritorio solo cuando el abanico no entra al costado ni bajando la tapa de fila
   —muchos planes a la venta y muchos apilados a la vez—. El alto es la dimensión que
   siempre sobra, así que acá no hay nada que medir: se reserva y listo.

   Por arriba o por abajo, según lo elegido en el panel (Portada › Planes). */
/* En vertical cambia **solo la dirección**: la caja sigue corrida hacia atrás por el
   margen negativo, así que sigue encima de la tapa y en la misma fila. Lo único distinto
   es hacia dónde asoma. */
.fr-planes__grid.is-mazo.is-vertical.is-asoma-arriba .fr-planes__carta--atras:not(.is-abierto) {
  /* Apoyada en el borde por el que asoma, no centrada: una tarjeta más corta que la tapa
     —la de «Próximamente», por ejemplo— quedaría enteramente escondida detrás de ella. */
  align-self: flex-start;
  translate: 0 calc(-72px * var(--fr-orden, 1));
}
.fr-planes__grid.is-mazo.is-vertical.is-asoma-arriba { padding-top: calc(72px * var(--fr-atras, 0)); }
.fr-planes__grid.is-mazo.is-vertical.is-asoma-arriba:has(.fr-planes__carta--atras:not(.is-abierto)) .fr-planes__carta--tapa .fr-plan {
  box-shadow: 0 -24px 48px -18px rgba(0, 0, 0, 0.95);
}

.fr-planes__grid.is-mazo.is-vertical.is-asoma-abajo .fr-planes__carta--atras:not(.is-abierto) {
  align-self: flex-end;
  translate: 0 calc(72px * var(--fr-orden, 1));
}
.fr-planes__grid.is-mazo.is-vertical.is-asoma-abajo { padding-bottom: calc(72px * var(--fr-atras, 0)); }
.fr-planes__grid.is-mazo.is-vertical.is-asoma-abajo:has(.fr-planes__carta--atras:not(.is-abierto)) .fr-planes__carta--tapa .fr-plan {
  box-shadow: 0 24px 48px -18px rgba(0, 0, 0, 0.95);
}
/* Asomando por abajo, la franja que se ve es un borde vacío: el aviso dice de qué
   tarjeta es. Va en la esquina, dentro de la franja. */
.fr-planes__grid.is-mazo.is-vertical .fr-planes__carta--atras:not(.is-abierto) .fr-planes__aviso { display: inline-flex; }
@media (max-width: 900px) {
  /* Una sola tarjeta por fila. Se nombra también `.is-ancha` porque un media query no
     suma especificidad y si no, en tablet terminaban dos. */
  .fr-planes__grid,
  .fr-planes__grid.is-ancha { --fr-tarjeta: 100%; }
  .fr-plan__cta .fr-btn { width: 100%; }

}

/* --- Galería (carrusel con avance automático) ---------------------------- */

.fr-galeria {
  overflow: hidden;
  /* El alto manda: el ancho de cada foto sale de su propia proporción. Cambiando esto
     se reescala la tira entera sin tocar ninguna otra medida. */
  --fr-galeria-alto: clamp(220px, 24vw, 330px);
  /* El aire de arriba y abajo. Tiene que ser mayor que el desnivel de las fotos pares,
     o se les recorta el borde. */
  --fr-galeria-aire: 32px;
}

/* La cinta sobresale del contenedor a propósito: en el diseño las fotos llegan
   hasta los bordes de la pantalla, no se detienen en la caja de 1280.
   El relleno vertical evita que las fotos elevadas se corten contra el borde del
   recorte; el margen negativo lo compensa para no alterar la altura de la sección.
   La pista lleva el mismo par por su cuenta: desde que es ella la que scrollea,
   también recorta. */
.fr-galeria__viewport {
  overflow: hidden;
  padding-inline: var(--gutter);
  padding-block: var(--fr-galeria-aire);
  margin-block: calc(var(--fr-galeria-aire) * -1);
}

.fr-galeria__track {
  display: flex;
  gap: 0;
  /* Respaldo para cuando la cinta no arranca —«reducir movimiento», o el JS que no
     llegó—: la fila queda quieta pero se desliza con el dedo. Sin esto quedaba
     recortada por el viewport y sin ninguna forma de llegar a las fotos.
     Ya no lleva `will-change`: era redundante y promovía una capa enorme. */
  overflow-x: auto;
  scrollbar-width: none;
  /* El aire para el desnivel va acá y no solo en el viewport: desde que la pista es la
     que scrollea, su `overflow-x` hace que el vertical también compute a `auto`, así que
     esta caja recorta. Sin este relleno, la foto levantada perdía su borde rojo de
     arriba. El margen negativo lo compensa para no mover la altura de la sección. */
  padding-block: var(--fr-galeria-aire);
  margin-block: calc(var(--fr-galeria-aire) * -1);
}
.fr-galeria__track::-webkit-scrollbar { display: none; }

/* Cada foto conserva su proporción, que llega como `--fr-ratio` desde el PHP leyendo el
   archivo. Con el alto fijo, el ancho sale de esa proporción: una vertical queda angosta
   y una apaisada ancha, en lugar de recortarlas todas al mismo rectángulo. El 4/3 es por
   si no se pudo medir el archivo. */
.fr-galeria__item {
  margin: 0 var(--space-5) 0 0;
  flex: 0 0 auto;
  height: var(--fr-galeria-alto);
  aspect-ratio: var(--fr-ratio, 4 / 3);
  border: 1px solid var(--accent);
  border-radius: var(--radius-card);
  overflow: hidden;
}
/* `object-fit: cover` casi no recorta nada, porque la caja ya tiene la proporción del
   archivo; está para absorber el redondeo de un píxel. */
.fr-galeria__item img { width: 100%; height: 100%; object-fit: cover; }

/* Alturas alternadas, como en el Figma. */
.fr-galeria__item:nth-child(even) { transform: translateY(calc(var(--fr-galeria-aire) * -0.75)); }

@media (max-width: 900px) {
  /* En el teléfono también se alternan y también cambian de ancho: antes acá todas
     quedaban del mismo tamaño y a la misma altura, que es justo lo que las volvía una
     tira plana. Solo se achica la escala. */
  .fr-galeria { --fr-galeria-alto: clamp(170px, 46vw, 240px); --fr-galeria-aire: 20px; }
  .fr-galeria__item { margin-right: var(--space-3); }
}

/* --- Cinta continua ------------------------------------------------------- */

/* La comparten la galería del final y el carrusel de la sala. cinta.js arma la
   fila con el tramo duplicado y le pone esta clase. La cinta se desplaza la mitad
   exacta de su ancho y vuelve a empezar: en ese punto la segunda mitad ocupa el
   lugar de la primera y no se nota la costura. Por eso la separación entre fotos
   va como margen de cada una y no como gap: con gap la cinta tendría un hueco
   menos que fotos y la mitad no caería justo en una costura. */
.fr-cinta {
  /* El desplazamiento es scroll nativo: lo mueve `cinta.js` avanzando el `scrollLeft`.
     Antes esto era `width: max-content` más una animación de CSS, y en iPhone esa
     versión tardaba en aparecer mientras que esta no. La barra se esconde, pero el
     gesto del dedo sigue funcionando y eso es a propósito. */
  overflow-x: auto;
  scrollbar-width: none;
  /* `base.css` pone `scroll-behavior: smooth` para todo el documento, y acá pelearía
     con el `scrollLeft` que se escribe en cada cuadro. */
  scroll-behavior: auto;
}
.fr-cinta::-webkit-scrollbar { display: none; }

/* --- Ficha de la clase ---------------------------------------------------- */

/* La tarjeta entera abre la ficha. El botón va encima, transparente y del tamaño
   de la tarjeta: así el clic funciona en cualquier punto y el teclado la alcanza,
   sin envolver párrafos y listas dentro de un <button>, que no es marcado válido. */
.fr-clase { position: relative; }
.fr-clase__abrir {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  border: 0;
  padding: 0;
  background: none;
  cursor: pointer;
  border-radius: inherit;
}
.fr-clase:has(.fr-clase__abrir:focus-visible) { box-shadow: var(--focus-ring); }
.fr-clase__abrir:focus-visible { outline: none; box-shadow: none; }

.fr-modal {
  width: min(560px, calc(100vw - 2 * var(--gutter)));
  max-height: calc(100vh - 2 * var(--space-5));
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-body);
  overflow: visible;
}
.fr-modal::backdrop {
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(3px);
}
.fr-modal__caja {
  position: relative;
  background: var(--surface-card);
  border-radius: 20px;
  /* La línea roja de arriba es la misma marca que el borde izquierdo de la tarjeta. */
  border-top: 3px solid var(--accent);
  padding: var(--space-5);
  max-height: inherit;
  overflow-y: auto;
  box-shadow: 0 40px 80px -30px rgba(0, 0, 0, 0.95);
}
.fr-modal__cerrar {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color var(--dur-rapida) var(--ease-suave), background var(--dur-rapida) var(--ease-suave);
}
.fr-modal__cerrar:hover { color: var(--republic-white); background: var(--concrete-raised); }

.fr-modal__titulo {
  font-family: var(--font-display);
  font-size: var(--text-display-md);
  line-height: var(--lh-display-md);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  /* Deja lugar al botón de cerrar aunque el nombre ocupe dos renglones. */
  padding-right: var(--space-6);
}
.fr-modal__desc {
  margin-top: var(--space-3);
  color: var(--text-secondary);
  font-size: var(--text-body-sm);
  line-height: var(--lh-body);
  text-wrap: pretty;
}
.fr-modal__desc:empty { display: none; }

.fr-modal__datos {
  margin: var(--space-5) 0 0;
  display: grid;
  gap: var(--space-2);
  font-size: var(--text-body-sm);
}
.fr-modal__fila {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
}
.fr-modal__fila dt { color: var(--text-secondary); }
.fr-modal__fila dd { margin: 0; font-weight: 700; text-align: right; }

.fr-modal__cupos { margin-top: var(--space-5); }
.fr-modal__cupos-linea {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  font-size: var(--text-body-sm);
  color: var(--text-secondary);
}
.fr-modal__cupos-linea strong { color: var(--text-body); }
/* Barra de ocupación: vacía = clase libre, llena = clase completa. */
.fr-modal__barra {
  margin-top: var(--space-2);
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--concrete-raised);
  overflow: hidden;
}
.fr-modal__barra span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--accent);
  transition: width var(--dur-larga) var(--ease-out);
}

@media (max-width: 600px) {
  .fr-modal__caja { padding: var(--space-4); }
  .fr-modal__titulo { font-size: var(--text-heading); }
}

/* ============================================================
   Sala de musculación
   ============================================================ */

/* Cinta continua, igual que la galería del final: la fila corre sola y llega hasta
   los bordes de la pantalla. Sin JavaScript queda una fila de fotos que se arrastra
   con el dedo. La separación va como margen de cada foto y no como gap: ver el
   comentario de .fr-cinta. */
/* El aire vertical es lo que evita que el "levantar" de una foto y su sombra se
   corten contra el borde del recorte; el margen negativo lo compensa para no
   alterar la altura de la sección. */
.fr-sala__carrusel {
  position: relative;
  overflow: hidden;
  padding-inline: var(--gutter);
  padding-block: 24px;
  margin-block: -24px;
}
.fr-sala__track {
  display: flex;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  /* El scroll es la técnica, no un respaldo: lo mueve `cinta.js` avanzando el
     `scrollLeft`. Antes había una línea que le devolvía `overflow-x: visible` en cuanto
     la cinta arrancaba —tenía sentido cuando el movimiento era una animación de CSS—, y
     ahora sería justo lo contrario de lo que hace falta.
     Tampoco lleva `will-change`: era redundante y promovía una capa enorme. */
  overflow-x: auto;
  scrollbar-width: none;
}
.fr-sala__track::-webkit-scrollbar { display: none; }
.fr-sala__item {
  flex: 0 0 clamp(240px, 30vw, 420px);
  margin-right: var(--space-5);
}
.fr-sala__foto {
  display: block;
  position: relative;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--surface-card);
  cursor: zoom-in;
  transition:
    transform var(--dur-larga) var(--ease-out),
    box-shadow var(--dur-larga) var(--ease-out);
}
.fr-sala__foto img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: scale var(--dur-larga) var(--ease-out);
}
/* Sin <dialog> la foto no se agranda: se saca la promesa de clic. */
.fr-sala__foto.is-plana { cursor: default; }
.fr-sala__foto.is-plana .fr-sala__lupa { display: none; }

@media (hover: hover) {
  .fr-sala__foto:hover {
    transform: translateY(-4px);
    box-shadow: 0 24px 40px -22px rgba(0, 0, 0, 0.95);
  }
  .fr-sala__foto:hover img { scale: 1.04; }
  .fr-sala__foto:hover .fr-sala__lupa { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .fr-sala__foto:hover { transform: none; }
  .fr-sala__foto:hover img { scale: 1; }
}
.fr-sala__foto:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* Lupa: marca que la foto se agranda. En pantallas táctiles queda siempre
   visible, porque ahí no hay hover que la revele. */
.fr-sala__lupa {
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.6);
  color: var(--republic-white);
  opacity: 1;
  transition: opacity var(--dur-media) var(--ease-suave);
}
@media (hover: hover) {
  .fr-sala__lupa { opacity: 0; }
}

@media (max-width: 900px) {
  .fr-sala__item {
    flex-basis: min(78vw, 320px);
    margin-right: var(--space-3);
  }
}

/* Visor: la foto a pantalla casi completa, sobre fondo oscuro. */
.fr-visor {
  width: min(1100px, calc(100vw - 2 * var(--gutter)));
  max-width: none;
  max-height: calc(100vh - 2 * var(--space-5));
  padding: 0;
  border: 0;
  background: none;
  overflow: visible;
}
.fr-visor::backdrop {
  background: rgba(0, 0, 0, 0.88);
  backdrop-filter: blur(4px);
}
.fr-visor img {
  display: block;
  width: 100%;
  max-height: calc(100vh - 2 * var(--space-5));
  object-fit: contain;
  border-radius: var(--radius-card);
}
.fr-visor__cerrar {
  position: absolute;
  top: -44px;
  right: 0;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--republic-white);
  cursor: pointer;
}
.fr-visor__cerrar:hover { background: var(--concrete-raised); }
.fr-visor__nav {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  background: rgba(0, 0, 0, 0.72);
}
.fr-visor__nav--prev { left: -20px; }
.fr-visor__nav--next { right: -20px; }

@media (max-width: 700px) {
  /* En móvil las flechas no caben fuera de la foto: van encima, en el borde. */
  .fr-visor__nav--prev { left: 8px; }
  .fr-visor__nav--next { right: 8px; }
  .fr-visor__cerrar { top: -40px; }
}

/* ============================================================
   Preguntas frecuentes
   ============================================================ */

.fr-faq__inner { max-width: 820px; }
.fr-faq__lista { display: grid; gap: var(--space-3); }
.fr-faq__item {
  background: var(--surface-card);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-card);
  padding: var(--space-4);
  transition: background var(--dur-larga) var(--ease-out);
}
@media (hover: hover) {
  .fr-faq__item:hover { background: var(--surface-raised); }
}
.fr-faq__pregunta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  cursor: pointer;
  font-weight: 700;
  line-height: var(--lh-body);
  /* El triángulo por defecto del navegador se reemplaza por el signo de la derecha. */
  list-style: none;
}
.fr-faq__pregunta::-webkit-details-marker { display: none; }
.fr-faq__item:has(.fr-faq__pregunta:focus-visible) { box-shadow: var(--focus-ring); }
.fr-faq__pregunta:focus-visible { outline: none; }

/* Signo "+" dibujado con dos líneas: al abrir, la vertical se apaga y queda un "−". */
.fr-faq__signo {
  position: relative;
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
}
.fr-faq__signo::before,
.fr-faq__signo::after {
  content: '';
  position: absolute;
  inset: 50% 0 auto 0;
  height: 2px;
  background: var(--accent);
  transition: opacity var(--dur-media) var(--ease-suave), rotate var(--dur-media) var(--ease-suave);
}
.fr-faq__signo::after { rotate: 90deg; }
.fr-faq__item[open] .fr-faq__signo::after { opacity: 0; rotate: 0deg; }
/* Mientras se cierra sigue abierto (faq.js anima la altura), pero el signo vuelve
   a "+" desde el principio para acompañar el movimiento. */
.fr-faq__item.is-cerrando .fr-faq__signo::after { opacity: 1; rotate: 90deg; }

/* El aire de arriba va como padding y no como margen: la animación de apertura
   anima la altura, y un margen quedaría afuera y se vería como un salto. */
.fr-faq__respuesta {
  padding-top: var(--space-3);
  overflow: hidden;
  color: var(--text-secondary);
  font-size: var(--text-body-sm);
  line-height: var(--lh-body);
  text-wrap: pretty;
}
.fr-faq__respuesta p { margin: 0 0 var(--space-2); }
.fr-faq__respuesta p:last-child { margin-bottom: 0; }
.fr-faq__respuesta a { color: var(--accent); text-decoration: underline; }
