/*
Copyright (c) 2026 Miguel Urquiola Fernández
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*
  Fuente única para toda la web: Poppins.
  Pesos cargados:
  - 300: texto de cuerpo
  - 400: texto normal
  - 500: menú de navegación
  - 700: títulos de series
  - 800: logotipo de marca (máximo disponible en Poppins)
*/
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,700;0,800;1,300;1,400&display=swap");

/*
  Este archivo controla el aspecto visual de la web.
*/

:root {
  --color-page: #101010;
  --color-text: #f5f5f5;
  --color-muted: #9a9a9a;
  --color-soft: #1f1f1f;
  --color-line: #333333;

  --page-gutter: clamp(22px, 3vw, 42px);
  --grid-gap: clamp(3px, 0.4vw, 6px);
  --header-top: clamp(28px, 4vw, 46px);

  /*
    Una sola familia tipográfica en toda la web.
    --font-main se usa en body, títulos y textos.
    --font-nav se mantiene como alias por si en el futuro se quiere diferenciar.
  */
  --font-main: "Poppins", "Helvetica Neue", Arial, sans-serif;
  --font-nav: "Poppins", "Helvetica Neue", Arial, sans-serif;
}

/*
  border-box hace que los anchos sean mas faciles de entender.
*/
* {
  box-sizing: border-box;
}

/*
  Reset de márgenes en headings.
  Los navegadores aplican margin-top por defecto a h1-h6 (normalmente 0.67em).
  Sin este reset, el h1 de info.html tiene más espacio encima que el h2 de series.
*/
h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
}

/*
  Desplazamiento suave al pulsar enlaces del menu.
*/
html {
  scroll-behavior: smooth;
}

/*
  SCROLLBAR
  ---------------------------------------------------------------------------
  En escritorio: la scrollbar se muestra siempre para evitar el "layout
  shift" que ocurre cuando aparece/desaparece (el contenido se mueve
  para hacerle hueco, típicamente 15-17px en Windows). scrollbar-gutter:
  stable reserva ese espacio permanentemente, exista o no la scrollbar.
  El comportamiento auto-ocultable se desactiva en escritorio.

  En móvil: los navegadores usan scrollbars "overlay" (flotan sobre el
  contenido sin ocupar espacio), así que no hay layout shift. Se mantiene
  el sistema auto-ocultable via scroll-top.js + clase "is-scrolling".

  Chrome / Safari / Edge: ::-webkit-scrollbar
  Firefox: scrollbar-width + scrollbar-color
*/

/* Escritorio: scrollbar siempre visible, espacio siempre reservado */
html {
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.35) transparent;
}

html::-webkit-scrollbar {
  width: 8px;
}

html::-webkit-scrollbar-track {
  background: transparent;
}

html::-webkit-scrollbar-thumb {
  background-color: rgba(255, 255, 255, 0.35);
  border-radius: 4px;
}

html::-webkit-scrollbar-thumb:hover {
  background-color: rgba(255, 255, 255, 0.55);
}

/* Móvil: scrollbar overlay, sin layout shift — auto-ocultable */
@media (max-width: 900px) {
  html {
    scrollbar-gutter: auto;
    scrollbar-width: none;
  }

  html::-webkit-scrollbar {
    width: 0px;
  }

  html.is-scrolling {
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, 0.35) transparent;
  }

  html.is-scrolling::-webkit-scrollbar {
    width: 8px;
  }

  html.is-scrolling::-webkit-scrollbar-track {
    background: transparent;
  }

  html.is-scrolling::-webkit-scrollbar-thumb {
    background-color: rgba(255, 255, 255, 0.35);
    border-radius: 4px;
  }

  html.is-scrolling::-webkit-scrollbar-thumb:hover {
    background-color: rgba(255, 255, 255, 0.55);
  }
}

