/* ============================================================================
   Kit de fluidez — guia-estilos/ux/ux.css
   ----------------------------------------------------------------------------
   Lo que hace que las apps de la suite se sientan iguales al usarlas, no solo
   al mirarlas: duraciones, foco, botón ocupado, entrada de modales, toast y el
   diálogo de confirmación. Va DESPUÉS del CSS de la app y no cambia nada en
   reposo: solo estados (foco, pulsado, ocupado) y transiciones.

   Usa los tokens del estándar (--navy, --gold, --ok, --err, --white, --gray-2,
   --r). Si la app no los define, cada var() trae su valor de respaldo.

   Se acompaña de ux.js. Ver README.md de esta carpeta.
   ========================================================================== */

:root {
  --ux-dur-fast: .15s;
  --ux-dur:      .2s;
  --ux-ease:     cubic-bezier(.2, .8, .2, 1);   /* salida suave: entra rápido y se posa */
  --ux-ease-in:  cubic-bezier(.4, 0, 1, 1);     /* para lo que se va */
  --ux-focus:    var(--gold, #FF8300);
  /* --ux-toast-top NO se define aquí: este archivo va después del CSS de la
     app y la pisaría. La app la declara en su :root (alto del header + 16px);
     si no lo hace, vale 76px. */
}

/* ── Cambio de página suave ─────────────────────────────────────────────────
   En las apps multipágina (PHP que pinta en servidor), el navegador funde una
   página con la siguiente en vez de pasar por blanco. Donde no hay soporte se
   ignora sin más. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: .18s; }
}

/* ── Foco de teclado ────────────────────────────────────────────────────────
   :where() deja la especificidad en cero: si la app ya tiene su propio anillo,
   manda el de la app. Solo aparece con teclado (focus-visible), no al clicar.
   Los campos de texto (input, select, textarea) quedan fuera: el navegador los
   marca focus-visible también con el mouse, y cada app ya los resalta con su
   borde dorado; con el anillo encima se veía un borde doble. */
:where(a, button, [role="button"], summary, [tabindex]:not(input):not(select):not(textarea),
       input[type="checkbox"], input[type="radio"], input[type="range"],
       input[type="submit"], input[type="button"], input[type="reset"]):focus-visible {
  outline: 2px solid var(--ux-focus);
  outline-offset: 2px;
}

/* ── Respuesta al pulsar ────────────────────────────────────────────────────
   Un hundimiento de 1px: el botón "contesta" aunque la acción tarde. */
:where(button, .btn, [role="button"]):active:not(:disabled):not([aria-disabled="true"]) {
  transform: translateY(1px);
}

/* ── Botón ocupado ──────────────────────────────────────────────────────────
   ux.js pone aria-busy="true" + disabled mientras se envía. El texto queda
   (no salta el ancho) y se le suma un spinner a la derecha. */
:where(button, .btn, input[type="submit"])[aria-busy="true"] {
  cursor: progress;
  opacity: .8;
}
:where(button, .btn)[aria-busy="true"]::after {
  content: "";
  display: inline-block;
  width: .9em;
  height: .9em;
  margin-left: .5em;
  vertical-align: -.12em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: ux-gira .7s linear infinite;
}
@keyframes ux-gira { to { transform: rotate(360deg); } }

/* ── Entrada de modales ─────────────────────────────────────────────────────
   Una animación CSS se reinicia cada vez que el elemento pasa de display:none
   a visible, así que esto anima los modales existentes sin tocar el JS que
   los abre (style.display, hidden, clases). La app indica cuáles son con
   .ux-modal en el overlay y .ux-modal-caja en la tarjeta, o añade aquí sus
   selectores. */
@keyframes ux-funde   { from { opacity: 0; } }
@keyframes ux-aparece { from { opacity: 0; transform: translateY(8px) scale(.98); } }
.ux-modal      { animation: ux-funde   var(--ux-dur) var(--ux-ease); }
.ux-modal-caja { animation: ux-aparece var(--ux-dur) var(--ux-ease); }

/* Mientras hay un modal abierto el fondo no se desplaza, y la barra de scroll
   deja su hueco reservado para que la página no salte de lado. */
html.ux-sin-scroll { overflow: hidden; scrollbar-gutter: stable; }

/* ── Toast ──────────────────────────────────────────────────────────────────
   El aviso de Control-de-Horas (fondo navy, filo verde o rojo, icono y ×),
   arriba al centro. Mismas clases que el estándar (.toasts / .toast-aviso),
   así que reemplaza la posición de esquina de ciso-ui.css. */
.toasts {
  position: fixed;
  top: var(--ux-toast-top, 76px);
  left: 50%;
  right: auto;
  bottom: auto;
  transform: translateX(-50%);
  z-index: 1090;              /* sobre el header (100) y los modales (500-1050) */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: max-content;
  max-width: min(460px, calc(100vw - 32px));
  pointer-events: none;       /* el contenedor no se traga clics; el aviso sí */
}
.toast-aviso {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  background: var(--navy, #192F50);
  color: var(--navy-on, #fff);
  border: 2px solid var(--gray-2, #d9d3c7);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(25, 47, 80, .30);
  font-size: 14px;
  line-height: 1.45;
  text-align: left;
  animation: ux-toast-entra .24s var(--ux-ease);
}
.toast-aviso.ok  { border-color: var(--ok-fill, #2ecc71); }
.toast-aviso.err { border-color: var(--err-line, #e74c3c); }
/* El icono repite lo que dice el borde: quien no distingue rojo de verde
   tiene igual el visto o la exclamación. */
.toast-aviso .toast-icono { flex: 0 0 auto; width: 18px; height: 18px; margin-top: 1px; }
.toast-aviso.ok  .toast-icono { color: #6ddc9b; }
.toast-aviso.err .toast-icono { color: #ff9d92; }
.toast-aviso .toast-texto { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.toast-aviso .toast-cerrar {
  flex: 0 0 auto;
  display: inline-flex;
  background: none;
  border: 0;
  padding: 2px;
  margin: -2px -4px 0 0;
  color: inherit;
  opacity: .65;
  cursor: pointer;
  transition: opacity var(--ux-dur-fast) ease;
}
.toast-aviso .toast-cerrar:hover { opacity: 1; }
.toast-aviso .toast-cerrar svg { width: 16px; height: 16px; }
@keyframes ux-toast-entra { from { opacity: 0; transform: translateY(-12px); } }
.toast-aviso.saliendo {
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity var(--ux-dur) var(--ux-ease-in), transform var(--ux-dur) var(--ux-ease-in);
}
@media (max-width: 575.98px) {
  .toasts { top: 12px; width: calc(100vw - 24px); }
  .toast-aviso { width: 100%; }
}

/* ── Diálogo de confirmación (ux.confirmar) ─────────────────────────────────
   Reemplaza al confirm() del navegador con la estética de la suite. */
.ux-dialogo {
  position: fixed;
  inset: 0;
  z-index: 1080;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(25, 47, 80, .65);
  animation: ux-funde var(--ux-dur) var(--ux-ease);
}
.ux-dialogo-caja {
  width: 100%;
  max-width: 420px;
  background: var(--white, #fff);
  color: var(--text, #1a1a2e);
  border-radius: 10px;
  padding: 28px;
  box-shadow: 0 24px 80px rgba(0, 0, 0, .45);
  animation: ux-aparece var(--ux-dur) var(--ux-ease);
}
.ux-dialogo-titulo {
  margin: 0 0 8px;
  font-size: 17px;
  font-weight: 800;
  color: var(--navy, #192F50);
}
.ux-dialogo-texto { margin: 0; font-size: 14px; line-height: 1.55; color: var(--sub, #5a6070); white-space: pre-line; }
.ux-dialogo-acciones { display: flex; justify-content: flex-end; gap: 10px; margin-top: 24px; flex-wrap: wrap; }
.ux-dialogo-btn {
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  padding: 10px 18px;
  border-radius: var(--r, 6px);
  border: 1px solid var(--gray-2, #d9d3c7);
  background: var(--white, #fff);
  color: var(--text, #1a1a2e);
  cursor: pointer;
  transition: background-color var(--ux-dur-fast) ease, border-color var(--ux-dur-fast) ease, color var(--ux-dur-fast) ease;
}
.ux-dialogo-btn:hover { background: var(--cream, #f5f0e8); }
.ux-dialogo-btn.es-primario { background: var(--navy, #192F50); border-color: var(--navy, #192F50); color: var(--navy-on, #fff); }
.ux-dialogo-btn.es-primario:hover { background: var(--navy-mid, #264168); }
.ux-dialogo-btn.es-peligro { background: var(--err, #c0392b); border-color: var(--err, #c0392b); color: #fff; }
.ux-dialogo-btn.es-peligro:hover { filter: brightness(.92); }
.ux-dialogo.saliendo { opacity: 0; transition: opacity var(--ux-dur-fast) var(--ux-ease-in); }

/* ── Indicador de guardado (ux.guardado) ────────────────────────────────────
   Una etiqueta discreta para el autoguardado: "Guardando…" → "Guardado". */
.ux-guardado {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--sub, #5a6070);
  opacity: 0;
  transition: opacity var(--ux-dur) ease;
}
.ux-guardado.es-visible { opacity: 1; }
.ux-guardado.es-ok  { color: var(--ok, #1e8449); }
.ux-guardado.es-err { color: var(--err, #c0392b); }

/* ── Movimiento reducido ────────────────────────────────────────────────────
   Quien pidió menos movimiento en su sistema no ve animaciones ni
   desplazamientos suaves. Los cambios siguen ocurriendo, solo que al instante. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
