/* ============================================================
   SYMBIO · GALERÍA
   Página del lienzo infinito. Hereda tokens, base y componentes de
   la landing: misma paleta, mismo vidrio, mismo logo.
   ============================================================ */

/* La página no scrollea: el movimiento lo maneja el lienzo. */
.body--galeria {
  height: 100svh;
  overflow: hidden;
  overscroll-behavior: none;    /* corta el rebote elástico en móvil */
}

/* ---------------------------------------------- Barra */

.gal-bar {
  position: fixed; top: var(--s-18); left: 0; right: 0;
  z-index: var(--z-nav);
  padding-inline: var(--gutter);
  pointer-events: none;         /* la barra no roba el arrastre del lienzo */
}
.gal-bar__inner {
  display: flex; align-items: center; gap: var(--s-24);
  max-width: var(--shell);
  margin-inline: auto;
  padding: 12px 12px 12px 24px;
  border-radius: var(--r);
  pointer-events: auto;         /* pero sus controles sí se tocan */
}
.gal-bar__title {
  margin-left: auto;
  font-size: var(--t-nav);
  font-weight: var(--w-semibold);
  letter-spacing: var(--tr-nav);
  text-transform: uppercase;
  color: var(--ash);
}
.gal-bar__back { flex: 0 0 auto; }

/* ---------------------------------------------- Lienzo */

.lienzo {
  position: fixed; inset: 0;
  z-index: var(--z-body);
  overflow: hidden;
  cursor: grab;
  touch-action: none;           /* el gesto es nuestro, no del navegador */
  -webkit-user-select: none; user-select: none;
}
.lienzo.is-grabbing { cursor: grabbing; }
.lienzo:focus-visible { outline: none; }

/* Capa que contiene las celdas. Las celdas se posicionan con
   transform absoluto, así que esta sólo define el origen. */
.lienzo__capa { position: absolute; inset: 0; }

/* Viñeta: los bordes se apagan y el lienzo se siente sin final, en
   lugar de recortado por la ventana. */
.lienzo::after {
  content: "";
  position: absolute; inset: 0;
  z-index: 3;
  pointer-events: none;
  background: radial-gradient(130% 100% at 50% 50%, transparent 52%, rgba(0,0,0,.72) 100%);
}

/* ---------------------------------------------- Celda */

.celda {
  position: absolute; top: 0; left: 0;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--glass-bg);
  border: 1px solid var(--glass-line);
  will-change: transform;       /* se mueve en cada frame */
  transition: border-color var(--d-fast) var(--ease);
}
.celda:hover { border-color: var(--glass-line-hi); }

.celda__media { position: absolute; inset: 0; }
.celda__media img,
.celda__media video {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;         /* el arrastre lo recibe el lienzo, no el media */
}

/* Placeholder: superficie con luz, no un hueco. Se reemplaza solo
   en cuanto la pieza tenga data-src. */
.celda__media--vacia {
  background:
    radial-gradient(90% 70% at 30% 18%, rgba(74,125,255,.16), transparent 62%),
    radial-gradient(80% 80% at 78% 88%, rgba(99,200,255,.12), transparent 62%),
    #0a0b10;
}
.celda__scan {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent, rgba(99,200,255,.10), transparent);
  transform: translateY(-100%);
  animation: celdaScan 6s var(--ease) infinite;
}
.celda:nth-child(2n) .celda__scan { animation-delay: 1.3s; }
.celda:nth-child(3n) .celda__scan { animation-delay: 2.7s; }
.celda:nth-child(5n) .celda__scan { animation-delay: 4.1s; }
@keyframes celdaScan {
  0%, 8%   { transform: translateY(-100%); }
  72%,100% { transform: translateY(100%); }
}

/* Pie: aparece al pasar el cursor. En táctil queda siempre visible,
   porque no hay hover que lo revele. */
.celda__pie {
  position: absolute; left: 0; right: 0; bottom: 0;
  z-index: 2;
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--s-18) var(--s-12) var(--s-12);
  background: linear-gradient(transparent, rgba(0,0,0,.82));
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--d-mid) var(--ease), transform var(--d-mid) var(--ease-out);
}
.celda:hover .celda__pie { opacity: 1; transform: none; }
.celda__pie em {
  font-style: normal;
  font-size: var(--t-caption);
  font-weight: var(--w-semibold);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--blue-bright);
}
.celda__pie span { font-size: var(--t-nav); color: var(--white); }

@media (hover: none) {
  .celda__pie { opacity: 1; transform: none; }
}

/* ---------------------------------------------- Pista */