/*
  Estilo base del cuerpo de la pagina.
  Dejamos margen cero para controlar nosotros todo el espacio.
*/
body {
  margin: 0;
  background: var(--color-page);
  color: var(--color-text);
  font-family: var(--font-main);
  font-size: 16px;
  line-height: 1.5;
  text-rendering: optimizeLegibility;
  /* Flex column: header arriba, main ocupa el resto */
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

/*
  Contenido principal.
  flex: 1 hace que ocupe todo el espacio disponible bajo el header.
*/
main {
  flex: 1;
}

/*
  Diseño fijo sin scroll para PC en Inicio e Info.
  El body ocupa toda la altura del viewport y usa flexbox para
  distribuir header y main verticalmente.
*/
body.page-home,
body.page-info {
  height: 100vh;
  /*
    calc(var(--vh-movil) * 100) en vez de 100dvh: --vh-movil la calcula
    scroll-top.js con JS (window.innerHeight), no depende de que el
    navegador implemente bien la unidad dvh — algunos navegadores
    móviles no la recalculan de forma fiable cuando la barra de
    direcciones se contrae/expande al hacer scroll, lo que puede
    descolocar elementos position:sticky/fixed dentro. El respaldo
    (1vh) es para el instante antes de que el JS se ejecute, o si
    estuviera deshabilitado.
  */
  height: calc(var(--vh-movil, 1vh) * 100);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/*
  El contenido principal ocupa el espacio disponible bajo el header.
*/
body.page-home main,
body.page-info main {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/*
  La sección de galería ocupa todo el espacio disponible del main.
*/
body.page-home .gallery-section {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/*
  Ocultar botón scroll-top en PC en páginas sin scroll (Inicio e Info).
*/
body.page-home .scroll-top,
body.page-info .scroll-top {
  display: none;
}

/*
  Cuando el visor de foto esta abierto, fijamos el body por completo.
  overflow: hidden por sí solo no es fiable en todos los móviles (sobre todo
  iOS Safari) si algo dentro del visor cambia de tamaño momentáneamente
  durante la decodificación de una imagen — puede disparar brevemente la
  barra de scroll del documento. position: fixed elimina esa posibilidad
  por completo: el documento no puede generar scroll bajo ninguna circunstancia.
  script.js guarda el scroll actual antes de fijar y lo restaura al cerrar.
  
  En Series, usamos solo overflow: hidden para evitar conflictos de posicionamiento
  con el visor que tiene position: fixed.
*/
body.is-lightbox-open {
  width: 100%;
  overflow: hidden;
}

/*
  Inicio e Info: el visor usa la técnica top negativo + position:fixed
  (ver script.js, abrirVisor/cerrarVisor). Sin position:fixed aquí,
  "document.body.style.top" que fija el JS no tiene ningún efecto porque
  top solo funciona en elementos posicionados (fixed/absolute/relative).
  Se limita a estas dos páginas porque Series bloquea el scroll de otra
  forma (ver regla siguiente) y no usa el offset de top.
*/
body.page-home.is-lightbox-open,
body.page-info.is-lightbox-open {
  position: fixed;
  left: 0;
  right: 0;
}

/*
  Los enlaces heredan el color del texto para mantener la estetica sobria.
*/
a {
  color: inherit;
}

/*
  Las imagenes nunca deben salirse de su contenedor.
*/
img {
  display: block;
  max-width: 100%;
}

/*
  Enlace visible solo al navegar con teclado.
*/
.skip-link {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 100;
  transform: translateY(-160%);
  background: var(--color-text);
  color: var(--color-page);
  padding: 10px 14px;
  text-decoration: none;
}

.skip-link:focus {
  transform: translateY(0);
}

/*
  Clase para textos que deben existir para lectores de pantalla,
  pero no verse en el diseño.
*/
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/*
  Cabecera: marca a la izquierda, menú a la derecha.
  El div.header-spacer entre brand y nav actúa como 1fr y empuja el nav
  al extremo derecho. Sin ese div, el nav ocuparía la columna 2 inmediatamente
  después del brand y el layout quedaría centrado, no a la derecha.

  NOTA CSP: Google Fonts requiere que en los tres HTML y en _headers estén:
    style-src 'self' https://fonts.googleapis.com;
    font-src 'self' https://fonts.gstatic.com;
*/
/*
  position: sticky en vez de fixed: en Inicio e Info el body nunca
  hace scroll (overflow:hidden en todos los anchos), así que aquí no
  cambia nada visualmente. En Series con contenido largo en escritorio
  (el body sí scrollea ahí), mantiene el header visible arriba del
  todo. background es necesario porque las fotos pasan por debajo
  mientras se hace scroll; sin fondo opaco se verían a través.
  z-index: 30 lo sitúa por encima del contenido normal, pero por
  debajo del botón scroll-top (40) y el visor de fotos (49/50).
*/
.site-header {
  display: grid;
  grid-template-columns: max-content 1fr max-content;
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--color-page);
  align-items: center;
  gap: clamp(16px, 2vw, 32px);
  padding: var(--header-top) var(--page-gutter) 28px;
  /* Línea simple bajo el header. */
  border-bottom: 1px solid var(--color-soft);
}

/*
  Logotipo tipografico.
  text-decoration: none quita el subrayado porque aqui funciona como marca.
*/
.brand {
  display: block;
  width: max-content;
  color: var(--color-text);
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0;
  line-height: 0.78;
}

.brand__row {
  display: flex;
  align-items: flex-end;
  gap: 7px;
}

.brand__small {
  color: #a8a8a8;
  font-size: clamp(15px, 1.87vw, 28px);
  font-weight: 800;
}

.brand__large {
  font-size: clamp(24px, 2.88vw, 44px);
  font-weight: 800;
}

.brand__bottom {
  display: block;
  color: #b0b0b0;
  font-size: clamp(18px, 2.32vw, 35px);
  font-weight: 800;
}

/*
  Menú principal: Poppins 500, primera letra en mayúscula, letter-spacing 0em, 1rem.
  Levon Biss: body-font → Poppins 400, letter-spacing 0em, 1rem, sin mayúsculas.
  Aquí: mismo patrón con weight 500 y capitalize por petición.
*/
.primary-nav {
  display: flex;
  align-items: center;
  gap: clamp(20px, 2.2vw, 32px);
  color: var(--color-text);
  font-family: var(--font-nav);
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0em;
  text-transform: capitalize;
}

.primary-nav a {
  position: relative;
  text-decoration: none;
}

/*
  Pequeña linea de hover.
  El menu sigue siendo minimalista, pero da respuesta visual al pasar el raton.
*/
.primary-nav a::after {
  position: absolute;
  right: 0;
  bottom: -6px;
  left: 0;
  height: 1px;
  background: currentColor;
  content: "";
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 180ms ease;
}

.primary-nav a:hover::after,
.primary-nav a:focus-visible::after,
.primary-nav a[aria-current="page"]::after {
  transform: scaleX(1);
}

/*
  Seccion de galeria.
  La referencia deja las fotos casi inmediatamente despues de la cabecera.
*/
.gallery-section {
  padding: 0 0 clamp(32px, 4vw, 54px);
}

/*
  Mensaje de error de la galeria.
  Solo aparece si el navegador no consigue leer los archivos JSON de fotos.
*/
.gallery-error {
  grid-column: 1 / -1;
  width: 100%;
  border: 1px solid var(--color-line);
  padding: 18px;
  background: #1a1a1a;
  color: #d0d0d0;
  font-size: 15px;
  font-weight: 300;
}

/*
  BARRA Y BOTÓN TOGGLE B&N ↔ COLOR
  Aparece solo cuando el JSON tiene fotos en la lista "color".
  Oculto por defecto (display: none); script.js añade la clase
  "is-visible" si detecta al menos una foto en modo color.
*/
.gallery-toggle-bar {
  display: none;
  justify-content: flex-end;
  align-items: center;
  padding: clamp(10px, 1.2vw, 16px) var(--page-gutter) clamp(14px, 1.5vw, 22px);
}

.gallery-toggle-bar.is-visible {
  display: flex;
  flex-shrink: 0;
}

.gallery-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--color-muted);
  cursor: pointer;
  /*
    Mismo criterio que el resto del rediseño: sin caja y atenuado por
    defecto, iluminándose al acercarse el cursor o al enfocar con
    teclado. En móvil, sin hover, se queda en este punto intermedio —
    visible pero discreto, no reclama atención sobre la foto hasta que
    se busca. width/height dan área de toque de sobra aunque el icono
    visible sea más pequeño (22px).
  */
  opacity: 0.55;
  transition:
    color 200ms ease,
    opacity 200ms ease;
}

.gallery-toggle:focus-visible {
  color: var(--color-text);
  opacity: 1;
}

/*
  Mismo motivo que .scroll-top:hover (ver su comentario): en táctil,
  :hover se queda "atascado" tras un toque hasta el siguiente toque en
  otro sitio. Con esta media query, :hover directamente no existe en
  táctil, así que no hay nada que se pueda quedar pegado.
*/
@media (hover: hover) and (pointer: fine) {
  .gallery-toggle:hover {
    color: var(--color-text);
    opacity: 1;
  }
}

/*
  Icono: círculo partido a la mitad. El stroke (contorno completo del
  <circle>) dibuja el aro; el <path> es un semicírculo relleno que
  cubre la mitad derecha. Juntos forman un círculo mitad hueco, mitad
  sólido — un solo símbolo en vez de textos "B&N"/"Color".
*/
.gallery-toggle__icon {
  width: 22px;
  height: 22px;
  transition: transform 320ms ease;
}

.gallery-toggle__icon-fill {
  fill: currentColor;
}

/*
  Estado activo (Color): se rota el icono 180° para que la mitad
  rellena "cambie de lado" — una sola pieza sirve para los dos
  estados, sin duplicar SVGs ni animar relleno/opacidad por separado.
*/
.gallery-toggle.is-active .gallery-toggle__icon {
  transform: rotate(180deg);
}

/*
  El carrusel es ahora el modo universal en todos los dispositivos.
  El díptico de dos columnas se elimina de la galería de inicio.
*/
.carrusel-movil {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  /*
    Altura calculada con svh, que es la que funcionaba bien en escritorio
    (donde no hay barra de direcciones que cambie con el scroll, así que
    svh es estable). En móvil esta misma unidad causaba que las fotos
    verticales crecieran al hacer scroll — ese caso se corrige aparte,
    de forma aislada, en el media query de móvil más abajo, sin tocar
    este comportamiento base que ya era correcto en escritorio.
  */
  height: calc(100svh - 220px);
  min-height: 300px;
}

/*
  Imagen del carrusel.
  - max-width: 90% da aire lateral a las fotos horizontales.
  - max-height: 100% limita la altura al contenedor.
  - width/height auto preserva proporciones sin estirar.
  - Sin width: 100% para evitar que las horizontales ocupen todo el ancho
    y que las verticales se ensanchen al cambiar la altura del viewport en móvil.
*/
.carrusel-movil__img {
  display: block;
  width: auto;
  height: auto;
  max-width: 90%;
  max-height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 350ms ease;
}

.carrusel-movil__img.is-loaded {
  opacity: 1;
}

/*
  Proporciones de las fotos dentro de una serie.
*/
.series-photo--vertical {
  aspect-ratio: 3 / 4;
}

.series-photo--horizontal {
  aspect-ratio: 4 / 3;
}

.series-photo--square {
  aspect-ratio: 1 / 1;
}

/*
  Imágenes dentro de cada foto de serie.
  object-fit: cover rellena el marco sin deformar.

  opacity + transition: las imágenes lazy aparecen con un fade suave en
  lugar de aparecer de golpe cuando terminan de descargar. La clase
  .is-loaded la añade series.js en el evento "load" de cada <img>.
*/
.series-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: none;
  opacity: 0;
  transition:
    transform 700ms ease,
    opacity 350ms ease;
}

