/* ============================================================
   29A LABS — Movimiento
   Regla: nada se mueve por decoración. El movimiento explica
   jerarquía (qué llega primero) o confirma una acción (hover).
   ============================================================ */

/* ── Entradas al scroll ───────────────────────────────────── */
/* El estado inicial solo se aplica si JS está vivo Y el usuario
   no pidió menos movimiento. Sin esto, un fallo de JS dejaría
   el contenido invisible para siempre. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity var(--d-slow) var(--e-out),
    transform var(--d-slow) var(--e-out);
}

.js [data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* Escalonado: las tarjetas llegan una detrás de otra, no en bloque.
   80ms es suficiente para leerse como secuencia sin sentirse lento. */
.js .entry-card[data-reveal]:nth-child(1) { transition-delay: 0ms; }
.js .entry-card[data-reveal]:nth-child(2) { transition-delay: 80ms; }
.js .entry-card[data-reveal]:nth-child(3) { transition-delay: 160ms; }
.js .entry-card[data-reveal]:nth-child(n+4) { transition-delay: 240ms; }

/* ── El cursor de la marca ────────────────────────────────── */
.brand__cursor {
  animation: tna-blink 1.1s steps(2, start) infinite;
}
@keyframes tna-blink { to { opacity: 0; } }

/* ── Glitch del logo ──────────────────────────────────────── */
/* Muy contenido: un tic de 220ms cada 9 segundos. La idea es que
   parezca una señal con interferencia, no una convulsión. */
.brand:hover .brand__mark,
.brand__mark.is-glitching {
  animation: tna-glitch 220ms steps(2, end);
}

@keyframes tna-glitch {
  0%   { transform: translate(0, 0);      filter: none; }
  25%  { transform: translate(-1.5px, 1px); filter: drop-shadow(2px 0 0 rgb(255 131 77 / 0.8)); }
  50%  { transform: translate(1.5px, -1px); filter: drop-shadow(-2px 0 0 rgb(196 73 26 / 0.8)); }
  75%  { transform: translate(-1px, -1px);  filter: drop-shadow(1px 0 0 rgb(255 131 77 / 0.6)); }
  100% { transform: translate(0, 0);      filter: none; }
}

/* ── Typewriter del hero ──────────────────────────────────── */
/* El JS reparte el texto en <span> y esto los va revelando. */
.js [data-typewriter] .tw-char {
  opacity: 0;
  animation: tna-type 1ms linear forwards;
  animation-delay: var(--tw-delay, 0ms);
}
@keyframes tna-type { to { opacity: 1; } }

/* El cursor que persigue al texto mientras se escribe */
.js [data-typewriter]::after {
  content: '_';
  color: var(--c-accent);
  animation: tna-blink 1.1s steps(2, start) infinite;
}

/* ── Respeto por prefers-reduced-motion ───────────────────── */
/* No basta con acortar las duraciones: hay que MATAR las animaciones
   que se repiten para siempre (parpadeos, glitch). Eso es lo que
   realmente molesta a quien activa este ajuste. */
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .brand__cursor,
  .js [data-typewriter]::after {
    animation: none;
  }
  .brand:hover .brand__mark,
  .brand__mark.is-glitching {
    animation: none;
  }
  .js [data-typewriter] .tw-char {
    opacity: 1;
    animation: none;
  }
  .entry-card:hover .entry-card__media img { transform: none; }
  .post-nav__link:hover { transform: none; }
}

/* ── El neón defectuoso de "LABS" ─────────────────────────── */
/* "29ALABS" va pegado; solo LABS enciende. El truco para que se lea como
   un tubo de neón averiado (y no como una animación bonita) es que el
   parpadeo sea IRREGULAR: casi siempre encendido, con microcortes en
   ráfaga en momentos raros del ciclo. Un blink regular parece un cursor;
   un blink arrítmico parece una avería. */
/* El halo tiene que ser CEÑIDO. Con blurs de 24–44px sobre un texto de 20px
   la luz se dispersa tanto que no se lee como neón: parece texto naranja a
   secas. Un núcleo caliente y cercano + dos capas medias es lo que lo vende. */
.brand__neon {
  color: var(--c-accent-lit);
  text-shadow:
    0 0 1px rgb(255 200 170 / 0.95),   /* núcleo caliente, casi blanco */
    0 0 3px rgb(252 97 33 / 1),
    0 0 8px rgb(252 97 33 / 0.85),
    0 0 16px rgb(252 97 33 / 0.55),
    0 0 30px rgb(252 97 33 / 0.30);
  animation: tna-neon 9s infinite;
}

@keyframes tna-neon {
  /* Encendido estable la mayor parte del ciclo */
  0%, 18%   { opacity: 1; }

  /* Primera ráfaga: dos cortes secos, muy juntos */
  19%       { opacity: 0.28; text-shadow: none; }
  20%       { opacity: 1; }
  21%       { opacity: 0.35; text-shadow: none; }
  23%       { opacity: 1; }

  24%, 57%  { opacity: 1; }

  /* Segunda ráfaga: más larga, el tubo "cuesta" arrancar */
  58%       { opacity: 0.22; text-shadow: none; }
  59%       { opacity: 0.85; }
  60%       { opacity: 0.25; text-shadow: none; }
  61%       { opacity: 1; }
  62%       { opacity: 0.4;  text-shadow: none; }
  64%       { opacity: 1; }

  65%, 88%  { opacity: 1; }

  /* Un último tic breve antes de reiniciar */
  89%       { opacity: 0.5; text-shadow: none; }
  90%, 100% { opacity: 1; }
}

/* El neón del footer va a otro ritmo: dos tubos idénticos parpadeando
   al unísono delatarían que es una animación, no una avería. */
