/* Academy Liquid Glass — el cristal que no cabe en un token.

   Cuatro bloques, en este orden:
     A. Receta del cristal (superficies de la interfaz)
     B. Lienzo con aurora: el «amanecer frío» de la zona de contenido
     C. Acceso: la galaxia de Nexus teñida de amanecer, marca a la izquierda y tarjeta de cristal ahumado
     D. Portada de la ficha del catálogo

   La receta A, B y D sale de «Alumne Flex Liquid Glass» (themes/alumneflexglass): mismas
   reglas y mismos motivos, con la paleta de Academy. El acceso (C) es propio de este tema.

   OJO CON EL ORDEN DE CARGA: templates/element/head.php pinta theme.css y este fichero
   ANTES de alumneflex-ui.css. Con la misma especificidad ganaría la capa común, así que
   cada selector lleva `:root` delante para subir un punto.

   TRES REGLAS QUE ESTE FICHERO NO PUEDE ROMPER (y por qué):

   1. `backdrop-filter` NUNCA sobre un contenedor que pueda tener dentro un modal.
      Además de desenfocar, convierte al elemento en bloque contenedor de sus hijos
      `position:fixed` y en contexto de apilamiento: un `.modal` de Bootstrap que viva
      dentro de una tarjeta quedaría recortado por ella y por debajo de su velo (que se
      inyecta en <body>). Por eso el cristal de las tarjetas va en un `::before` con
      `z-index:-1`. Solo llevan el filtro directo los elementos que YA flotan.

   2. Nada de `isolation` ni `z-index` en las tarjetas por el mismo motivo.

   3. `--surface-card` NO es el cristal (ver tokens/colors.css): el cristal es
      `--glass-bg` y se aplica aquí, selector a selector. */


/* =====================================================================================
   A. RECETA DEL CRISTAL
   ===================================================================================== */

/* --- A.1 Tarjetas y paneles: cristal en el ::before --------------------------------- */
:root .af-card,
:root .af-panel,
:root .af-empty,
:root .af-recent-overlay {
  position: relative;
  background: transparent;
  border: 1px solid var(--glass-border);
  box-shadow: 0 0 0 1px var(--glass-edge), var(--glass-shadow);
}
:root .af-card::before,
:root .af-panel::before,
:root .af-empty::before,
:root .af-recent-overlay::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: var(--glass-sheen), var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: inset 0 1px 0 var(--glass-highlight), inset 0 -1px 0 var(--glass-lowlight);
  pointer-events: none;
}
/* El estado vacío conserva su borde discontinuo: es lo que dice «aquí no hay nada». */
:root .af-empty { border-style: dashed; border-color: var(--border-default); }

/* Al pasar por encima, la lámina se levanta. */
:root .af-card-interactive:hover {
  box-shadow: 0 0 0 1px var(--glass-edge), var(--glass-shadow-lift);
}

/* Panel oscuro y portada de bienvenida de los paneles (templates/Dashboard/*.php y
   element/dashboard/welcome.php): un acceso en miniatura. Charcoal con dos luces que se
   mueven por dentro —el naranja de firma arriba a la derecha, el azul de sistema abajo a
   la izquierda— y las cifras en cristal ahumado.

   `isolation` y `overflow:hidden` son seguros AQUÍ y solo aquí: ninguna de las plantillas
   mete un modal ni un desplegable en este panel.

   La capa mide un 160 % del panel (inset -30 %): un punto del panel en X % cae en la capa
   en (X + 30) / 1,6 %. Sin `filter: blur()`: degradados ya suaves movidos con `transform`. */
:root .af-panel-ink {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background:
    radial-gradient(110% 90% at 0% 0%, #1E2A44 0%, rgba(30,42,68,0) 60%),
    linear-gradient(160deg, #1A2337 0%, #0B1120 100%);
  border: 1px solid rgba(255,255,255,.14);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.18), var(--shadow-lg);
}
:root .af-panel-ink::before,
:root .af-panel-ink::after {
  content: "";
  position: absolute; inset: -30%; z-index: -1;
  pointer-events: none;
  will-change: transform;
}
:root .af-panel-ink::before {
  background:
    radial-gradient(30% 36% at 70% 36%, rgba(255,106,0,.55), rgba(255,106,0,0) 70%),
    radial-gradient(18% 20% at 80% 26%, rgba(255,212,90,.30), rgba(255,212,90,0) 70%);
  animation: ag-ink-a calc(var(--dur-blob) * 1.2) var(--ease-liquid) infinite alternate;
}
:root .af-panel-ink::after {
  background: radial-gradient(32% 32% at 34% 74%, rgba(37,99,235,.45), rgba(37,99,235,0) 70%);
  animation: ag-ink-b calc(var(--dur-blob) * 1.5) var(--ease-liquid) infinite alternate;
}
@keyframes ag-ink-a {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  50%  { transform: translate3d(-7%, 6%, 0) rotate(-10deg) scale(1.12, .92); }
  100% { transform: translate3d(-3%, -5%, 0) rotate(8deg) scale(.94, 1.1); }
}
@keyframes ag-ink-b {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  50%  { transform: translate3d(8%, -5%, 0) rotate(12deg) scale(1.1, .9); }
  100% { transform: translate3d(-5%, -8%, 0) rotate(-6deg) scale(.92, 1.12); }
}
:root .af-panel-ink .af-muted,
:root .af-hero-sub { color: rgba(255,255,255,.86); }

/* Las cifras: cristal ahumado. `brightness` baja la luz que pasa para que el blanco se lea
   aunque la mancha naranja cruce justo por detrás. Filtro directo: hojas sin flotantes. */
:root .af-hero-stat {
  position: relative;
  border-radius: var(--radius-card);
  background: linear-gradient(160deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,.04) 60%, rgba(255,255,255,.08) 100%),
              rgba(11,17,32,.32);
  -webkit-backdrop-filter: blur(18px) saturate(160%) brightness(.8);
  backdrop-filter: blur(18px) saturate(160%) brightness(.8);
  border: 1px solid rgba(255,255,255,.24);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.50),
    inset 0 -1px 0 rgba(255,255,255,.08),
    inset 0 16px 32px -20px rgba(255,255,255,.18),
    0 14px 30px -16px rgba(3,7,18,.70);
  transition: var(--transition-surface), border-color var(--dur-base) var(--ease-out);
}
:root .af-hero-stat::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: linear-gradient(115deg, rgba(255,255,255,.20) 0%, rgba(255,255,255,0) 32%);
  pointer-events: none;
}
:root .af-hero-stat:hover {
  transform: translateY(-2px);
  border-color: rgba(255,255,255,.40);
}
:root .af-hero-stat-value { font-size: var(--text-2xl); }
:root .af-hero-stat-label { color: rgba(255,255,255,.84); }