.series-photo img.is-loaded {
  opacity: 1;
}

.series-photo__button:hover img,
.series-photo__button:focus-visible img {
  transform: scale(1.025);
}

/*
  Estado de foco para personas que navegan con teclado.
*/
.series-photo__button:focus-visible,
.lightbox__close:focus-visible,
.lightbox__nav:focus-visible {
  outline: 2px solid var(--color-text);
  outline-offset: 4px;
}

.section-copy {
  max-width: 610px;
}

.series-block h1,
.series-block__header h2 {
  margin: 0 0 10px;
  font-size: clamp(28px, 3.6vw, 52px);
  font-weight: 800;
  line-height: 0.95;
  letter-spacing: 0;
  opacity: 0.2;
}

/*
  PAGINA SERIES
  ---------------------------------------------------------------------------
  La pagina series.html se divide en bloques.
  Cada bloque es una serie fotografica con:
  - titulo arriba a la izquierda;
  - descripcion pequeña;
  - reticula de fotos.
*/
.series-page {
  padding: 0;
}

.series-block {
  padding: clamp(44px, 6vw, 84px) var(--page-gutter);
  border-top: 1px solid var(--color-soft);
}

/*
  En Info, el hueco entre el header y "Para y respira." quedaba
  demasiado grande en escritorio ancho (hasta 84px, el máximo del
  clamp de arriba). Se reduce solo aquí, vía .contact-section (propia
  de esta página), sin tocar .series-block, que también usa la vista
  de detalle de series.
*/
.contact-section {
  padding-top: clamp(28px, 3.5vw, 48px);
}

