/* Hoja base de /app/, trozo 04 — su index enlaza los trozos en orden, y la cascada depende de él. */
/* ===== INVENTARIO (F16, portada) ===== */
/* ==================================================================================================
   INVENTARIO (/inventario dentro del hilo — inventario.js)
   Mismo idioma visual que las tarjetas de acción: carril al margen, versalitas para lo que es
   vocabulario de la interfaz, y color SOLO en los dos estados que significan algo — «necesita
   revisión» y «no reconocido». Pintar «Gestionado» de verde diría «esto está bien», y gestionado
   no significa comprobado.
   ================================================================================================== */

.inventario { display: flex; flex-direction: column; gap: var(--e4); }

/* La caja de la regla: lo primero que se lee, porque evita el malentendido caro. No lleva acento
   —no es una acción— sino el carril de --alerta: es una advertencia estructural. */
.regla {
  position: relative;
  background: var(--elevada);
  border: var(--filete) solid var(--linea);
  border-radius: var(--r-tarjeta);
  box-shadow: var(--sombra-1);
  padding: var(--e4);
}
.regla::after {
  content: '';
  position: absolute; left: 0; top: var(--e3); bottom: var(--e3);
  width: var(--carril); border-radius: 0 var(--r-chip) var(--r-chip) 0;
  background: var(--alerta);
}
.regla-titulo {
  font-size: var(--t-lg); line-height: var(--lh-md); font-weight: 700;
  letter-spacing: -.01em;
}
.regla-pasos {
  margin: var(--e2) 0 0; padding-left: var(--e5);
  font-size: var(--t-sm); line-height: var(--lh-lg);
  color: var(--tinta-suave);
}
.regla-pasos li { margin: var(--e1) 0; }
.regla-cola {
  margin: var(--e3) 0 0;
  font-size: var(--t-sm); line-height: var(--lh-lg);
  color: var(--tinta-suave);
}

.inv-controles { display: flex; flex-wrap: wrap; gap: var(--e2); }
.inv-controles[hidden] { display: none; }
.inv-filtro, .cand-entorno {
  background: var(--elevada); color: var(--tinta);
  border: var(--filete) solid var(--borde); border-radius: var(--r-control);
  padding: var(--e2) var(--e3);
  font: 500 var(--t-md)/var(--lh-md) var(--prosa);
  cursor: pointer;
}
.inv-buscar {
  flex: 1; min-width: 180px;
  background: var(--elevada); color: var(--tinta);
  border: var(--filete) solid var(--borde); border-radius: var(--r-control);
  padding: var(--e2) var(--e3);
  font: 400 var(--t-md)/var(--lh-md) var(--prosa);
  outline: none;
}
.inv-buscar::placeholder { color: var(--tinta-apagada); }

.inv-estado { display: flex; flex-direction: column; gap: var(--e2); }
.inv-resumen {
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-sm);
  font-variant-numeric: tabular-nums;         /* «3 de 12 · 40 en total» no puede bailar */
}
.inv-resumen.vacio { color: var(--tinta-apagada); padding: 0; }

.inv-lista { display: flex; flex-direction: column; gap: var(--e3); }

.cand {
  position: relative;
  background: var(--elevada);
  border: var(--filete) solid var(--linea);
  border-radius: var(--r-tarjeta);
  box-shadow: var(--sombra-1);
  padding: var(--e4);
}
.cand::after {
  content: '';
  position: absolute; left: 0; top: var(--e3); bottom: var(--e3);
  width: var(--carril); border-radius: 0 var(--r-chip) var(--r-chip) 0;
  background: var(--marca);
}
.cand[hidden] { display: none; }
.cand[data-estado="est-revision"]    { --marca: var(--alerta); }
.cand[data-estado="est-desconocido"] { --marca: var(--desconocido); }

.cand-cab { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e2) var(--e3); }
.cand-tipo {
  font: 600 var(--t-2xs)/var(--lh-2xs) var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--tinta-apagada);
}
.cand-nombre {
  font-size: var(--t-lg); line-height: var(--lh-md); font-weight: 600;
  letter-spacing: -.01em; overflow-wrap: anywhere;
}
/* «compartido» avisa de que tocarlo afecta a varias aplicaciones: es una advertencia, y por eso
   lleva marca de --alerta como las demás advertencias, no una etiqueta neutra. */
.cand-compartido {
  display: inline-flex; align-items: center; gap: var(--e2);
  font: 700 var(--t-2xs)/var(--lh-2xs) var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--alerta);
}
.cand-compartido::before {
  content: '';
  width: var(--carril-fino); height: 1.1em; border-radius: var(--r-chip);
  background: currentColor;
}
.cand-conf {
  margin-left: auto;
  font-size: var(--t-xs); line-height: var(--lh-xs);
  color: var(--tinta-apagada);
  font-variant-numeric: tabular-nums;
}