:root .af-hero-live {
  background: rgba(255,255,255,.12);
  -webkit-backdrop-filter: blur(12px) saturate(150%);
  backdrop-filter: blur(12px) saturate(150%);
  border: 1px solid rgba(255,255,255,.22);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.30);
}
:root a.af-hero-live:hover { background: rgba(255,255,255,.20); }

/* --- A.2 Superficies que YA flotan: filtro directo, cristal denso ------------------- */
:root .modal-content,
:root .dropdown-menu,
:root .offcanvas,
:root .popover,
:root .toast,
:root .af-gs-panel,
:root .af-config-search-results {
  background: var(--glass-sheen), var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur-strong);
  backdrop-filter: var(--glass-blur-strong);
  border: 1px solid var(--glass-border);
  box-shadow: inset 0 1px 0 var(--glass-highlight), 0 0 0 1px var(--glass-edge), var(--shadow-xl);
}
:root .modal { --bs-modal-bg: transparent; --bs-modal-border-color: var(--glass-border); }
:root .dropdown-menu { --bs-dropdown-bg: transparent; }
:root .offcanvas { --bs-offcanvas-bg: transparent; }
:root .af-gs-loading { background: var(--glass-bg-strong); }
:root .modal-backdrop { -webkit-backdrop-filter: blur(8px) saturate(120%); backdrop-filter: blur(8px) saturate(120%); }

@media (max-width: 767.98px) {
  :root .af-filterbar,
  :root .af-bulkbar,
  :root .af-catalog-bar,
  :root .af-config-search.is-stuck {
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur-strong);
    backdrop-filter: var(--glass-blur-strong);
  }
  /* La barra de filtros es casi siempre un `.af-panel`, y la receta del cristal (A.1) le
     pone `position: relative` con `:root` delante, que gana al `sticky` de la capa común.
     Relativa, su `top: var(--topbar-h)` la BAJA 64 px: hueco encima y la primera tarjeta
     tapada. Se devuelve el `sticky` con el mismo peso. */
  :root .af-filterbar { position: sticky; }
  /* Y en vez de la franja recta de lado a lado de la capa común, una cápsula de cristal
     que flota bajo la barra superior con un respiro, como la barra superior del
     escritorio. Por el respiro se ve pasar el contenido: en este tema es lo buscado
     (en la capa común se leería como un fallo de pintado, aquí es el cristal).
     Una sola cara: la del propio elemento, con el ::before de la receta apagado para
     no sumar dos desenfoques. Abierta, con los filtros desplegados, pasa a tarjeta. */
  :root { --ag-bar-gap: 8px; }
  :root .af-filterbar {
    top: calc(var(--topbar-h) + var(--ag-bar-gap));
    margin-inline: 0;
    max-height: calc(100dvh - var(--topbar-h) - 2 * var(--ag-bar-gap));
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-pill);
    background: var(--glass-sheen), var(--glass-bg-strong);
    box-shadow: inset 0 1px 0 var(--glass-highlight), 0 0 0 1px var(--glass-edge), var(--glass-shadow);
  }
  :root .af-filterbar::before { content: none; }
  :root .af-filterbar .af-panel-body,
  :root form.af-filterbar { padding: 6px 10px; }
  :root .af-filterbar.is-open { border-radius: var(--radius-card); }
  :root .af-filterbar.is-open .af-panel-body,
  :root form.af-filterbar.is-open { padding: var(--space-3) var(--space-4); }
  /* El campo, píldora como los dos botones redondos que lo flanquean. */
  :root .af-filterbar-main-row .form-control { border-radius: var(--radius-pill); }
  /* La barra de acciones en bloque se cuelga debajo: también con su respiro. */
  :root .af-bulkbar { top: calc(var(--topbar-h) + var(--filterbar-h, 0px) + 2 * var(--ag-bar-gap)); }
  /* El panel que solo envuelve un listado apilado deja de ser tarjeta en el teléfono
     (alumneflex-ui.css le quita fondo, borde y sombra): aquí el cristal vive en el
     ::before, que esa regla no alcanza, y quedaba una lámina recta tras las fichas.
     Mismos selectores que la capa común. */
  :root .af-panel:not(:has(> .af-panel-head)):has(> .af-table-wrap > .af-table-stack)::before,
  :root .af-panel:not(:has(> .af-panel-head)):has(> .af-panel-body > .af-table-stack)::before,
  :root .af-panel:not(:has(> .af-panel-head)):has(> .af-table-stack)::before,
  :root .af-panel.af-panel-flush-mobile::before { content: none; }
}