/*
  En la vista de detalle de serie (#seriesListado), justo debajo del
  header, el padding-top de arriba llegaba hasta 84px en escritorios
  anchos — demasiado hueco. Se reduce solo aquí; padding-bottom y el
  uso compartido de .series-block en info.html no cambian.
  Sin línea divisoria ni botón "Volver" en este punto: el único punto
  de vuelta al grid es #seriesBackFloat, el botón flotante abajo a la
  izquierda (aparece con el scroll, igual que el de subir arriba).
*/
#seriesListado .series-block {
  padding-top: clamp(24px, 3vw, 40px);
  border-top: none;
}

/*
  Alternamos fondos muy suaves para separar una serie de la siguiente
  sin usar cajas pesadas ni romper el estilo minimalista.
*/
.series-block:nth-child(even) {
  background: #161616;
}

.series-block:nth-child(odd) {
  background: var(--color-page);
}

.series-block__header {
  max-width: none;
  width: 100%;
  margin-bottom: clamp(20px, 2.5vw, 36px);
}

.series-block__header h2 {
  margin: 0 0 10px;
  font-size: clamp(28px, 3.6vw, 52px);
  font-weight: 800;
  line-height: 0.96;
  letter-spacing: 0;
  opacity: 0.2;
}

.series-block__header p {
  max-width: none;
  width: 100%;
  margin: 0;
  /*
    Mínimo subido de 8px a 14px: un floor de 8px es ilegible en
    cualquier pantalla, no solo en las más estrechas donde llegaría a
    aplicarse — por debajo de los mínimos de legibilidad habituales
    para texto de cuerpo.
  */
  font-size: clamp(14px, 2vw, 16px);
  opacity: 0.6;
}

