/* Componentes visuales reutilizables. Todos requieren prefijo eva-. */
.eva-surface-card {
  background: var(--eva-surface);
  border: 1px solid var(--eva-border);
  border-radius: var(--eva-radius);
  box-shadow: var(--eva-shadow-sm);
  transition: border-color var(--eva-duration-fast) ease,
    box-shadow var(--eva-duration-fast) ease,
    transform var(--eva-duration-fast) ease;
}

.eva-surface-card.eva-interactive:hover {
  border-color: rgba(37, 99, 235, 0.3);
  box-shadow: var(--eva-shadow);
  transform: translateY(-0.2rem);
}

.eva-section-heading {
  align-items: flex-end;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.25rem;
  justify-content: space-between;
  margin-bottom: 1.25rem;
}

.eva-section-heading__copy {
  max-width: 48rem;
}

.eva-section-heading__eyebrow {
  color: var(--eva-primary);
  display: block;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  margin-bottom: 0.35rem;
  text-transform: uppercase;
}

.eva-status {
  align-items: center;
  border-radius: 999px;
  display: inline-flex;
  font-size: 0.72rem;
  font-weight: 600;
  gap: 0.35rem;
  line-height: 1;
  min-height: 1.8rem;
  padding: 0.4rem 0.7rem;
}