/* --- A.3 Cromo: barra lateral y barra superior como piezas flotantes ---------------- */
@media (min-width: 992px) {
  :root .af-sidebar {
    top: 12px;
    height: calc(100vh - 24px);
    margin: 12px 0 12px 12px;
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-shell);
    background: transparent;
    box-shadow: 0 0 0 1px var(--glass-edge), var(--glass-shadow);
  }
  /* La barra lateral es sticky: ya es su propio contexto, el ::before se queda dentro. */
  :root .af-sidebar::before {
    content: "";
    position: absolute; inset: 0; z-index: -1;
    border-radius: inherit;
    background: var(--glass-sheen), var(--surface-sidebar);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    box-shadow: inset 0 1px 0 var(--glass-highlight), inset 0 -1px 0 var(--glass-lowlight);
    pointer-events: none;
  }
  :root .af-sidebar-toggle {
    top: 37px;
    left: calc(var(--sidebar-w) - 2px);
    background: var(--glass-bg-strong);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-color: var(--glass-border);
    box-shadow: inset 0 1px 0 var(--glass-highlight), var(--shadow-sm);
  }
  :root .af-shell.is-collapsed .af-sidebar-toggle { left: calc(var(--sidebar-w-collapsed) - 2px); }

  :root .af-topbar {
    top: 12px;
    margin: 12px var(--gutter-panel) 0;
    padding: 0 var(--space-4) 0 var(--space-6);
    height: calc(var(--topbar-h) - 8px);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-pill);
    box-shadow: inset 0 1px 0 var(--glass-highlight), 0 0 0 1px var(--glass-edge), var(--glass-shadow);
  }
}
@media (max-width: 991.98px) {
  :root .af-topbar {
    border-bottom: 1px solid var(--glass-border);
    box-shadow: inset 0 -1px 0 var(--glass-edge);
  }
}

/* La navegación: el enlace activo es una gota de cristal teñida de amanecer. */
:root .af-nav-link { border-radius: var(--radius-control); }
:root .af-nav-link.active {
  background: linear-gradient(135deg, rgba(255,212,90,.22) 0%, rgba(255,106,0,.13) 100%);
  box-shadow: inset 0 1px 0 var(--glass-highlight), inset 0 0 0 1px rgba(255,106,0,.22),
              0 6px 16px -8px rgba(232,93,0,.42);
}
:root .af-nav-link.active i[data-lucide],
:root .af-nav-link.active svg { color: var(--action-primary); }
:root[data-theme="dark"] .af-nav-link.active {
  background: linear-gradient(135deg, rgba(255,143,61,.26) 0%, rgba(255,106,0,.15) 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14), inset 0 0 0 1px rgba(255,143,61,.30),
              0 8px 20px -8px rgba(255,106,0,.52);
}
:root .af-sidebar-foot { border-top-color: var(--border-subtle); }

/* Logotipo del interior un 50 % más grande. Medido, no restado del tope: la capa común
   pinta el lockup (3:1) a 42 px de alto y 126 de ancho; a 63 px mide 189, que cabe en los
   214 px útiles de la cabecera de la barra (248 menos rellenos), así que el `max-width`
   no llega a recortarlo. `height` y `max-height` a la vez: un tope menor limitaría la
   altura pedida. En el menú del teléfono, de 24 a 36 px. */
:root .af-sidebar-logo img { height: 63px; max-height: 63px; }
:root .af-offcanvas-logo img { height: 36px; max-height: 36px; }

/* --- A.4 Botones ------------------------------------------------------------------
   Píldora en todos los botones de texto; los campos conservan 12 px. El primario lleva el
   degradado «flex» con un brillo de cristal en el canto. */