/* Las insignias del inventario comparten la base .insignia (marca de 2px + versalitas); solo
   cambian el color las que dicen algo que no es neutro. */
.insignia.est-revision    { color: var(--alerta); }
.insignia.est-desconocido { color: var(--desconocido); }
.insignia.est-detectado,
.insignia.est-incorporado,
.insignia.est-gestionado  { color: var(--tinta-suave); }
.insignia.est-apartado    { color: var(--tinta-apagada); }

.cand-aviso:not(:empty) { margin-top: var(--e3); }
.cand-acciones {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2);
  margin-top: var(--e3);
}
.cand-nota {
  display: block;
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-sm);
}
.cand-acciones .cand-nota { flex-basis: 100%; }
.cand > .cand-nota { margin-top: var(--e2); }

/* Las capacidades cuelgan de un <details> y se cargan al abrirlo. */
.cand-caps { margin-top: var(--e3); border-top: var(--filete) solid var(--linea); padding-top: var(--e3); }
.cand-caps-titulo {
  list-style: none; cursor: pointer;
  font: 600 var(--t-md)/var(--lh-md) var(--prosa);
  color: var(--acento);                       /* lo único pulsable de una ficha ya incorporada */
}
.cand-caps-titulo::-webkit-details-marker { display: none; }
.cand-caps-cuerpo { display: flex; flex-direction: column; gap: var(--e3); margin-top: var(--e3); }

.cap-grupo {
  display: flex; flex-direction: column; gap: var(--e1);
  background: var(--superficie);
  border: var(--filete) solid var(--linea); border-radius: var(--r-control);
  padding: var(--e3);
}
.cap-grupo-titulo { font-size: var(--t-md); line-height: var(--lh-md); font-weight: 700; }
.cap-grupo-ex {
  color: var(--tinta-suave);
  font-size: var(--t-sm); line-height: var(--lh-sm);
  margin-bottom: var(--e2);
}
/* Cada capacidad es una casilla con su código y su explicación. Altura táctil desde ya: esto se
   marca con el dedo tanto como con el ratón, y una casilla que se falla concede lo que no era. */
.cap {
  display: flex; align-items: flex-start; gap: var(--e2);
  min-height: var(--alto-tactil);
  padding: var(--e2);
  border-radius: var(--r-menudo);
  cursor: pointer;
}
.cap:hover { background: var(--elevada); }
.cap-texto { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e2); min-width: 0; }
.cap-codigo {
  font: 500 var(--t-sm)/var(--lh-sm) var(--mono);
  background: var(--hundida); color: var(--tinta);
  border-radius: var(--r-menudo); padding: .1em var(--e1);
}
.cap-desc { color: var(--tinta-suave); font-size: var(--t-sm); line-height: var(--lh-sm); }
.cap-aviso:not(:empty) { margin-top: var(--e2); }


/* ===== ENTRADA (F16, portada) ===== */
/* ==================================================================================================
   ENTRADA (sin sesión)
   La app entera es esto cuando no hay sesión, así que lleva la firma de la casa: la misma raya de
   acento de la marca, arriba del formulario.
   ================================================================================================== */

.entrada {
  display: flex; flex-direction: column; gap: var(--e3);
  max-width: 380px; margin: 12vh auto 0;
  padding: var(--e6);
  background: var(--elevada);
  border: var(--filete) solid var(--linea); border-radius: var(--r-burbuja);
  box-shadow: var(--sombra-2);
}
.entrada::before {
  content: '';
  width: var(--e5); height: var(--carril);
  border-radius: var(--r-chip);
  background: var(--acento);
  margin-bottom: var(--e1);
}
.entrada label {
  font: 600 var(--t-xs)/var(--lh-xs) var(--prosa);
  text-transform: uppercase; letter-spacing: var(--track-micro);
  color: var(--tinta-suave);
}
.entrada input {
  width: 100%;
  background: var(--hundida); color: var(--tinta);
  border: var(--filete) solid var(--borde); border-radius: var(--r-control);
  padding: var(--e3);
  font: 400 var(--t-md)/var(--lh-md) var(--prosa);
  outline: none;
}
.entrada .error {
  min-height: 1.2em;
  color: var(--mal);
  font-size: var(--t-sm); line-height: var(--lh-sm);
}
.entrada a { font-size: var(--t-sm); }