.site-footer__neon {
  color: var(--c-accent);
  text-shadow:
    0 0 4px rgb(252 97 33 / 0.75),
    0 0 12px rgb(252 97 33 / 0.45),
    0 0 26px rgb(252 97 33 / 0.20);
  animation: tna-neon 9s infinite;
  animation-delay: -3.7s;   /* desfasado a propósito */
}

/* ── El 404: un letrero de neón que ya no arranca ─────────── */
/* La figura y el número parpadean POR SEPARADO. Si fueran al unísono se leería
   como una animación; desincronizados se leen como una avería, que es lo que
   cuenta la página: el tubo fundido eres tú, que buscabas algo que no está. */
.error404__figure {
  animation: tna-neon 7s infinite;
  animation-delay: -1.2s;
}

.error404__code {
  animation: tna-neon 9s infinite, tna-404-tiemble 6s infinite;
}

/* Un temblor mínimo: 1px. Suficiente para que el letrero parezca vivo, no
   tanto como para marear a quien solo quería leer el mensaje. */
@keyframes tna-404-tiemble {
  0%, 92%, 100% { transform: translate(0, 0); }
  93%           { transform: translate(-1px, 1px); }
  95%           { transform: translate(1px, -1px); }
  97%           { transform: translate(-1px, 0); }
}

/* El glitch del titular: dos copias desfasadas en cian y naranja, como un
   canal de color que se despega. Van en ::before/::after leyendo data-glitch,
   así el texto real (el que lee un lector de pantalla) sigue siendo uno solo. */
.error404__msg {
  position: relative;
  display: inline-block;
}
.error404__msg::before,
.error404__msg::after {
  content: attr(data-glitch);
  position: absolute;
  inset: 0;
  pointer-events: none;
  clip-path: inset(0 0 0 0);
}
.error404__msg::before {
  color: var(--c-accent);
  animation: tna-404-glitch-a 4.2s infinite steps(2, end);
}
.error404__msg::after {
  color: #5FF3F0;   /* el cian del canal que se despega */
  animation: tna-404-glitch-b 4.2s infinite steps(2, end);
}

@keyframes tna-404-glitch-a {
  0%, 88%, 100% { transform: translate(0); opacity: 0; }
  89%  { transform: translate(-2px, -1px); opacity: 0.9; clip-path: inset(10% 0 55% 0); }
  92%  { transform: translate(2px, 1px);   opacity: 0.7; clip-path: inset(60% 0 12% 0); }
  95%  { transform: translate(-1px, 0);    opacity: 0.5; clip-path: inset(30% 0 40% 0); }
}
@keyframes tna-404-glitch-b {
  0%, 88%, 100% { transform: translate(0); opacity: 0; }
  90%  { transform: translate(2px, 1px);   opacity: 0.75; clip-path: inset(55% 0 20% 0); }
  93%  { transform: translate(-2px, -1px); opacity: 0.55; clip-path: inset(5% 0 70% 0); }
  96%  { transform: translate(1px, 0);     opacity: 0.4;  clip-path: inset(40% 0 30% 0); }
}

@media (prefers-reduced-motion: reduce) {
  /* Un letrero que parpadea es exactamente lo que molesta a quien pide
     menos movimiento. Se queda encendido, fijo, con su glow. */
  .brand__neon,
  .site-footer__neon,
  .error404__figure,
  .error404__code {
    animation: none;
    opacity: 1;
  }
  .error404__msg::before,
  .error404__msg::after { content: none; }
}

@media (forced-colors: active) {
  /* En alto contraste el sistema decide los colores: un glitch de canales de
     color no significa nada y solo ensucia. */
  .error404__msg::before,
  .error404__msg::after { content: none; }
  .error404__figure { filter: none; }
}

/* ── Hover de la marca: el 29A se invierte ────────────────── */
/* Problema: .brand es un <a>, así que al hacer hover heredaba el color de la
   regla global a:hover — y "29A" se volvía del MISMO naranja que LABS. Los
   dos elementos se fundían en una mancha única e ilegible.

   Solución: al pasar el mouse, "29A" se invierte a negro sobre un fondo
   difuminado. Los dos elementos siguen leyéndose como dos, y el contraste
   negro-sobre-naranja (6.5:1, AA) mantiene el texto legible. */
.brand__head {
  /* El color va explícito para ganarle a la regla global de a:hover. */
  color: var(--c-text-strong);
  transition: color var(--d-base) var(--e-out), text-shadow var(--d-base) var(--e-out);
}

/* El halo NO es una caja detrás del texto: es `text-shadow`, que sigue el
   contorno exacto de cada glifo. Un ::before rectangular (o cualquier
   background) siempre pinta una mancha con forma de caja; esto abraza la
   letra por los cuatro costados y se apaga a los pocos píxeles.

   Las tres primeras capas van a alfa 1 y radio mínimo: construyen el cuerpo
   opaco que hace legible el negro. Las dos últimas solo lo desvanecen. */
.brand:hover .brand__head,
.brand:focus-visible .brand__head {
  color: var(--c-accent-ink);   /* negro */
  text-shadow:
    0 0 1px rgb(255 255 255 / 1),
    0 0 2px rgb(255 255 255 / 1),
    0 0 3px rgb(255 255 255 / 0.95),
    0 0 5px rgb(255 255 255 / 0.6),
    0 0 9px rgb(255 255 255 / 0.28);
}

/* Acá el texto es negro sobre un fondo negro: lo único que lo hace visible es
   el halo. El modo de alto contraste forzado (Windows) ELIMINA los
   text-shadow — y el "29A" desaparecería del todo. Ahí no se invierte nada. */
@media (forced-colors: active) {
  .brand:hover .brand__head,
  .brand:focus-visible .brand__head {
    color: LinkText;
    text-shadow: none;
  }
}