:root .btn { --bs-btn-border-radius: var(--radius-pill); }
:root .af-icon-btn { border-radius: var(--radius-pill); }
:root .btn-brand {
  --bs-btn-bg: transparent;
  --bs-btn-border-color: transparent;
  --bs-btn-hover-bg: transparent;
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-bg: transparent;
  --bs-btn-active-border-color: transparent;
  background-image: var(--gradient-brand);
  background-color: var(--action-primary);
  background-size: 140% 100%;
  background-position: 0% 50%;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.45), inset 0 -1px 0 rgba(107,42,0,.18), var(--shadow-brand);
  transition: var(--transition-control), background-position var(--dur-slow) var(--ease-out);
}
/* Al pasar, el degradado se desliza hacia el lado más intenso. */
:root .btn-brand:hover { background-position: 100% 50%; box-shadow: inset 0 1px 0 rgba(255,255,255,.5), inset 0 -1px 0 rgba(107,42,0,.2), var(--shadow-brand); }
:root .btn-brand:active { transform: scale(.97); }
/* Foco con teclado. Bootstrap quita el contorno a los botones (`.btn:focus-visible {
   outline: 0 }`) y dibuja el anillo con box-shadow, pero la sombra de arriba lo pisa:
   el botón principal no mostraba nada al recibir el foco (2.4.7, auditoría del acceso).
   Con el color de la acción y no con --focus-ring: el naranja de marca no llega a 3:1
   sobre el lienzo claro, el de la acción sí (≥4,5:1). En el acceso lo pisa el melocotón. */
:root .btn-brand:focus-visible { outline: 2px solid var(--action-primary); outline-offset: 3px; }

:root .btn-outline-brand {
  --bs-btn-bg: rgba(255,255,255,.42);
  --bs-btn-hover-bg: rgba(255,255,255,.72);
  --bs-btn-active-bg: rgba(255,255,255,.72);
  box-shadow: inset 0 1px 0 var(--glass-highlight);
}
:root[data-theme="dark"] .btn-outline-brand {
  --bs-btn-bg: rgba(255,255,255,.04);
  --bs-btn-hover-bg: rgba(255,255,255,.10);
  --bs-btn-active-bg: rgba(255,255,255,.10);
}
:root .btn-ink { box-shadow: inset 0 1px 0 rgba(255,255,255,.18), var(--shadow-sm); }

/* --- A.5 Formularios ---------------------------------------------------------------- */
:root .form-control,
:root .form-select {
  background-color: rgba(255,255,255,.72);
  box-shadow: inset 0 1px 2px rgba(15,23,42,.06), 0 1px 0 rgba(255,255,255,.8);
}
:root .form-control:focus,
:root .form-select:focus {
  background-color: rgba(255,255,255,.95);
  box-shadow: var(--ring-focus), inset 0 1px 2px rgba(15,23,42,.04);
}
:root[data-theme="dark"] .form-control,
:root[data-theme="dark"] .form-select {
  background-color: rgba(5,9,20,.42);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.30), 0 1px 0 rgba(255,255,255,.05);
}
:root[data-theme="dark"] .form-control:focus,
:root[data-theme="dark"] .form-select:focus { background-color: rgba(5,9,20,.62); }
/* El <option> no hereda el cristal: en oscuro sería texto claro sobre blanco. */
:root[data-theme="dark"] .form-select option { background: var(--ink-850); color: var(--text-body); }

/* --- A.6 Tablas, píldoras y badges: tinte SIN desenfoque --------------------------- */
:root .af-table th { background: rgba(15,23,42,.035); }
:root[data-theme="dark"] .af-table th { background: rgba(255,255,255,.04); }
:root .af-chip { background: rgba(255,255,255,.55); border-color: var(--border-default); }
:root[data-theme="dark"] .af-chip { background: rgba(255,255,255,.05); }
:root .af-icon-box { box-shadow: inset 0 1px 0 rgba(255,255,255,.6); }
:root .progress { --bs-progress-bg: rgba(15,23,42,.08); }
:root[data-theme="dark"] .progress { --bs-progress-bg: rgba(255,255,255,.10); }
:root .progress-bar,
:root .af-recent-bar > span,
:root .af-course-bar > span { background: var(--gradient-brand); }

/* --- A.7 Reserva sin backdrop-filter ------------------------------------------------ */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root { --glass-bg: rgba(255,255,255,.90); --glass-bg-strong: rgba(255,255,255,.97); }
  :root[data-theme="dark"] { --glass-bg: rgba(26,35,55,.94); --glass-bg-strong: rgba(17,24,41,.98); }
}


/* =====================================================================================
   B. LIENZO CON AURORA — el «amanecer frío» de la zona de contenido
   =====================================================================================
   Solo con el cascarón (.af-shell). Dos capas fijas bajo todo (z-index -2), cada una con
   su deriva y su periodo; solo se anima `transform`. La segunda capa además se desplaza
   con el scroll mediante `translate`, que se suma a `transform` sin pisarlo.
   En claro: melocotón y ámbar arriba, un azul hielo que equilibra abajo. En oscuro: brasa
   naranja y la luz azul del sistema dentro del charcoal. */
:root:has(.af-shell) {
  background: var(--gradient-canvas), var(--aurora-base);
  background-attachment: fixed;
}
:root body:has(> .af-shell),
:root .af-shell { background: transparent; }