/* ===== FOCO (F16, portada) ===== */
/* ==================================================================================================
   FOCO — nada pierde su indicador.
   El anillo global vive en la base; aquí se corrige lo que ese anillo no puede saber solo:
     a) el radio — el anillo abraza la forma REAL de la pieza, no le impone la suya;
     b) el recorte — dentro de un contenedor con scroll (lista, popover, bloque de código) un
        anillo con offset positivo se corta o empuja el scroll: ahí va POR DENTRO;
     c) los campos sin outline propio: su indicador sube a 2px, porque 1px no es un anillo.
   El anillo va siempre en --acento, medido a ≥3:1 contra TODAS las superficies de ambos temas.
   ================================================================================================== */

button.primario:focus-visible, button.secundario:focus-visible, button.nueva:focus-visible,
.adjuntar:focus-visible, .adj-opcion:focus-visible,
.conv-abrir:focus-visible, .acciones-titulo:focus-visible, .sug:focus-visible,
.filtro:focus-visible, .tarjeta > summary:focus-visible, .cand-caps-titulo:focus-visible,
.buscar-conv:focus-visible, .inv-buscar:focus-visible, .inv-filtro:focus-visible,
.cand-entorno:focus-visible, .confirmar input:focus-visible,
.entrada input:focus-visible { border-radius: var(--r-control); }

.conv-btn:focus-visible, .turno-copiar:focus-visible,
.md-copiar:focus-visible, .conv-editar:focus-visible { border-radius: var(--r-menudo); }

.btn-lateral:focus-visible { border-radius: var(--r-control); }

/* b) anillo por dentro donde el de fuera se cortaría. */
.conversaciones .conv-abrir:focus-visible,
.conversaciones .conv-btn:focus-visible,
.conversaciones .conv-editar:focus-visible,
.sugerencias .sug:focus-visible,
.adj-menu .adj-opcion:focus-visible,
.md-bloque .md-copiar:focus-visible { outline-offset: -2px; }

/* c) campos con borde propio: el foco añade acento + halo. El campo de la frase de confirmación
      es el control más serio de la app —quien lo tiene enfocado está a punto de teclear una
      autorización— y no puede quedarse con un borde de 1px como único indicador. */
.confirmar input:focus-visible, .entrada input:focus-visible,
.buscar-conv:focus-visible, .inv-buscar:focus-visible, .conv-editar:focus-visible {
  border-color: var(--acento);
  box-shadow: var(--halo);
}
/* La paleta se abre con el campo ya enfocado; el subrayado de acento de 2px es su anillo. */
.paleta input:focus-visible {
  border-bottom-color: var(--acento);
  box-shadow: inset 0 -2px 0 var(--acento);
}

/* Lo que solo aparece al pasar el ratón tiene que aparecer también al llegar con Tab. */
.turno-copiar:focus-visible, .conv-btn:focus-visible { opacity: 1; }

/* Casillas de capacidad: el anillo nativo sigue siendo el indicador; la fila entera se ilumina
   para que se vea CUÁL de las doce está enfocada. Dos anillos concéntricos serían ruido. */
.cap:has(input:focus-visible) { background: var(--elevada); }


/* ===== MOVIMIENTO Y SU REDUCCIÓN (F16, portada) ===== */
/* ==================================================================================================
   MOVIMIENTO
   Cada animación contesta una sola pregunta: ¿qué le explica esto a quien mira? La que no la
   contesta, no está.

     1. Un turno NUEVO sube 8px con fundido (180 ms)  → «esto es nuevo, no estaba hace un segundo».
     2. Al pintar el hilo, las piezas se escalonan 30 ms, TOPE de 6 → «se lee en este orden». De la
        séptima en adelante entran ya puestas: escalonar cuarenta piezas no explica un orden, lo
        esconde detrás de una cortina que tarda un segundo en levantarse.
     3. Una tarjeta que se despliega crece con su ALTURA MEDIDA (200 ms) → «esto sale de aquí».
     4. El popover de «/» y la paleta escalan 0.98→1 con fundido (120 ms) → «esto viene de aquí».
     5. La barra lateral desliza con curva de SALIDA, no lineal → tiene inercia, es un objeto.
     6. La insignia de una acción que CAMBIÓ de estado da un pulso corto, una vez → «esto es lo
        que cambió». Un repintado seco esconde justo eso: la tarjeta se vuelve a dibujar idéntica
        salvo una palabra, y esa palabra es toda la noticia.

   LO QUE NO ESTÁ, Y POR QUÉ
     · Ningún rebote elástico: los dos puntos de control de --curva-salida tienen y ≤ 1. Esto
       administra servidores; un botón que rebota promete un juguete.
     · Ningún bucle salvo el latido del esqueleto, que solo existe mientras se espera y desaparece
       con el dato. Nada compite por la atención con lo que hay que leer.
     · Nada retrasa una respuesta: un turno nuevo entra con retraso CERO.
     · No se anima NI UN COLOR DE ESTADO. Ni la cadena, ni el carril, ni la insignia cruzan de un
       color a otro: un fotograma a medio camino entre --alerta y --bien es literalmente un estado
       que no existe, pintado como si existiera. Cambian de golpe; lo que se anima es la ATENCIÓN
       sobre el cambio (el pulso), no el cambio.

   RENDIMIENTO HONESTO — solo transform y opacity, con dos excepciones declaradas: la ALTURA del
   despliegue (que es el punto entero de la animación 3 y no se puede falsear con transform sin
   deformar el texto) y el margin-left de la barra en escritorio (la columna tiene que RECUPERAR
   el sitio; con transform la barra se iría y el hueco se quedaría).

   CLASES QUE PONE animacion.js Y NINGÚN MÓDULO EMITE: .nace, .ya-puesta, .pulso, .desplegando.
   Si ese fichero no se carga, todo esto degrada al escalonado por :nth-child y al plegado nativo.
   ================================================================================================== */