/* Párrafo de texto largo dentro de series-block (página info) */
.series-block .section-copy p {
  margin-top: 0;
  color: #cfcfcf;
  font-size: clamp(14px, 2vw, 16px);
  font-weight: 300;
  line-height: 1.36;
  opacity: 0.6;
}

.series-block .section-copy a {
  text-underline-offset: 5px;
}

.series-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--grid-gap);
  align-items: start;
}

.series-photo {
  margin: 0;
  overflow: hidden;
  background: #2a2a2a;
}

.series-photo__button {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: zoom-in;
}

/*
  Visor grande de fotos.
  display: none lo oculta por defecto.
*/
.lightbox {
  /*
    position: fixed pega el visor a la ventana, no a una parte concreta
    del documento. inset: 0 equivale a poner top/right/bottom/left a 0,
    por eso cubre toda la pantalla.
  */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  /*
    height fijo para evitar que la altura cambie según el scroll.
    Esto soluciona el problema de posicionamiento del visor en Series.
    calc(var(--vh-movil) * 100) en vez de 100vh a secas: --vh-movil la
    calcula scroll-top.js con JS, así que refleja la altura real visible
    en el momento del último resize "de verdad" (no del scroll), en vez
    del valor "más alto posible" que asume 100vh estático (como si la
    barra de direcciones estuviera siempre oculta).
  */
  height: calc(var(--vh-movil, 1vh) * 100);

  /*
    z-index lo pone por encima del resto de la pagina.
    Si otro elemento tuviera un z-index mayor, podria tapar el visor.
  */
  z-index: 50;
  display: none;
  align-items: center;
  justify-content: center;
  padding: clamp(18px, 4vw, 48px);
  box-sizing: border-box;
}

/*
  Cuando script.js añade la clase is-open, el visor aparece.
*/
.lightbox.is-open {
  display: flex;
}

.lightbox__backdrop {
  position: fixed;
  inset: 0;
  background: rgba(16, 16, 16, 1);
  z-index: 49;
}

.lightbox__panel {
  position: relative;
  z-index: 50;
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: stretch;
  width: min(1080px, 100%);
  height: 100%;
  max-height: 100%;
  margin: 0;
  padding-top: 48px;
  box-sizing: border-box;
  /*
    Eliminamos overflow-y: auto para evitar scroll interno que pueda
    afectar al posicionamiento de las flechas. Usamos flexbox para
    distribuir el contenido verticalmente.
  */
  overflow: hidden;
}

.lightbox__panel--series {
  width: min(1120px, 100%);
}

.lightbox__panel img {
  display: block;
  width: auto;
  max-width: 100%;
  /*
    flex: 1 + min-height: 0 hace que la imagen ocupe todo el espacio
    disponible entre el padding-top y el nav-bar inferior.
  */
  flex: 1;
  min-height: 0;
  max-height: 100%;
  object-fit: contain;
  background: var(--color-page);
  filter: none;
}

.lightbox__panel figcaption {
  margin-top: 12px;
  color: var(--color-muted);
  font-size: 14px;
  font-weight: 300;
  text-align: center;
}

.lightbox__close {
  position: absolute;
  top: 0;
  right: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 0;
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
}

.lightbox__close svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
}