:root body:has(> .af-shell)::before,
:root body:has(> .af-shell)::after {
  content: "";
  position: fixed; inset: -25%; z-index: -2;
  pointer-events: none;
  will-change: transform;
}
:root body:has(> .af-shell)::before {
  background:
    radial-gradient(34% 38% at 16% 18%, var(--aurora-1), transparent 70%),
    radial-gradient(28% 32% at 82% 14%, var(--aurora-2), transparent 70%),
    radial-gradient(46% 42% at 54% 50%, var(--aurora-5), transparent 72%);
  animation: ag-aurora-a var(--dur-aurora) var(--ease-liquid) infinite alternate;
}
:root body:has(> .af-shell)::after {
  background:
    radial-gradient(36% 40% at 82% 80%, var(--aurora-3), transparent 70%),
    radial-gradient(30% 34% at 20% 86%, var(--aurora-4), transparent 70%);
  animation: ag-aurora-b calc(var(--dur-aurora) * .77) var(--ease-liquid) infinite alternate,
             ag-aurora-scroll linear both;
  animation-timeline: auto, scroll(root);
}
@keyframes ag-aurora-a {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  50%  { transform: translate3d(4%, 3%, 0) rotate(8deg) scale(1.06, .96); }
  100% { transform: translate3d(-3%, 5%, 0) rotate(-6deg) scale(.96, 1.05); }
}
@keyframes ag-aurora-b {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  50%  { transform: translate3d(-5%, -3%, 0) rotate(-10deg) scale(1.08, .95); }
  100% { transform: translate3d(3%, -5%, 0) rotate(6deg) scale(.95, 1.06); }
}
@keyframes ag-aurora-scroll {
  from { translate: 0 0; }
  to   { translate: -6% -14%; }
}
/* Grano: textura que desenfocar (sin él el degradado se ve con bandas). Tesela de 160 px. */
:root .af-shell::after {
  content: "";
  position: fixed; inset: 0; z-index: -1;
  pointer-events: none;
  opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
:root[data-theme="dark"] .af-shell::after { opacity: .07; }


/* =====================================================================================
   C. ACCESO — la galaxia de Nexus teñida de amanecer, marca a la izquierda (1.3.0)
   =====================================================================================
   Login, instalador, recuperación, consentimiento y también las páginas de error (todas
   usan .af-auth). Va siempre en claro: esas pantallas no tienen script de data-theme.

   Sustituye al escaparate partido de la 1.2 (lámina clara a la derecha). Ahora es la
   maquetación del acceso de Nexus Portal, que pinta la capa común con theme.json:
     · login.background "galaxy": la galaxia animada (webroot/js/login-galaxy.js) como
       fondo por defecto. Si el tema trae imagen en zones.loginBackground, manda ella.
     · login.layout "split": en la pantalla de acceso, columna de marca a la izquierda
       (logotipo, nombre, titular y lema) y la tarjeta a la derecha. Por debajo de 992 px
       la columna desaparece y queda la tarjeta sobre la galaxia.
   Aquí solo se pone el color: la paleta de la galaxia (--galaxy-*, tripletes «r,g,b»),
   la columna de marca y la tarjeta, que es el cristal ahumado de Alumne Flex Liquid
   Glass con el naranja de Academy.

   Nada de z-index ni filtros en .af-auth ni en .af-auth-inner: los modales
   («¿Olvidaste la contraseña?», código por correo) viven dentro de .af-auth-inner y
   quedarían atrapados por debajo del telón de Bootstrap. */

/* El telón: el charcoal de la marca con el arranque del amanecer abajo a la izquierda.
   Es también el fondo de las pantallas sin galaxia (páginas de error, o con imagen
   mientras carga), así que nunca se ve el lienzo claro del tema. */
:root .af-auth {
  background:
    radial-gradient(60% 55% at 0% 100%, rgba(255,106,0,.22) 0%, rgba(255,106,0,0) 70%),
    radial-gradient(70% 60% at 100% 0%, rgba(37,99,235,.14) 0%, rgba(37,99,235,0) 65%),
    #0B1120;
}
/* La rejilla de reserva de la capa común lleva un halo naranja escrito en el marcado:
   con el telón de arriba sobra. */
:root .af-auth-bg { display: none; }

/* La galaxia en los colores de Academy: estrellas en ámbar y naranja, núcleo crema, halo
   naranja con el canto azul frío, impulsos azules y neuronas que se encienden en blanco
   (en Nexus se encienden en naranja, que aquí se perdería entre las estrellas). */
:root .af-auth--galaxy {
  --galaxy-halo: 255,106,0;
  --galaxy-halo-edge: 37,99,235;
  --galaxy-core: 255,236,210;
  --galaxy-star-a: 255,170,107;
  --galaxy-star-b: 255,212,90;
  --galaxy-link: 255,190,140;
  --galaxy-pulse: 91,141,239;
  --galaxy-fire: 255,255,255;
  --galaxy-neuron: 255,200,150;
}

/* --- Columna de marca --------------------------------------------------------------
   El logotipo pasa a silueta blanca con un filtro y NO cambiando la imagen: si la cuenta
   ha subido el suyo en «Marca rápida», AppController::brandingAssets() lo pone en este
   mismo <img>. Contraste sobre el telón (#0B1120, sin contar las estrellas, que son
   puntos de 1 px): antetítulo #FFC7A8 12:1, lema al .82 11:1 y la segunda línea del
   titular, que empieza en #FF8F3D, 7,6:1 (texto grande, pide 3). */
:root .af-auth-brand-logo { filter: brightness(0) invert(1); height: 60px; }
:root .af-auth-brand-kicker { color: #FFC7A8; letter-spacing: .14em; }
:root .af-auth-brand-headline {
  font-weight: var(--weight-extra);
  letter-spacing: var(--tracking-display);
}
:root .af-auth-brand-headline span {
  color: #FFD45A;
  background: linear-gradient(90deg, #FF8F3D 0%, #FFD45A 100%);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
:root .af-auth-brand-tagline { color: rgba(255,255,255,.82); font-size: var(--text-base); }

/* --- La tarjeta: cristal ahumado ----------------------------------------------------
   La de Alumne Flex Liquid Glass. Filtro directo en la tarjeta: aquí sí se puede, porque
   los modales de templates/Users/login.php van FUERA de .af-auth-card, como hermanos
   dentro de .af-auth-inner.

   Detrás solo hay telón oscuro y puntos de la galaxia; el punto más claro posible es el
   centro del halo naranja (≈#3A2418 compuesto sobre el charcoal). Sobre él, con el velo
   y `brightness(.72)`: título y etiquetas en blanco ≥12:1, subtítulo al .92 ≥10:1 y los
   enlaces #FFC7A8 ≥9:1. */
:root .af-auth-card {
  position: relative;
  border-radius: var(--radius-shell);
  padding: var(--space-9) var(--space-8) var(--space-8);
  background: linear-gradient(160deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,.05) 55%, rgba(255,255,255,.09) 100%),
              rgba(11,17,32,.46);
  -webkit-backdrop-filter: blur(22px) saturate(150%) brightness(.72);
  backdrop-filter: blur(22px) saturate(150%) brightness(.72);
  border: 1px solid rgba(255,255,255,.24);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.50),
    inset 0 -1px 0 rgba(255,255,255,.10),
    inset 0 24px 48px -24px rgba(255,255,255,.12),
    0 40px 90px -24px rgba(3,7,18,.75);
  color: rgba(255,255,255,.92);
}
/* Reflejo diagonal de la cara: se mueve al revés que el puntero (--pointer-x/-y, que
   escribe alumneflex-shell.js en <html>; sin JS, quieto). */
:root .af-auth-card::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: linear-gradient(115deg, rgba(255,255,255,.20) 0%, rgba(255,255,255,0) 30%,
              rgba(255,255,255,0) 68%, rgba(255,255,255,.07) 100%);
  translate: calc(var(--pointer-x, 0) * -6px) calc(var(--pointer-y, 0) * -6px);
  pointer-events: none;
}
/* Filete de luz que recorre el canto: amanecer y azul frío. Ángulo registrado para
   poder animarlo; sin @property, el anillo se queda quieto. */
@property --ag-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
:root .af-auth-card::after {
  content: "";
  position: absolute; inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(from var(--ag-angle),
              rgba(255,255,255,0) 0deg, rgba(255,255,255,0) 200deg,
              rgba(255,143,61,.95) 250deg, rgba(255,228,154,.95) 272deg,
              rgba(91,141,239,.8) 300deg, rgba(255,255,255,0) 340deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  animation: ag-rim 9s linear infinite;
  pointer-events: none;
}
@keyframes ag-rim { to { --ag-angle: 360deg; } }

/* Logotipo de la tarjeta (en el teléfono, y en las pantallas sin columna de marca):
   silueta blanca, por el mismo motivo que el de la columna. */
:root .af-auth-logo { filter: brightness(0) invert(1); height: 44px; }

:root .af-auth-card .af-auth-title { color: #FFFFFF; font-size: var(--text-3xl); font-weight: var(--weight-extra); letter-spacing: var(--tracking-display); }
:root .af-auth-card .af-auth-sub { color: rgba(255,255,255,.92); }
:root .af-auth-card .form-label { color: #FFFFFF; }
:root .af-auth-card .form-check-label { color: rgba(255,255,255,.94); }
:root .af-auth-card .form-text,
:root .af-auth-card .af-muted,
:root .af-auth-card .af-auth-local-note { color: rgba(255,255,255,.88); }
:root .af-auth-card a:not(.btn):not(.af-auth-sso) { color: #FFC7A8; }
:root .af-auth-card a:not(.btn):not(.af-auth-sso):hover { color: #FFFFFF; }

/* Campos: láminas blancas casi opacas sobre el cristal, con el texto en tinta como en el
   resto del tema. */
:root .af-auth-card .form-control {
  min-height: var(--control-h-lg);
  background-color: rgba(255,255,255,.92);
  border-color: rgba(255,255,255,.95);
  color: var(--ink-900);
  box-shadow: inset 0 1px 2px rgba(11,17,32,.08), 0 8px 20px -12px rgba(3,7,18,.5);
}
:root .af-auth-card .form-control:focus {
  background-color: #FFFFFF;
  border-color: #FFFFFF;
  /* Anillo opaco: al .5 se quedaba en 2,9:1 contra la tarjeta (1.4.11). #FF8F3D, 8,3:1. */
  box-shadow: 0 0 0 4px #FF8F3D, 0 8px 20px -12px rgba(3,7,18,.5);
}
/* --ink-600 (6,4:1 sobre el campo): --ink-500 se quedaba en 4,1:1. */
:root .af-auth-card .form-control::placeholder { color: var(--ink-600); }
:root .af-auth-card .af-field-icon,
:root .af-auth-card .af-password-toggle { color: var(--ink-500); }
:root .af-auth-card .af-password-toggle:hover { background: rgba(11,17,32,.06); color: var(--ink-900); }
:root .af-auth-card .form-check-input {
  background-color: rgba(255,255,255,.18);
  border-color: rgba(255,255,255,.70);
}
:root .af-auth-card .form-check-input:checked {
  background-color: var(--action-primary);
  border-color: var(--action-primary);
}
/* El anillo de Bootstrap va al 24 % del naranja y no se ve sobre el cristal (1,3:1). */
:root .af-auth-card .form-check-input:focus { box-shadow: 0 0 0 3px #FFC7A8; }
:root .af-auth-card .btn-brand { height: 52px; }

/* Separador «o» y botones de SSO: cristal sobre cristal. */
:root .af-auth-card .af-auth-sep { color: rgba(255,255,255,.86); }
:root .af-auth-card .af-auth-sep::before,
:root .af-auth-card .af-auth-sep::after { background: rgba(255,255,255,.22); }
:root .af-auth-card .af-auth-sso {
  background: rgba(255,255,255,.12);
  border-color: rgba(255,255,255,.30);
  color: #FFFFFF;
  border-radius: var(--radius-pill);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.30);
}
:root .af-auth-card .af-auth-sso:hover { background: rgba(255,255,255,.20); border-color: rgba(255,255,255,.5); color: #FFFFFF; }
:root .af-auth-card :focus-visible { outline-color: #FFC7A8; }

/* Pasos del instalador y cajas de diagnóstico, dentro de la tarjeta oscura. */
:root .af-auth-card .af-steps,
:root .af-auth-card h2,
:root .af-auth-card h3 { color: #FFFFFF; }

/* Pie, bajo la tarjeta y sobre el telón: el .38 de la capa común se queda en 3,6:1, así
   que el copyright sube. Lema al .72 ≈8:1 y copyright al .66 ≈7:1. */
:root .af-auth-foot { color: rgba(255,255,255,.72); }
:root .af-auth-foot-copy { color: rgba(255,255,255,.66); }
:root .af-auth-foot a { color: #FFFFFF; }

@media (max-width: 575.98px) {
  :root .af-auth-card { padding: var(--space-7) var(--space-6); border-radius: var(--radius-panel); }
}


/* =====================================================================================
   D. PORTADA DE LA FICHA DEL CATÁLOGO (templates/element/catalog/detail_hero.php)
   =====================================================================================
   Igual que en alumneflexglass: la portada sube por detrás de la barra superior flotante
   y en vez de fundirse A un color opaco se DESVANECE con una máscara, así que debajo
   aparece la propia aurora. catalog.css se carga después: los selectores llevan `:root`. */
:root { --ag-topbar-space: var(--topbar-h); }
@media (min-width: 992px) { :root { --ag-topbar-space: calc(var(--topbar-h) + 4px); } }

/* Catálogo en modo streaming (0.212.0): sus portadas (slider, cabecera de categoría y
   ficha) suben por detrás de la cápsula de la barra superior, como la portada de la
   ficha antigua de aquí arriba. Solo en escritorio: en el teléfono la barra va de
   lado a lado y es una barra de verdad. El buscador y «Gestionar catálogo» quedan
   flotando justo debajo de la cápsula. Ver 2_UI_DOCS/THEMES.md. */
@media (min-width: 992px) {
  :root {
    --stream-topbar-overlap: var(--ag-topbar-space);
    /* Y empiezan y acaban donde la cápsula, con sus esquinas: separadas del menú
       lateral, que también es una cápsula. */
    --stream-hero-inset: var(--gutter-panel);
    --stream-hero-radius: var(--radius-panel);
  }
}

:root .af-catalog-hero {
  background: transparent;
  min-height: calc(clamp(300px, 46vh, 480px) + var(--ag-topbar-space));
  --ag-hero-mask:
    linear-gradient(180deg, #000 0%, #000 42%, rgba(0,0,0,.55) 70%, rgba(0,0,0,.18) 88%, transparent 100%),
    linear-gradient(90deg, transparent 0%, #000 7%);
}
:root .af-catalog-hero:first-child {
  margin-top: calc(-1 * (var(--hero-pad-top) + var(--ag-topbar-space)));
}
:root .af-catalog-hero-inner {
  padding-top: calc(var(--space-6) + var(--ag-topbar-space));
}
@media (max-width: 991.98px) {
  :root .af-catalog-hero { min-height: calc(clamp(260px, 40vh, 380px) + var(--ag-topbar-space)); }
}
@media (max-width: 767.98px) {
  :root .af-catalog-hero { min-height: calc(clamp(240px, 62vw, 320px) + var(--ag-topbar-space)); }
  :root .af-catalog-hero-inner { padding-top: calc(var(--space-4) + var(--ag-topbar-space)); }
  :root .af-catalog-hero { --ag-hero-mask: linear-gradient(180deg, #000 0%, #000 42%, rgba(0,0,0,.55) 70%, rgba(0,0,0,.18) 88%, transparent 100%), linear-gradient(#000 0 0); }
}
:root .af-catalog-hero-img,
:root .af-catalog-hero::after {
  -webkit-mask-image: var(--ag-hero-mask);
  -webkit-mask-composite: source-in;
  mask-image: var(--ag-hero-mask);
  mask-composite: intersect;
}
:root .af-catalog-hero::after {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--surface-stage) 58%, transparent) 0%,
      color-mix(in srgb, var(--surface-stage) 20%, transparent) 30%,
      transparent 50%),
    linear-gradient(180deg,
      transparent 34%,
      color-mix(in srgb, var(--aurora-base) 45%, transparent) 60%,
      color-mix(in srgb, var(--aurora-base) 80%, transparent) 100%);
}
:root .af-catalog-hero-title { text-shadow: 0 1px 18px var(--aurora-base), 0 0 2px color-mix(in srgb, var(--aurora-base) 60%, transparent); }
:root .af-catalog-hero-subtitle { text-shadow: 0 1px 12px var(--aurora-base); }

/* --- Tarjetas de material del visor (0.178.0) -----------------------------------------
   Test, entregable, encuesta, enlace, videoconferencia y entrada del calendario se pintan
   en el escenario del reproductor con `.af-matcard`. Aquí el telón no es un halo plano: son
   tres manchas de color (la del tema y las de dos tipos de material) que dan algo que
   desenfocar, y la tarjeta es una lámina de cristal sobre ellas.

   El cristal va en la PROPIA tarjeta y no en un ::before: dentro no hay ningún modal (los
   del intento y del entregable cuelgan de <body>), así que el `backdrop-filter` no atrapa
   nada. Con `--glass-bg-strong` casi opaco, el texto se lee igual que en la tarjeta lisa. */
:root .af-matstage {
  background:
    radial-gradient(55% 48% at 16% 10%, color-mix(in srgb, var(--material-stage-glow, var(--action-primary)) 46%, transparent) 0%, transparent 70%),
    radial-gradient(50% 44% at 88% 90%, color-mix(in srgb, var(--material-practice, var(--action-accent)) 36%, transparent) 0%, transparent 70%),
    radial-gradient(40% 36% at 92% 8%, color-mix(in srgb, var(--material-survey, var(--status-ai-solid)) 28%, transparent) 0%, transparent 70%);
}
:root .af-matcard {
  background: var(--glass-sheen), var(--glass-bg-strong);
  -webkit-backdrop-filter: var(--glass-blur-strong);
  backdrop-filter: var(--glass-blur-strong);
  border: 1px solid var(--glass-border);
  border-top: 4px solid var(--af-mat);
  box-shadow: inset 0 1px 0 var(--glass-highlight), 0 0 0 1px var(--glass-edge), var(--glass-shadow);
}
/* El pie, los recuadros y la banda neutra, translúcidos sobre el cristal en vez de hundidos. */
:root .af-matcard-foot { background: color-mix(in srgb, var(--surface-sunken) 55%, transparent); border-top-color: var(--glass-border); }
:root .af-matcard-tile,
:root .af-matcard-site,
:root .af-matcard-reqs li,
:root .af-matcard-chips li { background: color-mix(in srgb, var(--surface-sunken) 60%, transparent); }
:root .af-matcard-status.is-neutral { background: color-mix(in srgb, var(--surface-sunken) 60%, transparent); }
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  :root .af-matcard { background: var(--surface-card); }
}

/* ---------------------------------------------------------------------------
   Reproductor de cursos (1.2.0)

   Inmersivo: el panel inferior (descripción, foro, clase) y el índice FLOTAN sobre el
   vídeo o el paquete. Con --surface-card al .86 se leía el contenido de detrás a través
   del panel y la escena de la clase quedaba sin anclar: van en sólido, blanco en claro
   y el tono opaco del tema en oscuro, sin desenfoque.

   Clásico, solo en escritorio (en ≤1024 px el índice es un cajón que flota sobre el
   contenido y se queda sólido): el índice no es una tarjeta encima de la aurora, sino
   una columna TRANSPARENTE sobre el mismo fondo, separada por una línea (Guille lo
   prefirió sin velo). Para que se lea en cualquier punto, dentro del índice bajan de
   tono el texto secundario y el naranja del título activo: medido sobre la base con las
   tres manchas que llegan al borde derecho (sola y de dos en dos), en claro y en
   oscuro, todo el texto queda por encima de 4,5:1.
   --------------------------------------------------------------------------- */
:root { --ag-player-solid: #FFFFFF; --ag-side-muted: var(--text-muted); --ag-side-brand: #873500; }
:root[data-theme="dark"] { --ag-player-solid: #1A2337; --ag-side-muted: #B8C0CC; --ag-side-brand: #FFA466; }

:root .af-player-immersive .af-imm-sheet,
:root .af-player-immersive .af-player-side {
  background: var(--ag-player-solid);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

@media (min-width: 1025px) {
  :root .af-player:not(.af-player-immersive) .af-player-side {
    background: transparent;
    border-left: 1px solid var(--border-default);
    box-shadow: none;
    --text-muted: var(--ag-side-muted);
    --text-subtle: var(--ag-side-muted);
    --text-brand: var(--ag-side-brand);
  }
  :root .af-player:not(.af-player-immersive) .af-player-side-head { border-bottom-color: var(--border-default); }
}