:root {
  /* Duraciones: son TIEMPOS, no espacio (por eso no salen de --e1..--e7). Cada una atada a lo que
     explica, no a un gusto. */
  --dur-pop: 120ms;         /* aparecer pegado a su origen: popover de «/» y paleta */
  --dur-entrada: 180ms;     /* un turno que llega */
  --dur-lateral: 180ms;     /* el mismo tiempo que un turno: la app tiene un solo pulso */
  --dur-despliegue: 200ms;  /* recorrer una altura medida pide algo más que aparecer */
  --dur-pulso: 320ms;       /* señalar un cambio: se tiene que ver entero y acabarse */
  --escalon: 30ms;          /* entre pieza y pieza al pintar el hilo */

  /* La curva de SALIDA: arranca a velocidad y frena al llegar. Imposible que rebote. */
  --curva-salida: cubic-bezier(.2, .8, .3, 1);

  /* Las dos magnitudes que la transformación mueve. En movimiento reducido se anulan abajo, así
     que ningún sitio tiene que acordarse de apagarlas por su cuenta. */
  --sube: 8px;   /* desde dónde entra un turno */
  --pop: .98;    /* desde qué escala nace un popover */
}

@keyframes entra-pieza {
  from { opacity: 0; transform: translateY(var(--sube)); }
  to   { opacity: 1; transform: translateY(0); }
}
/* La misma entrada sin transformación, para movimiento reducido. Es un fundido de LLEGADA: acaba
   en opacidad 1 y no atenúa nada que ya se estuviera leyendo. */