.lienzo__pista {
  position: absolute; left: 50%; bottom: var(--s-36);
  z-index: 4;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: var(--s-12);
  margin: 0;
  padding: 10px 18px;
  border-radius: var(--r-full);
  background: rgba(10,11,16,.62);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--glass-line);
  font-size: var(--t-caption);
  font-weight: var(--w-semibold);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ash);
  pointer-events: none;
  transition: opacity var(--d-slow) var(--ease), transform var(--d-slow) var(--ease-out);
}
.lienzo__pista.is-off { opacity: 0; transform: translate(-50%, 10px); }

/* Punto que va y viene: sugiere el gesto sin explicarlo */
.lienzo__pista-icono {
  width: 26px; height: 6px;
  border-radius: var(--r-full);
  background: rgba(255,255,255,.14);
  position: relative; overflow: hidden;
}
.lienzo__pista-icono::after {
  content: "";
  position: absolute; top: 0; bottom: 0; left: 0;
  width: 10px;
  border-radius: var(--r-full);
  background: var(--blue-bright);
  animation: pistaIda 2.4s var(--ease-inout, cubic-bezier(.65,0,.35,1)) infinite alternate;
}
@keyframes pistaIda {
  from { transform: translateX(0); }
  to   { transform: translateX(16px); }
}

/* ---------------------------------------------- Lightbox */

.lb {
  position: fixed; inset: 0;
  z-index: 100;
  display: grid; place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
  /* Sin backdrop-filter, y es a propósito.

     Acá había un blur(22px) saturate(1.1) sobre todo el viewport. El
     navegador tiene que rehacer ese desenfoque cada vez que cambia lo
     que hay detrás, y encima estaba reproduciéndose un video de
     1080×1920 adelante: la pantalla grande se componía a tirones
     mientras la grilla, que no tiene ningún filtro, iba perfecta.

     Lo peor del asunto es que el efecto no se veía. El fondo ya era
     negro al 90%: el desenfoque quedaba tapado casi por completo. Se
     pagaba el costo entero de un efecto invisible.

     Subiendo el negro a 96% el resultado visual es el mismo —un fondo
     opaco que apaga la galería— y el compositor se queda sin trabajo
     por frame. */
  background: rgba(0,0,0,.96);
  opacity: 0;
  transition: opacity var(--d-mid) var(--ease);
  cursor: zoom-out;            /* el fondo cierra */
}
.lb.is-open { opacity: 1; }
.lb[hidden] { display: none; }

/* El marco no hereda el cursor del fondo: acá no se cierra. */
.lb__marco {
  margin: 0;
  display: flex; flex-direction: column; gap: var(--s-18);
  max-width: min(100%, 1200px);
  max-height: 100%;
  cursor: default;
  transform: scale(.97);
  transition: transform var(--d-mid) var(--ease-out);
}
.lb.is-open .lb__marco { transform: none; }

.lb__media {
  display: flex; align-items: center; justify-content: center;
  min-height: 0;               /* permite que el hijo se encoja dentro del flex */
}

/* `contain` y no `cover`: en la grilla la miniatura recorta, pero acá
   se muestra el cuadro completo. Es el contrato de cualquier galería
   seria — nadie quiere descubrir que le cortaron la composición. */
.lb__img,
.lb__video {
  display: block;
  max-width: 100%;
  max-height: calc(100svh - 11rem);
  width: auto; height: auto;
  object-fit: contain;
  border-radius: var(--r-sm);
  background: #000;
}

/* La sombra va sólo en las fotos. En el video se sacó: una sombra de
   90px de desenfoque alrededor de un elemento que cambia de contenido
   24 veces por segundo es trabajo de pintado que puede sacar al video
   del camino rápido del compositor. Y sobre el fondo negro al 96% no
   se veía: era negro sobre negro. */
.lb__img { box-shadow: 0 30px 90px rgba(0,0,0,.6); }