/*
  Barra de flechas: fila centrada debajo de la imagen.
  position: sticky + bottom: 0 la ancla siempre al borde inferior del
  área visible del panel (que tiene overflow-y: auto). Esto garantiza
  que las flechas sean siempre alcanzables sin depender de calcular la
  altura exacta del viewport en JS — incluso si esa altura es imprecisa
  (por ejemplo, justo después de hacer scroll en móvil, cuando la barra
  de direcciones del navegador está en transición), sticky simplemente
  mantiene la barra pegada al fondo de lo que sea visible en cada momento.
  El fondo opaco evita que la imagen se vea "pasar por debajo" al hacer
  scroll dentro del panel.
*/
.lightbox__nav-bar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 40px;
  margin-top: auto;
  padding-top: 16px;
  padding-bottom: 16px;
  flex-shrink: 0;
  background: var(--color-page);
}

.lightbox__nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border: none;
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  transition: opacity 180ms ease;
}

/*
  Mismo motivo que .scroll-top:hover: sin esto, :hover se queda
  "atascado" en táctil tras un toque. :active no se toca aquí porque
  ese sí se comporta bien en táctil (solo dura mientras se pulsa).
*/
@media (hover: hover) and (pointer: fine) {
  .lightbox__nav:hover {
    opacity: 0.45;
  }
}

.lightbox__nav:active {
  opacity: 0.45;
}

.lightbox__nav:focus {
  outline: none;
}

.lightbox__nav:focus-visible {
  outline: 2px solid var(--color-text);
  outline-offset: 4px;
}

.lightbox__nav svg {
  width: 26px;
  height: 26px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  pointer-events: none;
}

.lightbox__nav:disabled {
  cursor: not-allowed;
  opacity: 0.2;
}

/*
  VISTA DE CUADRICULA DE SERIES (GRID)
  =====================================
  Muestra todas las series en una cuadrícula con título y foto de muestra.
*/
.series-grid-view[hidden] {
  display: none;
}

.series-grid-view {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: clamp(30px, 4vw, 50px);
  padding: clamp(40px, 5vw, 80px) var(--page-gutter);
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  justify-items: center;
  transition: opacity 220ms ease;
}

.series-grid-view.is-fading {
  opacity: 0;
  pointer-events: none;
}

/*
  Tarjeta individual de serie en el grid.
*/
.series-card {
  position: relative;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  transition: opacity 240ms ease;
}

/*
  Mismo motivo que .scroll-top:hover: sin esto, :hover se queda
  "atascado" en táctil tras un toque, mostrando la tarjeta atenuada
  aunque nadie la esté tocando.
*/
@media (hover: hover) and (pointer: fine) {
  .series-card:hover {
    opacity: 0.7;
  }
}

.series-card:active {
  opacity: 0.5;
}

/*
  Contenedor de la foto de muestra en la tarjeta.
*/
.series-card__photo {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--color-soft);
}

.series-card__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition:
    transform 480ms ease,
    opacity 350ms ease;
}

/*
  is-loaded la añade series.js en el evento "load" de cada miniatura.
  Sin esta regla la clase no tenía ningún efecto visual: la imagen
  aparecía de golpe en cuanto terminaba de decodificar en vez de con
  un fundido, lo que hacía más visible cualquier diferencia de tiempos
  de carga entre las tres tarjetas del grid.
*/
.series-card__photo img.is-loaded {
  opacity: 1;
}

@media (hover: hover) and (pointer: fine) {
  .series-card:hover .series-card__photo img {
    transform: scale(1.05);
  }
}

/*
  Overlay oscuro sobre la foto.
  Oculto por defecto, aparece al hacer hover junto con el título.
  En móvil (sin hover) siempre visible — ver media query más abajo.
*/
.series-card__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  pointer-events: none;
  opacity: 0;
  transition: opacity 680ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .series-card:hover .series-card__photo::after {
    opacity: 1;
  }
}

/*
  Título superpuesto sobre la foto, centrado.
  Oculto por defecto, aparece al hacer hover.
  En móvil siempre visible — ver media query más abajo.
*/
.series-card__title {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0 clamp(12px, 2vw, 24px);
  font-size: clamp(18px, 2vw, 26px);
  font-weight: 700;
  color: var(--color-text);
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 680ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .series-card:hover .series-card__title {
    opacity: 0.95;
  }
}

/*
  VISTA DETALLADA DE SERIE
  =========================
  Muestra una sola serie con título, descripción y todas las fotos.
  El atributo [hidden] lo oculta; quitarlo lo muestra.
  La regla display:none explícita garantiza que el atributo hidden
  siempre gane sobre cualquier regla CSS que pudiera conflictuar.
*/
.series-detail-view[hidden] {
  display: none;
}