@keyframes entra-pieza-plana {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes nace-popover {
  from { opacity: 0; transform: scale(var(--pop)); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes nace-popover-plano {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* El pulso de la insignia se declara UNA vez, más abajo, junto al resto del movimiento.
   Aquí hubo una segunda declaración cuyo comentario prometía «sin pasarse de 1.06» mientras el
   navegador aplicaba la de abajo (1.12): dos @keyframes con el mismo nombre no conviven, gana el
   último, y el comentario del perdedor describía algo que no ocurría en ninguna pantalla. */

/* ---- 1 y 2 · la entrada de las piezas del hilo -------------------------------------------------
   `both` es obligatorio: sin él, una pieza con retraso se vería un instante YA PUESTA y luego
   saltaría atrás para empezar su entrada.
   El escalonado va por :nth-child y no por JavaScript a propósito: así funciona igual cuando
   animacion.js no está cargado. */
.tiempo > * { animation: entra-pieza var(--dur-entrada) var(--curva-salida) both; }
.tiempo > :nth-child(2) { animation-delay: calc(var(--escalon) * 1); }
.tiempo > :nth-child(3) { animation-delay: calc(var(--escalon) * 2); }
.tiempo > :nth-child(4) { animation-delay: calc(var(--escalon) * 3); }
.tiempo > :nth-child(5) { animation-delay: calc(var(--escalon) * 4); }
.tiempo > :nth-child(6) { animation-delay: calc(var(--escalon) * 5); }
.tiempo > :nth-child(n+7) { animation: none; }

/* Van DESPUÉS de las de :nth-child a propósito: misma especificidad (0,2,0), gana la última.
     .ya-puesta  pieza de un repintado en bloque más allá del tope → entra ya puesta.
     .nace       pieza que ha llegado sola a un hilo ya pintado → entra SIEMPRE, esté donde esté,
                 y sin retraso: una respuesta no espera a nadie. */
.tiempo > .ya-puesta { animation: none; }
.tiempo > .nace {
  animation: entra-pieza var(--dur-entrada) var(--curva-salida) both;
  animation-delay: 0s;
}

/* ---- 3 · el despliegue de una tarjeta ----------------------------------------------------------
   La clase la pone y la quita animacion.js alrededor de la transición. Aquí vive lo que es de la
   hoja: tiempo, curva, recorte mientras crece y el will-change ACOTADO — se va con la clase, que
   es lo que impide dejar una capa de composición viva en cada tarjeta de la pantalla. */
.tarjeta.desplegando {
  overflow: hidden;
  will-change: height;
  transition: height var(--dur-despliegue) var(--curva-salida);
}

/* ---- 4 · popover de «/» y paleta ---------------------------------------------------------------
   Los dos nacen al pasar de oculto a visible, así que basta con declarar la animación. No hay
   animación de SALIDA y es deliberado: cerrar tiene que ser instantáneo, o el siguiente gesto
   pelea con una caja que todavía se está yendo. */
.sugerencias:not([hidden]) {
  animation: nace-popover var(--dur-pop) var(--curva-salida) both;
  transform-origin: bottom center;   /* nace del borde del cajón, que es de donde sale de verdad */
}
.paleta[open] {
  animation: nace-popover var(--dur-pop) var(--curva-salida) both;
  transform-origin: center;          /* no cuelga de ningún control: nace de sí misma */
}
.paleta[open]::backdrop { animation: entra-pieza-plana var(--dur-pop) linear both; }
/* El menú del «+» nace igual que el popover de «/» y del mismo sitio: del borde del cajón. */
.adj-menu:not([hidden]) {
  animation: nace-popover var(--dur-pop) var(--curva-salida) both;
  transform-origin: bottom left;
}
/* El velo de soltar es un fundido de LLEGADA a secas (0→1): sin transformación, así que vale
   igual con movimiento reducido y no hace falta apagarlo abajo. */
.soltar:not([hidden]) { animation: entra-pieza-plana var(--dur-pop) linear both; }

/* ---- 6 · la insignia que cambió de estado ------------------------------------------------------
   La clase la pone animacion.js SOLO cuando el estado de esa acción es distinto del que enseñaba
   la última vez, y se la quita al acabar: es un aviso de una vez, no un adorno permanente. */
.insignia.pulso { animation: pulso-insignia var(--dur-pulso) var(--curva-salida) 1; }

/* ==================================================================================================
   MOVIMIENTO REDUCIDO — requisito, no extra.
   Se apaga TODO lo de transformación y quedan los fundidos de LLEGADA. Una excepción que no es
   estética sino MEDIDA: el pulso de la insignia NO se convierte en un parpadeo de opacidad. Se
   calculó qué contraste tiene la insignia compuesta con alfa sobre el fondo de la tarjeta y
   cualquier atenuación la saca de AA en el tema claro (a 0.90 de alfa ya baja de 4.5:1). Así que
   en movimiento reducido la insignia no se mueve y no se apaga — y no se pierde información,
   porque la información nunca estuvo en el pulso: está escrita en la propia insignia y en la
   cadena de peldaños.
   La regla general que sale de ese cálculo y vale para toda la hoja: un fundido de LLEGADA (0→1)
   es legítimo, porque no le quita legibilidad a nada que ya estuviera; una atenuación de lo que
   ya se lee (1→0.x) no lo es.
   ================================================================================================== */

@media (prefers-reduced-motion: reduce) {
  :root { --sube: 0px; --pop: 1; }

  .lateral { transition: none; }
  .principal { scroll-behavior: auto; }
  .conv-acciones, .turno-copiar, .cajon-linea,
  button.primario, button.secundario { transition: none; }

  .tiempo > *, .tiempo > .nace { animation-name: entra-pieza-plana; }
  .sugerencias:not([hidden]), .paleta[open],
  .adj-menu:not([hidden]) { animation-name: nace-popover-plano; }
  .adjuntar { transition: none; }
  .tarjeta.desplegando { transition: none; }
  .insignia.pulso { animation: none; }

  /* Los selectores del esqueleto repiten la especificidad de los de arriba a propósito: con
     `.cargando::after` a secas, la regla de `.conversaciones > .cargando::after` (más específica)
     seguiría ganando y el latido no se apagaría. Un «respeto» a reduced-motion que pierde en la
     cascada es peor que no haberlo escrito, porque parece cumplido. */
  .esqueleto,
  .conversaciones > .cargando::after,
  .principal > .cargando::after { animation: none; opacity: 1; }
}