.lb__pie {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-18);
  flex-wrap: wrap;
}
.lb__texto { display: flex; align-items: baseline; gap: var(--s-12); min-width: 0; }
.lb__pie em {
  font-style: normal;
  font-size: var(--t-caption);
  font-weight: var(--w-semibold);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--blue-bright);
}
.lb__pie span { font-size: var(--t-nav); color: var(--silver); }
.lb__contador {
  font-family: var(--font);
  font-size: var(--t-caption) !important;
  letter-spacing: var(--tr-label);
  color: var(--fg-4, #5a5a63);
  font-variant-numeric: tabular-nums;   /* el contador no baila al cambiar de pieza */
  flex: 0 0 auto;
}

/* ---- botones ---- */

.lb__btn {
  position: absolute;
  display: grid; place-items: center;
  width: 46px; height: 46px;
  border-radius: 50%;
  border: 1px solid var(--glass-line);
  background: rgba(16,17,22,.72);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  color: var(--silver);
  cursor: pointer;
  transition: color var(--d-fast) var(--ease),
              border-color var(--d-fast) var(--ease),
              background var(--d-fast) var(--ease),
              transform var(--d-fast) var(--ease-out);
}
.lb__btn svg { width: 20px; height: 20px; }
.lb__btn:hover { color: var(--white); border-color: var(--glass-line-hi); background: rgba(26,28,36,.85); }
.lb__btn:active { transform: scale(.94); }
.lb__btn[hidden] { display: none; }

.lb__cerrar { top: clamp(1rem, 3vw, 2rem); right: clamp(1rem, 3vw, 2rem); }

.lb__nav { top: 50%; margin-top: -23px; }
.lb__nav--prev { left: clamp(.5rem, 2vw, 1.75rem); }
.lb__nav--next { right: clamp(.5rem, 2vw, 1.75rem); }

@media (max-width: 620px) {
  .lb { padding: 1rem .75rem 1.25rem; }
  .lb__img, .lb__video { max-height: calc(100svh - 13rem); }
  /* En pantalla angosta las flechas bajan: a los costados taparían
     la pieza, que es justamente lo que se vino a ver. */
  .lb__nav { top: auto; bottom: 1rem; margin-top: 0; }
  .lb__nav--prev { left: 50%; margin-left: -58px; }
  .lb__nav--next { right: 50%; margin-right: -58px; }
  .lb__pie { padding-bottom: 3.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .lb, .lb__marco { transition: none; }
}

/* ---------------------------------------------- Sin JavaScript */

.noscript {
  position: fixed; left: 50%; top: 50%;
  z-index: 10;
  transform: translate(-50%, -50%);
  max-width: 34rem;
  padding: var(--s-30);
  text-align: center;
  color: var(--silver);
}
.noscript a { color: var(--blue-bright); border-bottom: 1px solid currentColor; }

/* ---------------------------------------------- Accesibilidad */

@media (prefers-reduced-motion: reduce) {
  .celda__scan,
  .lienzo__pista-icono::after { animation: none; }
}

/* ---------------------------------------------- Angosto */

@media (max-width: 620px) {
  .gal-bar__title { display: none; }
  .gal-bar__inner { padding: 10px 10px 10px 18px; gap: var(--s-12); }
  .lienzo__pista { bottom: var(--s-24); font-size: 10px; }
}

/* ============================================================ CARGA

   El indicador de la pieza grande. Las piezas van a resolución
   nativa y pesan varios MB, así que entre el click y el primer
   frame hay un momento real que hay que contar — un hueco negro
   sin explicación se lee como que algo se rompió.

   Es un anillo que gira sobre el centro del marco. No tapa el
   poster: se superpone, así que mientras carga ya se ve de qué
   pieza se trata. */

.lb__media { position: relative; }

.lb__media::after {
  content: "";
  position: absolute; top: 50%; left: 50%;
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,.16);
  border-top-color: var(--blue-bright);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--d-fast) var(--ease);
}

/* Sólo gira cuando hace falta. Una animación infinita que corre
   siempre, aunque esté invisible, es trabajo de compositor regalado
   justo mientras el navegador decodifica video. */
.lb.is-cargando .lb__media::after {
  opacity: 1;
  animation: lbGirar .8s linear infinite;
}

@keyframes lbGirar { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .lb.is-cargando .lb__media::after {
    animation: none;
    border-top-color: rgba(255,255,255,.16);
    border-right-color: var(--blue-bright);
  }
}

/* ============================================================
   La grilla se apaga de verdad mientras el lightbox está abierto.

   No es redundante con el fondo opaco. Cada celda lleva
   `will-change: transform`, que le pide al navegador una capa de
   composición propia: son unas treinta y cinco capas que el
   compositor sigue administrando aunque estén tapadas, y justo
   mientras tiene que presentar un video de 1080×1920.

   Se oculta recién terminada la transición de entrada, para que no
   se vea desaparecer la galería de golpe detrás de un fondo que
   todavía está apareciendo. Al cerrar vuelve en el acto.
   ============================================================ */

.lienzo__capa { transition: visibility 0s; }

body.is-locked .lienzo__capa {
  visibility: hidden;
  transition: visibility 0s linear var(--d-mid);
}