.series-detail-view {
  width: 100%;
  animation: fadeIn 240ms ease;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/*
  Adaptacion para tablets.
*/
@media (max-width: 900px) {
  .site-header {
    grid-template-columns: max-content 1fr max-content;
  }

  .series-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/*
  Adaptacion para moviles.
  Todo se vuelve de una columna y el menu puede saltar de linea.
*/
@media (max-width: 620px) {
  :root {
    --page-gutter: 18px;
    --grid-gap: 8px;
  }

  /*
    Diseño fijo sin scroll para móvil en Inicio, Series e Info.
    El body ocupa toda la altura del viewport y usa flexbox para
    distribuir header y main verticalmente.
  */
  body.page-home,
  body.page-series,
  body.page-info {
    height: 100vh;
    /* Ver el comentario de la regla equivalente de escritorio, más arriba. */
    height: calc(var(--vh-movil, 1vh) * 100);
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  /*
    El contenido principal ocupa el espacio disponible bajo el header.
  */
  body.page-home main,
  body.page-info main {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  /*
    La página de series tiene scroll en el main.
  */
  body.page-series main {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
  }

  /*
    La sección de galería ocupa todo el espacio disponible del main.
  */
  body.page-home .gallery-section {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  /*
    El carrusel ocupa el espacio restante después del toggle.
    Añadimos padding-top para dar más espacio entre el menú y las fotos.
  */
  body.page-home .carrusel-movil {
    flex: 1;
    min-height: 0;
    padding-top: 20px;
  }

  /*
    Imagen del carrusel más pequeña en móvil para dejar más aire.
  */
  body.page-home .carrusel-movil__img {
    max-width: 75%;
    max-height: 85%;
  }

  /* Flechas del visor más arriba en móvil */
  .lightbox__nav-bar {
    padding-bottom: 36px;
  }

  /* Eliminar padding-top del panel en móvil para evitar desplazamiento */
  .lightbox__panel {
    padding-top: 0;
  }

  /* Eliminar transición de opacidad en móvil para evitar que las flechas cambien de color */
  .lightbox__nav {
    transition: none;
  }

  .lightbox__nav:hover,
  .lightbox__nav:active,
  .lightbox__nav:focus {
    opacity: 1;
  }

  /* Mantener el estado disabled con opacidad reducida */
  .lightbox__nav:disabled,
  .lightbox__nav.is-disabled {
    opacity: 0.2 !important;
  }

  .site-header {
    /* En móvil: brand ocupa toda la primera fila; nav baja a la segunda */
    grid-template-columns: 1fr;
    align-items: center;
    padding-top: 20px;
    padding-bottom: 12px;
    flex-shrink: 0;
  }

  /* El espaciador no sirve en móvil: se oculta */
  .header-spacer {
    display: none;
  }

  /* Nav ocupa toda la segunda fila */
  .primary-nav {
    grid-column: 1 / -1;
    gap: 14px;
    font-size: 13px;
    flex-wrap: wrap;
  }

  /* Toggle más compacto en móvil */
  .gallery-toggle-bar {
    padding: 8px 0;
    flex-shrink: 0;
  }

  /* Ocultar botón scroll-top en móvil en páginas sin scroll (Inicio e Info) */
  body.page-home .scroll-top,
  body.page-info .scroll-top {
    display: none;
  }

  /* Brand se ancla en la primera fila, columna izquierda */
  .brand {
    grid-row: 1;
    grid-column: 1;
  }

  .series-grid {
    grid-template-columns: 1fr;
  }

  /*
    Cuadrícula de series más compacta en móvil.
    padding-bottom más generoso que el resto (60px en vez de 24px):
    el botón de subir (fixed, siempre visible) necesita sitio libre al
    final del scroll para no solaparse con la última fila de tarjetas.
  */
  .series-grid-view {
    grid-template-columns: 1fr;
    gap: 24px;
    padding: 24px var(--page-gutter) 60px;
  }

  .series-card__title {
    font-size: 18px;
  }

  /*
    En móvil no hay hover: el overlay y el título se muestran siempre.
    Título con opacidad reducida para no competir con la foto.
  */
  .series-card__photo::after {
    opacity: 1;
  }

  .series-card__title {
    opacity: 0.55;
  }

  /*
    Reduce el padding superior del bloque de serie en móvil — el valor
    de escritorio (hasta 84px) deja demasiado espacio entre la línea
    divisoria y el título.

    Usa el mismo selector con #seriesListado que la regla de escritorio
    (misma especificidad): al ir después en el archivo, esta gana en
    móvil. Con solo ".series-block" (menor especificidad) la regla de
    escritorio seguía ganando incluso aquí, aunque el media query
    coincidiera.
  */
  #seriesListado .series-block {
    padding-top: clamp(20px, 4vw, 32px);
    /*
      Los botones flotantes (subir, volver) son fixed y siempre
      visibles dentro de una serie: sin este espacio extra, la última
      foto queda detrás de ellos al llegar al final del scroll.
    */
    padding-bottom: 60px;
  }
}

/*
  Respeta usuarios que prefieren menos animacion.
*/
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .series-photo img,
  .carrusel-movil__img,
  .gallery-toggle__icon,
  .primary-nav a::after {
    transition: none;
  }
}

/*
  BOTÓN SCROLL-TO-TOP
  Aparece en la esquina inferior derecha cuando el usuario ha scrollado
  más de 200px. Se oculta automáticamente arriba del todo.
  La transición es opacity + transform para que entre y salga suavemente.

  Sin backdrop-filter a propósito: con position:fixed + backdrop-filter,
  muchos navegadores móviles dejan de repintar el elemento durante un
  scroll táctil activo (el difuminado es costoso de recalcular en cada
  frame) — el botón "desaparecía" hasta el siguiente toque en pantalla,
  que forzaba un repintado. El fondo ya es casi opaco (0.92), así que
  el difuminado apenas se notaba.

  will-change: opacity, transform se mantiene porque este botón anima
  esas dos propiedades cada vez que aparece/desaparece (varias veces
  por sesión) — está justificado por sí solo, aparte de que en su día
  también se probó (sin éxito) como mitigación de un bug de repintado
  específico de DuckDuckGo para Android, confirmado en 4 navegadores
  distintos y ya abandonado como línea de investigación.
*/
.scroll-top {
  position: fixed;
  right: clamp(18px, 3vw, 36px);
  bottom: clamp(10px, 2vw, 20px);
  will-change: opacity, transform;
  z-index: 40;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(16, 16, 16, 0.92);
  color: var(--color-text);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition:
    opacity 240ms ease,
    transform 240ms ease;
}

.scroll-top.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.scroll-top svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
}

/*
  hover(hover) and (pointer: fine) = solo dispositivos con un puntero
  de verdad (ratón), nunca táctil. En móvil, tocar un botón activa su
  :hover normal y, al no existir un "quitar el ratón de encima", se
  queda con ese estilo aplicado hasta tocar otra cosa — el botón
  reaparece "marcado" al volver a bajar, aunque nadie lo esté tocando.
  Con esta media query el :hover directamente no existe en táctil, así
  que no hay nada que se pueda quedar pegado.
*/
@media (hover: hover) and (pointer: fine) {
  .scroll-top:hover {
    background: var(--color-text);
    color: var(--color-page);
    border-color: var(--color-text);
  }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-top {
    transition: opacity 240ms ease;
    transform: none;
  }
}

/*
  BOTÓN FLOTANTE "VOLVER"
  Gemelo de .scroll-top (mismo tamaño, fondo, borde, transición) pero
  con la flecha apuntando a la izquierda. Va en la misma esquina
  inferior derecha, justo a la izquierda de .scroll-top (que no se
  toca, mantiene su posición: right: clamp(18px, 3vw, 36px)), con un
  hueco de 14px entre ambos — ese cálculo es: el hueco del borde de
  .scroll-top + su ancho (42px) + 14px de separación.

  Sin backdrop-filter, mismo motivo que .scroll-top: ver su comentario
  de cabecera más arriba.

  Solo existe en series.html; series.js le añade/quita is-visible al
  abrir/cerrar una serie (no según el scroll: se ve todo el tiempo
  mientras hay una serie abierta).
*/
.series-back-float {
  position: fixed;
  right: calc(clamp(18px, 3vw, 36px) + 42px + 14px);
  bottom: clamp(10px, 2vw, 20px);
  will-change: opacity, transform;
  z-index: 40;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(16, 16, 16, 0.92);
  color: var(--color-text);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition:
    opacity 240ms ease,
    transform 240ms ease;
}

.series-back-float.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.series-back-float svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
}

/*
  Mismo motivo que .scroll-top:hover: solo dispositivos con puntero
  fino, para que no se quede "atascado" tras un toque en móvil.
*/
@media (hover: hover) and (pointer: fine) {
  .series-back-float:hover {
    background: var(--color-text);
    color: var(--color-page);
    border-color: var(--color-text);
  }
}

@media (prefers-reduced-motion: reduce) {
  .series-back-float {
    transition: opacity 240ms ease;
    transform: none;
  }
}