.eva-status--success { background: var(--eva-success-tint); color: var(--eva-success-ink); }
.eva-status--warning { background: var(--eva-warning-tint); color: #b45309; }
.eva-status--danger { background: var(--eva-danger-tint); color: #b91c1c; }
.eva-status--info { background: var(--eva-surface-info); color: var(--eva-primary-dark); }
.eva-status--neutral { background: var(--eva-border-soft); color: var(--eva-text-soft); }

.eva-empty-state {
  align-items: center;
  background: var(--eva-surface-soft);
  border: 1px dashed var(--eva-border-strong);
  border-radius: var(--eva-radius);
  color: var(--eva-muted);
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 14rem;
  padding: 2rem;
  text-align: center;
}

.eva-empty-state__icon {
  align-items: center;
  background: var(--eva-surface-info);
  border-radius: 50%;
  color: var(--eva-primary);
  display: inline-flex;
  height: 3.5rem;
  justify-content: center;
  margin-bottom: 1rem;
  width: 3.5rem;
}

.eva-skeleton {
  animation: evaSkeleton 1.4s ease-in-out infinite;
  background: linear-gradient(90deg, var(--eva-border-soft) 25%, var(--eva-surface-soft) 50%, var(--eva-border-soft) 75%);
  background-size: 200% 100%;
  border-radius: 0.5rem;
  min-height: 1rem;
}

.eva-reveal {
  animation: evaReveal var(--eva-duration) ease-out both;
}

@keyframes evaReveal {
  from { opacity: 0; transform: translateY(0.5rem); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes evaSkeleton {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

@media (max-width: 575.98px) {
  .eva-section-heading {
    align-items: stretch;
  }

  .eva-section-heading > .btn,
  .eva-section-heading > .btn-group {
    width: 100%;
  }
}

/* ==========================================================================
   Campo de busqueda con filtrado (searchable-select)
   --------------------------------------------------------------------------
   El JS de app.js reemplaza un <select> por un campo de texto que filtra las
   opciones mientras se escribe. El <select> original permanece en el DOM
   porque es lo que se envia en el formulario, pero debe quedar oculto: sin
   estas reglas el usuario ve la lista desplegable Y el campo de busqueda uno
   debajo del otro, y los resultados salen como botones grises del navegador
   que empujan el formulario hacia abajo en vez de flotar sobre el.
   ========================================================================== */

.eva-app .searchable-select-native {
  display: none !important;
}

.eva-app .searchable-select {
  position: relative;
}

.eva-app .searchable-select-input {
  background: var(--eva-surface-soft);
  border: 1px solid #d7e0ec;
  border-radius: .7rem;
  box-sizing: border-box;
  color: var(--eva-text);
  font-size: 1rem;
  min-height: 2.65rem;
  padding: .375rem 2.25rem .375rem .75rem;
  width: 100%;
}

/* Lupa dibujada con un gradiente conico no: se usa una mascara SVG en linea
   para no depender de Font Awesome dentro de un campo generado por JS. */
.eva-app .searchable-select::after {
  background: var(--eva-muted);
  content: "";
  height: 1rem;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z'/%3E%3C/svg%3E") no-repeat center / contain;
  opacity: .55;
  pointer-events: none;
  position: absolute;
  right: .8rem;
  top: 1.325rem;
  transform: translateY(-50%);
  width: 1rem;
}

.eva-app .searchable-select-input:hover {
  border-color: #b6c4d6;
}

.eva-app .searchable-select-input:focus {
  background: var(--eva-surface);
  border-color: var(--eva-primary);
  box-shadow: 0 0 0 .22rem rgba(37, 99, 235, .13);
  outline: none;
}

.eva-app .searchable-select-input::placeholder {
  color: var(--eva-muted);
}

/* Flota sobre el formulario: si ocupara espacio, abrir la lista desplazaria
   los campos siguientes cada vez que el usuario escribe una letra. */
.eva-app .searchable-select-options {
  background: var(--eva-surface);
  border: 1px solid var(--eva-border);
  border-radius: .7rem;
  box-shadow: var(--eva-shadow-md, 0 .75rem 1.75rem rgba(15, 30, 55, .16));
  left: 0;
  margin-top: .3rem;
  max-height: 15rem;
  overflow-y: auto;
  padding: .3rem;
  position: absolute;
  right: 0;
  top: 100%;
  z-index: 30;
}

.eva-app .searchable-select-option {
  background: transparent;
  border: 0;
  border-radius: .5rem;
  color: var(--eva-text);
  cursor: pointer;
  display: block;
  font-size: .92rem;
  line-height: 1.35;
  padding: .5rem .65rem;
  text-align: left;
  width: 100%;
}

.eva-app .searchable-select-option:hover,
.eva-app .searchable-select-option:focus-visible {
  background: rgba(37, 99, 235, .1);
  color: var(--eva-primary);
  outline: none;
}

.eva-app .searchable-select-empty {
  color: var(--eva-muted);
  font-size: .85rem;
  padding: .6rem .65rem;
}

/* Dentro de un modal la lista debe superar la pila del propio modal. */
.eva-app .modal .searchable-select-options,
.eva-app .eva-modal .searchable-select-options {
  z-index: 1080;
}

@media (max-width: 575.98px) {
  .eva-app .searchable-select-options {
    max-height: 12rem;
  }
}

/* =====================================================================
   Campo de archivo
   El JS (enhanceFileInputs) construye la estructura pero no habia ni una
   regla que la vistiera: se veia como texto suelto junto a un boton del
   navegador. Aqui se convierte en una zona de carga con estado propio.
   ===================================================================== */
.eva-app .file-upload-control {
  align-items: center;
  background: var(--eva-surface-soft);
  border: 1.5px dashed #c3d0e2;
  border-radius: .7rem;
  cursor: pointer;
  display: flex;
  gap: .75rem;
  min-height: 3.4rem;
  padding: .7rem .85rem;
  transition: border-color .15s ease, background-color .15s ease;
  width: 100%;
}
.eva-app .file-upload-control:hover { background: var(--eva-surface-soft); border-color: var(--eva-primary); }
.eva-app .file-upload-control:focus-within {
  border-color: var(--eva-primary);
  border-style: solid;
  box-shadow: 0 0 0 .22rem rgba(37,99,235,.13);
}

/* El input real se oculta pero sigue siendo accesible por teclado. */
.eva-app .file-upload-native {
  height: 100%;
  inset: 0;
  opacity: 0;
  position: absolute;
  width: 100%;
}
.eva-app .file-upload-control { position: relative; }

.eva-app .file-upload-mark {
  align-items: center;
  background: var(--eva-surface-info);
  border-radius: .5rem;
  color: var(--eva-primary);
  display: inline-flex;
  flex: 0 0 auto;
  height: 2.1rem;
  justify-content: center;
  width: 2.1rem;
}
.eva-app .file-upload-mark svg { display: block; }

/* min-width:0 es lo que evita que un nombre de archivo largo estire el
   campo y descoloque el boton: sin el, el texto no se deja recortar. */
.eva-app .file-upload-copy {
  display: grid;
  flex: 1 1 auto;
  gap: .1rem;
  min-width: 0;
}
.eva-app .file-upload-copy strong {
  font-size: .86rem;
  font-weight: 600;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.eva-app .file-upload-name {
  color: var(--eva-muted);
  font-size: .76rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.eva-app .file-upload-action {
  background: #fff;
  border: 1px solid #cfdaea;
  border-radius: .5rem;
  color: var(--eva-primary);
  flex: 0 0 auto;
  font-size: .78rem;
  font-weight: 600;
  margin-left: auto;
  padding: .42rem .7rem;
  white-space: nowrap;
}
.eva-app .file-upload-control:hover .file-upload-action {
  background: var(--eva-primary);
  border-color: var(--eva-primary);
  color: #fff;
}

/* Con archivo elegido el borde deja de ser punteado: la carga ya ocurrio. */
.eva-app .file-upload-control.tiene-archivo {
  background: #f2f9f5;
  border-color: #4c9a72;
  border-style: solid;
}
.eva-app .file-upload-control.tiene-archivo .file-upload-mark { background: #dff0e6; color: #2f7d55; }

/* Archivo mas grande de lo que acepta el servidor: se avisa antes de subir,
   no despues de esperar toda la transferencia. */
.eva-app .file-upload-control.archivo-excedido {
  background: var(--eva-danger-tint);
  border-color: var(--eva-danger);
  border-style: solid;
}
.eva-app .file-upload-control.archivo-excedido .file-upload-mark { background: #fee2e2; color: var(--eva-danger); }
.eva-app .file-upload-control.archivo-excedido .file-upload-copy strong { color: var(--eva-danger); }

@media (max-width: 480px) {
  .eva-app .file-upload-control { flex-wrap: wrap; }
  .eva-app .file-upload-action { margin-left: 0; width: 100%; text-align: center; }
}

/* ---------------------------------------------------------------------
   Formularios: jerarquia mas clara entre etiqueta, campo y ayuda.
   --------------------------------------------------------------------- */
.eva-app .field > label {
  align-items: center;
  display: flex;
  gap: .35rem;
  letter-spacing: .01em;
}
/* Marca discreta en los campos obligatorios. */
.eva-app .field:has(> input[required], > select[required], > textarea[required]) > label::after {
  color: var(--eva-danger);
  content: "*";
  font-size: .9em;
}

/* =====================================================================
   Formularios en modal: mismo acabado en toda la aplicacion.
   El formulario de contenido del aula ya tenia cabecera y pie fijos y
   campos agrupados en modulos; aqui esa presentacion pasa a ser la
   general para que ningun formulario quede como campos sueltos.
   Las reglas de courses.css son mas especificas y siguen mandando en
   la pagina de clases.
   ===================================================================== */
.eva-app dialog.modal .modal-panel.form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-height: calc(100vh - 2rem);
  overflow: auto;
}

/* La cabecera y los botones quedan a la vista aunque el formulario sea
   largo y haya que desplazarse. */
.eva-app dialog.modal .modal-panel.form > .modal-header {
  background: #fff;
  flex: 0 0 auto;
  position: sticky;
  top: calc(-1 * var(--eva-modal-space, 1rem));
  z-index: 2;
}
.eva-app dialog.modal .modal-panel.form > .modal-header h2 {
  font-size: 1.08rem;
  letter-spacing: -.01em;
}

.eva-app dialog.modal .modal-panel.form > .form-actions:last-child {
  background: var(--eva-surface-soft);
  border-top: 1px solid var(--eva-border);
  bottom: calc(-1 * var(--eva-modal-space, 1rem));
  box-sizing: border-box;
  flex: 0 0 auto;
  justify-content: flex-end;
  margin: .25rem calc(-1 * var(--eva-modal-space, 1rem)) calc(-1 * var(--eva-modal-space, 1rem));
  padding: .9rem var(--eva-modal-space, 1rem);
  position: sticky;
  width: calc(100% + var(--eva-modal-space, 1rem) * 2);
  z-index: 2;
}
.eva-app dialog.modal .modal-panel.form > .form-actions:last-child .btn { min-width: 7.5rem; }

/* ---------------------------------------------------------------------
   Modulos de formulario reutilizables: un bloque con titulo agrupa los
   campos relacionados en lugar de apilarlos sueltos.
   La separacion sale del gap de la rejilla porque eva-pages.css fuerza
   margin-bottom:0 !important en .field.
   --------------------------------------------------------------------- */
.eva-app .form-section {
  background: var(--eva-surface-soft);
  border: 1px solid var(--eva-border);
  border-radius: .85rem;
  display: grid;
  gap: .75rem;
  padding: 1rem;
}
.eva-app .form-section[hidden] { display: none !important; }

.eva-app .form-section-title {
  color: var(--eva-primary);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .05em;
  margin: 0;
  text-transform: uppercase;
}

.eva-app .form-section-grid {
  align-items: start;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
.eva-app .form-section-grid > .field { margin: 0; min-width: 0; }
.eva-app .form-section-grid textarea { min-height: 5.5rem; }
/* Campo que ocupa la fila completa (textos largos, avisos). */
.eva-app .form-field-wide { grid-column: 1 / -1; }

@media (max-width: 640px) {
  .eva-app .form-section { padding: .85rem; }
  .eva-app .form-section-grid { gap: .85rem; }
  .eva-app dialog.modal .modal-panel.form > .form-actions:last-child { flex-direction: column-reverse; }
  .eva-app dialog.modal .modal-panel.form > .form-actions:last-child .btn { min-width: 0; width: 100%; }
}

/* El campo de archivo es un <label> y por eso heredaba el aspecto de las
   etiquetas (tamano, peso, margen inferior). Aqui se le devuelve el suyo. */
.eva-app .field > label.file-upload-control,
.eva-app label.file-upload-control.form-label {
  color: inherit;
  font-size: 1rem;
  font-weight: 400;
  margin-bottom: 0;
}

/* =====================================================================
   Ventana de vista previa de documentos.
   Solo se muestra PDF, que el navegador dibuja por su cuenta. El resto de
   formatos se ofrecen para descarga, asi que aqui no hace falta nada mas
   que el marco y su encuadre.
   ===================================================================== */
/* Una pagina A4 mide unos 794 px mas margenes: el modal se hace lo
   bastante ancho para que no aparezca scroll horizontal. */
.eva-app dialog.doc-preview-modal { width: min(72rem, calc(100vw - 2rem)); }
.eva-app .doc-preview-panel {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 3rem);
  min-height: min(32rem, calc(100vh - 3rem));
}
.eva-app .doc-preview-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

.eva-app .doc-preview-frame {
  border: 1px solid var(--eva-border);
  border-radius: .6rem;
  display: block;
  height: 100%;
  min-height: 26rem;
  width: 100%;
}

/* Las utilidades "subtle" de Bootstrap combinan un fondo muy claro con el
   color de marca a plena saturacion: #2563eb sobre #cfe2ff da 3.93 y no
   llega a AA. Se usa el azul oscuro, que sube a 5.10. */
.eva-app .bg-primary-subtle.text-primary,
.eva-app .badge.bg-primary-subtle { color: var(--eva-primary-dark) !important; }
