:root {
  --bg: #0e0e10;
  --ink: #f2efea;
  --muted: #837f77;
  --hairline: #201f21;
  --accent: #d8cfc0;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--accent); color: #0e0e10; }

/* ---------- masthead ---------- */

/* flotante sobre la grilla en vez de ocupar una franja fija arriba: le gana
   ese espacio de vuelta al contenido. pointer-events: none en la franja
   entera para no tapar el hover de los tiles de abajo; se reactiva solo en
   la píldora que realmente tiene contenido clickeable. */
.masthead {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 0.6rem clamp(1rem, 3vw, 1.5rem);
  pointer-events: none;
  z-index: 15;
}

.masthead__meta {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  background: rgba(14, 14, 16, 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 0.4rem 0.8rem;
  pointer-events: auto;
}

.options-toggle {
  background: none;
  border: 1px solid var(--hairline);
  color: var(--ink);
  border-radius: 50%;
  width: 1.9rem;
  height: 1.9rem;
  line-height: 1;
  font-size: 0.9rem;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.2s ease, border-color 0.2s ease;
}

.options-toggle:hover,
.options-toggle:focus-visible {
  opacity: 1;
  border-color: var(--accent);
}

.options-menu {
  position: fixed;
  top: 3rem;
  right: clamp(1rem, 3vw, 1.5rem);
  background: #17171a;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 0.9rem 1rem;
  display: none;
  flex-direction: column;
  gap: 0.6rem;
  z-index: 15;
  min-width: 200px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.4);
}

.options-menu.is-open { display: flex; }

.options-menu__field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.options-menu__field select,
.options-menu__field input {
  background: #0e0e10;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 4px;
  padding: 0.4rem 0.5rem;
  font-size: 0.85rem;
  text-transform: none;
  letter-spacing: normal;
  font-family: inherit;
}

/* campos que no aplican a la vista activa (data-views + setWallVisibility):
   el UA pone display:none con [hidden], pero nuestra regla display:flex de
   .options-menu__field le gana en especificidad — hay que pisarla. */
.options-menu__field[hidden] { display: none; }

/* lista de directorios con checkbox: cuáles participan de la presentación */
.options-menu__checklist {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.options-menu__checklist label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.85rem;
  text-transform: none;
  letter-spacing: normal;
  color: var(--ink);
  cursor: pointer;
}

.options-menu__checklist input {
  accent-color: var(--accent);
  margin: 0;
}

.options-menu__checklist .dir-count {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.options-menu__version {
  margin-top: 0.2rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: right;
  text-decoration: none;
  cursor: pointer;
}

.options-menu__version:hover,
.options-menu__version:focus-visible {
  color: var(--accent);
  text-decoration: underline;
}

/* ---------- grid ---------- */

/* masonry vía CSS multi-column: como los tiles tienen alturas reales distintas
   (relación de aspecto real), un grid de filas fijas deja huecos grises donde
   un tile es más bajo que el más alto de su fila. Con columnas, cada tile se
   apila justo debajo del anterior en su columna, sin huecos. */
.grid {
  column-width: var(--thumb-width, 230px);
  column-gap: 1px;
  background: var(--hairline);
}

.tile {
  position: relative;
  aspect-ratio: 9 / 16; /* fallback hasta que se conozcan las dimensiones reales del archivo */
  overflow: hidden;
  background: #050505;
  cursor: pointer;
  break-inside: avoid;
  margin-bottom: 1px;
}

.tile__media {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: grayscale(1) contrast(1.04) brightness(0.96);
  transform: scale(1.001);
  transition: filter 0.6s ease, transform 0.6s ease;
}

.tile:hover .tile__media,
.tile:focus-visible .tile__media {
  filter: grayscale(0) contrast(1.02) brightness(1);
  transform: scale(1.035);
}

/* videos stay in color always; only images fade from grayscale on hover.
   no filter at all on video (not even a no-op one) so the browser can use
   its fastest video compositing/decode path instead of routing frames
   through the filter shader on every tile. */
.tile[data-kind="video"] .tile__media,
.tile[data-kind="video"]:hover .tile__media,
.tile[data-kind="video"]:focus-visible .tile__media {
  filter: none;
}

/* Opciones → Fotos: "Color" apaga el efecto blanco y negro por defecto de
   las imágenes (los videos ya son siempre a color, sin esto). Misma receta
   que la regla de video de arriba: hay que pisar tanto el estado normal
   como el :hover/:focus-visible. */
body.photos-color .tile[data-kind="image"] .tile__media,
body.photos-color .tile[data-kind="image"]:hover .tile__media,
body.photos-color .tile[data-kind="image"]:focus-visible .tile__media {
  filter: none;
}

.tile__num {
  position: absolute;
  left: 0.7rem;
  bottom: 0.65rem;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  color: var(--ink);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.35s ease, transform 0.35s ease;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 6px rgba(0,0,0,0.65);
}

.tile__kind {
  position: absolute;
  right: 0.7rem;
  top: 0.65rem;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.55;
}

.tile[data-kind="image"] .tile__kind { display: none; }

/* Opciones → "Nombre de archivo"/"Nombre de directorio": a diferencia de
   .tile__num (que solo aparece en hover), estos van SIEMPRE visibles
   cuando están prendidos — el objetivo es distinguir de un vistazo qué es
   cada cosa mirando muchas a la vez (grilla completa o wall), no al pasar
   el mouse por una en particular. Ocultos por defecto (display:none) y
   controlados por una clase en <body>, igual patrón que .photos-color. */
.tile__label {
  display: none;
  position: absolute;
  left: 0.6rem;
  font-size: 0.6rem;
  letter-spacing: 0.02em;
  color: var(--ink);
  text-shadow: 0 1px 6px rgba(0,0,0,0.8);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

.tile__label--dir {
  top: 0.55rem;
  right: 1.3rem; /* deja lugar al punto de .tile__kind */
  font-weight: 600;
}

.tile__label--file {
  top: 1.45rem;
  right: 0.6rem;
  opacity: 0.85;
}

body.show-dir-label .tile__label--dir { display: block; }
body.show-file-label .tile__label--file { display: block; }

.tile:hover .tile__num,
.tile:focus-visible .tile__num {
  opacity: 1;
  transform: translateY(0);
}

.tile:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: -1px;
}

/* ---------- wall (pantallas fijas, sin scroll) ---------- */

/* grid-template-columns/rows se calculan en JS según viewport (computeWallGrid).
   El gap+background hairline es el mismo truco que .grid para las líneas finas
   entre tiles, sin usar borders (que sumarían al tamaño de la celda). */
.wall {
  position: fixed;
  inset: 0;
  z-index: 1;
  background: var(--hairline);
  gap: 1px;
  display: none;
}

.wall-slot {
  position: relative;
  overflow: hidden;
  background: #050505;
  cursor: pointer;
}

.wall-slot__media {
  width: 100%;
  height: 100%;
  object-fit: contain; /* nunca recorta: el contenido es mayormente vertical, cover lo cortaba */
  display: block;
}

/* mismo criterio que .tile__label (ver arriba): siempre visibles cuando
   están prendidos, no solo en hover — acá ni siquiera hay hover real. */
.wall-slot__label {
  display: none;
  position: absolute;
  left: 0.5rem;
  right: 0.5rem;
  font-size: 0.6rem;
  letter-spacing: 0.02em;
  color: var(--ink);
  text-shadow: 0 1px 6px rgba(0,0,0,0.8);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

.wall-slot__label--dir {
  top: 0.5rem;
  font-weight: 600;
}

.wall-slot__label--file {
  bottom: 0.5rem;
  opacity: 0.85;
}

body.show-dir-label .wall-slot__label--dir { display: block; }
body.show-file-label .wall-slot__label--file { display: block; }

/* ---------- indicador de carga ---------- */

/* Un <video> recién insertado no dibuja NADA hasta que decodifica su primer
   frame: con los clips a 720p (varios MB cada uno) eso puede ser un par de
   segundos de rectángulo negro sin ninguna señal de que algo está pasando.
   El spinner vive como ::after del contenedor (tile, slot del wall, peek y
   lightbox) — cero DOM extra — y JS solo prende/apaga la clase .is-loading
   (ver trackMediaLoading en app.js). También reaparece si el video se queda
   sin buffer en el medio ('waiting'). */
.tile.is-loading::after,
.wall-slot.is-loading::after,
.lb-neighbor.is-loading::after,
.lightbox.is-loading::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 24px;
  height: 24px;
  margin: -12px 0 0 -12px;
  border: 1px solid rgba(216, 207, 192, 0.16);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: media-spin 0.9s linear infinite;
  pointer-events: none;
  z-index: 3;
}

/* a pantalla completa el mismo tamaño se ve minúsculo */
.lightbox.is-loading::after {
  width: 38px;
  height: 38px;
  margin: -19px 0 0 -19px;
  border-width: 2px;
}

@keyframes media-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .tile.is-loading::after,
  .wall-slot.is-loading::after,
  .lb-neighbor.is-loading::after,
  .lightbox.is-loading::after {
    animation: none;
    border-color: rgba(216, 207, 192, 0.35); /* anillo estático: sigue indicando sin girar */
  }
}

/* ---------- belt (cinta continua) ---------- */

/* Tercer modo de vista: celdas en movimiento perpetuo — las nuevas entran
   por un borde y empujan a las viejas hacia el otro hasta sacarlas de
   pantalla. Solo existe en el DOM lo que se ve más un colchón (ver
   fillLane en app.js). Las celdas reusan .wall-slot (estilos, labels,
   spinner), pero a diferencia del wall NO hay grilla uniforme: la cinta
   son CARRILES de grosor fijo donde cada celda mide exactamente la
   proporción de su contenido — sin franjas negras de object-fit:contain,
   mismo criterio que los tiles de la grilla. Con dirección vertical
   (arriba/abajo) los carriles son columnas en vez de filas. */
.belt {
  position: fixed;
  inset: 0;
  z-index: 1;
  background: var(--hairline);
  overflow: hidden;
  display: none; /* JS lo pasa a flex al activar el modo */
  flex-direction: column; /* carriles horizontales apilados */
  gap: 1px; /* hairline entre carriles, mismo truco que .grid/.wall */
}

.belt--vertical { flex-direction: row; } /* carriles verticales lado a lado */

.belt-lane {
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;
}

.belt-lane__track {
  display: flex;
  height: 100%;
  gap: 1px;
  will-change: transform;
}

.belt--vertical .belt-lane__track {
  flex-direction: column;
  height: auto;
  width: 100%;
}

.belt .wall-slot {
  flex: 0 0 auto; /* el tamaño en el eje de avance lo pone JS por celda */
}

/* la celda ya tiene la relación de aspecto del contenido (la calcula JS
   desde el manifest): cover acá no recorta de verdad, solo absorbe el
   redondeo a px — es lo que elimina el negro que heredaba del wall. */
.belt .wall-slot__media {
  object-fit: cover;
}

/* "Hacia la derecha"/"Hacia abajo": mismo motor que izquierda/arriba pero
   espejado con transform en el contenedor; cada celda se contra-espeja para
   que el contenido no se vea invertido. */
.belt--flip-x { transform: scaleX(-1); }
.belt--flip-x .wall-slot { transform: scaleX(-1); }
.belt--flip-y { transform: scaleY(-1); }
.belt--flip-y .wall-slot { transform: scaleY(-1); }

/* ---------- corner tracker ---------- */

.tracker {
  position: fixed;
  right: clamp(1rem, 3vw, 2rem);
  bottom: 1.1rem;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  z-index: 5;
  background: linear-gradient(90deg, transparent, var(--bg) 30%);
  padding: 0.2rem 0 0.2rem 1.6rem;
}

#tracker-current { color: var(--accent); }
.tracker__sep { margin: 0 0.15em; opacity: 0.5; }

/* ---------- lightbox ---------- */

.lightbox {
  position: fixed;
  inset: 0;
  background: #060607;
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 20;
}

.lightbox.is-open { display: flex; }

.lb-stage {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(0.5rem, 2vw, 1.5rem);
}

/* Los TRES (anterior / actual / siguiente) se dimensionan igual: alto fijo
   92vh y ancho automático según la relación de aspecto del ítem (que JS le
   pone desde el manifest, para que sea correcto desde el primer frame sin
   esperar a que cargue el archivo). Al no tener ancho de caja fijo, no hay
   relleno vacío del object-fit:contain empujando a los vecinos hacia afuera
   — que era lo que los hacía verse chicos o recortados en todos los
   intentos anteriores (90vw, 60vw, alto calculado en JS por peek).
   Si los tres no entran, .lb-stage los centra igual y el excedente de los
   laterales se recorta contra el borde de la pantalla: ese ES el efecto
   peek, no una escala más chica. */
.lb-media,
.lb-neighbor__media {
  height: 92vh;
  width: auto;
  min-width: 0;
  min-height: 0;
  object-fit: contain;
  display: block;
}

.lb-media {
  flex: 0 0 auto;
  /* único tope, y solo pega con contenido horizontal: el elemento central
     nunca se sale de la pantalla. */
  max-width: 96vw;
}

/* actual CUADRADO: a 92vh casi no queda margen para los costados — se achica
   un poco todo (el central y las columnas de peek bajan juntos a 80vh) para
   que entren también los vecinos. Con actual vertical no hace falta; con
   actual horizontal directamente no hay peeks (ver renderLightboxMedia). */
.lightbox.is-square .lb-media,
.lightbox.is-square .lb-neighbor {
  height: 80vh;
}

/* video vertical: no llena el ancho, así que se muestran anterior/siguiente
   a los costados. Cada peek es una COLUMNA de ancho fijo igual al margen
   real que deja el central (medido en showNeighborPeeks y puesto inline por
   JS) — el mismo ancho a ambos lados, así el actual queda SIEMPRE centrado
   aunque un vecino sea más angosto que el otro. El contenido adentro se
   agranda hasta donde entre COMPLETO (sin recortar): un vecino vertical
   suele llegar al mismo alto que el actual, uno wide/cuadrado se achica lo
   necesario para entrar en la columna. Con uno a cada lado, el activo se
   reconoce por posición: es el del medio. */
.lb-neighbor {
  position: relative; /* ancla del spinner de carga (.lb-neighbor.is-loading::after) */
  flex: 0 0 auto; /* el ancho lo pone JS */
  height: 92vh;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.lb-neighbor .lb-neighbor__media {
  /* pisa la regla compartida con .lb-media de arriba: acá el tamaño no es
     fijo — lo más grande que entre en la columna sin salirse, manteniendo
     la relación de aspecto (el aspect-ratio lo pone JS desde el manifest). */
  height: auto;
  width: auto;
  max-width: 100%;
  max-height: 100%;
}

.lb-btn {
  position: absolute;
  background: none;
  border: none;
  color: var(--ink);
  cursor: pointer;
  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
  opacity: 0.6;
  transition: opacity 0.25s ease;
}

.lb-btn:hover, .lb-btn:focus-visible { opacity: 1; }

.lb-close {
  top: 1.2rem;
  right: 1.4rem;
  font-size: 1.8rem;
  line-height: 1;
  font-weight: 300;
}

.lb-prev, .lb-next {
  top: 50%;
  transform: translateY(-50%);
  font-size: 2.6rem;
  font-weight: 300;
  padding: 0.5rem 0.9rem;
}

.lb-prev { left: clamp(0.25rem, 2vw, 1.5rem); }
.lb-next { right: clamp(0.25rem, 2vw, 1.5rem); }

.lb-sound {
  top: 1.2rem;
  left: 1.4rem;
  font-size: 1.1rem;
}

.lb-index {
  position: absolute;
  bottom: 1.3rem;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

#lb-index-current { color: var(--accent); }

/* en pantallas angostas no entran tres columnas de video vertical sin que
   quede todo diminuto: se ocultan los peeks y el video principal usa todo
   el ancho disponible, como en horizontal. La condición de orientación cubre
   el monitor rotado a vertical: puede tener >900px de ancho físico y aun así
   no haber lugar real para tres videos verticales lado a lado. */
@media (max-width: 900px), (orientation: portrait) {
  .lb-neighbor { display: none; }
}

@media (max-width: 640px) {
  .masthead { padding: 0.5rem 1rem; }
  .grid { column-width: var(--thumb-width, 150px); }
  .lb-prev, .lb-next { font-size: 2rem; padding: 0.4rem 0.6rem; }
}

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