/* ── Componente Aether ───────────────────────────────────────────────────────
   Video de fondo. El JS decide QUÉ suena y CUÁNDO; todo lo que se VE está aquí,
   detrás de variables, así que cambiar el aspecto de un héroe no debería
   obligar a tocar una línea de JavaScript.

   Las cuatro capas (en orden de pintado)
   --------------------------------------
     .upaep__aether-poster     la imagen. Lo que se ve mientras carga… y lo único
                               que se ve si no hay video (reduced-motion, ahorro
                               de datos, `disableOn`, o el archivo falló)
     .upaep__aether-stage      el recorte. Es el `container` que hace la cuenta
                               del sobreancho de los iframes, y el que se cruza
                               con el poster al llegar `.is-ready`
     .upaep__aether-overlay    el tinte o degradado que hace legible el texto
     .upaep__aether-controls   fuera de la capa a propósito (ver abajo)

   Por qué los controles NO van dentro de la capa
   ----------------------------------------------
   La capa lleva `z-index: -1` para pintarse sobre el fondo del host pero DEBAJO
   de su contenido: así un héroe no tiene que ponerle `position: relative` a cada
   párrafo. El precio es que ahí dentro nada es clicable — por eso los botones
   son hermanos de la capa, no hijos.

   Recortar un <video> y recortar un iframe no es lo mismo
   -------------------------------------------------------
   Al <video> se le dice `object-fit: cover`. A un iframe no se le puede: no es
   contenido reemplazado. Hay que sobredimensionarlo por el lado que falte, y esa
   cuenta necesita saber el tamaño del marco — de ahí `container-type: size` en
   el stage y las unidades `cqw`/`cqh`. Sin JS, sin `resize`, sin recalcular.

   Estados que pone el JS en `.upaep__aether`
   ------------------------------------------
     .is-ready      el medio ya puede reproducirse → se cruza el poster
     .is-playing    está corriendo
     .is-muted      el sonido está apagado (que es lo normal)
     .is-blocked    no se va a reproducir; el porqué queda en
                    `data-aether-blocked` (reduced-motion, save-data, disable-on-…)
     .is-error      la fuente falló y se queda el poster
     .is-disabled   `disable()`

   Variables configurables del componente (fallback a tokens globales):
--------------------------------------------------------------------------- */

/*? El host: la sección que lleva el video detrás. `__anchor` solo lo pone el JS
    si el host era `static` — pisarle el `position` a un host que ya era
    `absolute` movería la sección entera de sitio. */
.upaep__aether-host.__anchor {
    position: relative;
}

/*? `isolation` NO es cosmético, es lo que hace que `z-index: -1` funcione.
    Sin contexto de apilamiento propio, un hijo con z-index negativo se pinta
    contra el contexto del ancestro, y ahí el orden es: fondo raíz → hijos
    negativos → fondos de los bloques en flujo. Traducido: el fondo de la propia
    sección taparía el video. Con `isolate` el orden pasa a ser fondo del host →
    la capa → contenido del host, que es justo lo que se quiere.

    El precio: los z-index de dentro del host quedan encerrados. Si en ese héroe
    vive un dropdown o un tooltip que TIENE que salirse por encima de la sección
    siguiente, monta el componente con `isolate: false` y ponle tú al contenido
    un `position: relative`. */
.upaep__aether-host.__isolate {
    isolation: isolate;
}


/* ── La capa ──────────────────────────────────────────────────────────────── */
.upaep__aether {
    /* colocación */
    --upaep-aether-zindex: -1;
    --upaep-aether-radius: 0;
    --upaep-aether-bg: var(--upaep-color-black);

    /* el video */
    --upaep-aether-position: center;
    /* relación de aspecto: solo la usan los iframes */
    --upaep-aether-ratio: 1.7778;
    --upaep-aether-blur: 0px;
    /* escala que compensa el borde que descubre el desenfoque */
    --upaep-aether-bleed: 1;
    /* filtros extra (grayscale, saturate…): se suman al blur. Vacío a
       propósito — un valor vacío sustituye a nada y la lista sigue siendo
       válida, cosa que `none` dentro de una lista de filtros no cumple. */
    --upaep-aether-filter: ;

    /* el cruce poster → video */
    --upaep-aether-fade: 600ms;
    --upaep-aether-ease: cubic-bezier(.4, 0, .2, 1);

    /* el poster */
    --upaep-aether-poster: none;
    --upaep-aether-poster-size: cover;
    --upaep-aether-poster-position: center;

    /* el tinte */
    --upaep-aether-overlay-color: var(--upaep-color-black);
    --upaep-aether-overlay-opacity: 0;

    position: absolute;
    inset: 0;
    z-index: var(--upaep-aether-zindex);
    overflow: hidden;
    border-radius: var(--upaep-aether-radius);
    background-color: var(--upaep-aether-bg);

    /*? Un fondo no intercepta clics. Con `z-index: -1` ya sería inalcanzable,
        pero en modo página la capa puede quedar por encima de algo. */
    pointer-events: none;
}

/*? Modo página: fija a la ventana, no a la sección. */
.upaep__aether.__page {
    position: fixed;
}


/* ── Poster ───────────────────────────────────────────────────────────────── */
.upaep__aether-poster {
    position: absolute;
    inset: 0;
    background-image: var(--upaep-aether-poster);
    background-size: var(--upaep-aether-poster-size);
    background-position: var(--upaep-aether-poster-position);
    background-repeat: no-repeat;
}


/* ── Escenario ────────────────────────────────────────────────────────────── */
.upaep__aether-stage {
    position: absolute;
    inset: 0;

    /*? De aquí salen los `cqw`/`cqh` con los que se recorta un iframe. */
    container-type: size;

    /*? Arranca invisible y se cruza con el poster al llegar `.is-ready`: sin
        esto se vería el primer fotograma en negro sobre la imagen. */
    opacity: 0;
    transition: opacity var(--upaep-aether-fade) var(--upaep-aether-ease);
}

.upaep__aether.is-ready .upaep__aether-stage {
    opacity: 1;
}

/*? El filtro se aplica SOLO si hace falta (el JS pone `__filtered` cuando hay
    `blur`). Un `filter` permanente sobre un video a pantalla completa fuerza una
    superficie de composición extra que en un equipo modesto sí se nota. Para un
    filtro propio: añade tú la clase y redefine `--upaep-aether-filter`. */
.upaep__aether.__filtered .upaep__aether-stage {
    filter: blur(var(--upaep-aether-blur)) var(--upaep-aether-filter);
    scale: var(--upaep-aether-bleed);
}


/* ── El medio ─────────────────────────────────────────────────────────────── */
.upaep__aether-media {
    position: absolute;
    display: block;
    border: 0;
    pointer-events: none;
}

/*? <video>: contenido reemplazado, así que basta object-fit. */
.upaep__aether-media.__native {
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: var(--upaep-aether-position);
}

.upaep__aether.__fit-contain .upaep__aether-media.__native {
    object-fit: contain;
}

.upaep__aether.__fit-fill .upaep__aether-media.__native {
    object-fit: fill;
}

/*? iframe (YouTube/Vimeo): no hay object-fit que valga. Se sobredimensiona por
    el lado que falte, y la cuenta se hace contra el tamaño del stage con
    unidades de contenedor. El envoltorio es el que crece; el iframe de dentro
    solo lo rellena —así da igual el width/height que el reproductor de terceros
    se ponga a sí mismo. */
.upaep__aether-media.__embed {
    top: 50%;
    left: 50%;
    translate: -50% -50%;
    width: max(100cqw, calc(100cqh * var(--upaep-aether-ratio)));
    height: max(100cqh, calc(100cqw / var(--upaep-aether-ratio)));
}

.upaep__aether.__fit-contain .upaep__aether-media.__embed {
    width: min(100cqw, calc(100cqh * var(--upaep-aether-ratio)));
    height: min(100cqh, calc(100cqw / var(--upaep-aether-ratio)));
}

.upaep__aether.__fit-fill .upaep__aether-media.__embed {
    width: 100cqw;
    height: 100cqh;
}

.upaep__aether-media.__embed iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    pointer-events: none;
}


/* ── Tinte ────────────────────────────────────────────────────────────────── */
/*? `background` en forma corta y con fallback: si nadie declaró
    `--upaep-aether-overlay-image`, se usa el color plano. Así los degradados de
    abajo son variantes de una sola propiedad y los tokens `--upaep-gradient*`
    —que ya vienen en forma corta— se pueden enchufar tal cual. */
.upaep__aether-overlay {
    position: absolute;
    inset: 0;
    background: var(--upaep-aether-overlay-image, var(--upaep-aether-overlay-color));
    opacity: var(--upaep-aether-overlay-opacity);
    pointer-events: none;
}

/* ── Variantes del tinte ──────────────────────────────────────────────────── */
/*? El clásico para que un título blanco se lea sobre cualquier fotograma. */
.upaep__aether-overlay.__gradient,
.upaep__aether-overlay.__gradient-bottom {
    --upaep-aether-overlay-image: linear-gradient(to top,
            var(--upaep-aether-overlay-color) 0%,
            color-mix(in srgb, var(--upaep-aether-overlay-color) 40%, transparent) 45%,
            transparent 80%);
}

.upaep__aether-overlay.__gradient-top {
    --upaep-aether-overlay-image: linear-gradient(to bottom,
            var(--upaep-aether-overlay-color) 0%,
            transparent 70%);
}

.upaep__aether-overlay.__gradient-left {
    --upaep-aether-overlay-image: linear-gradient(to right,
            var(--upaep-aether-overlay-color) 0%,
            transparent 65%);
}

.upaep__aether-overlay.__gradient-right {
    --upaep-aether-overlay-image: linear-gradient(to left,
            var(--upaep-aether-overlay-color) 0%,
            transparent 65%);
}

/*? Los dos lados oscuros y el centro limpio: para texto centrado. */
.upaep__aether-overlay.__gradient-sides {
    --upaep-aether-overlay-image: linear-gradient(to right,
            var(--upaep-aether-overlay-color) 0%,
            transparent 35%,
            transparent 65%,
            var(--upaep-aether-overlay-color) 100%);
}

.upaep__aether-overlay.__vignette {
    --upaep-aether-overlay-image: radial-gradient(ellipse at center,
            transparent 40%,
            var(--upaep-aether-overlay-color) 100%);
}

/*? Degradados de marca: los tokens globales, sin repetir los colores. */
.upaep__aether-overlay.__brand {
    --upaep-aether-overlay-image: var(--upaep-gradient2);
}

.upaep__aether-overlay.__brand-red {
    --upaep-aether-overlay-image: var(--upaep-gradient4);
}


/* ── Controles ────────────────────────────────────────────────────────────── */
/*? Hermanos de la capa, no hijos: dentro de un `z-index: -1` no habría clic. */
.upaep__aether-controls {
    --upaep-aether-controls-inset: 1.25rem;
    --upaep-aether-controls-gap: .5rem;
    --upaep-aether-controls-zindex: 2;

    position: absolute;
    z-index: var(--upaep-aether-controls-zindex);
    display: flex;
    gap: var(--upaep-aether-controls-gap);
    inset: auto var(--upaep-aether-controls-inset) var(--upaep-aether-controls-inset) auto;
}

.upaep__aether-host.__page>.upaep__aether-controls {
    position: fixed;
}

.upaep__aether-controls.__bottom-left {
    inset: auto auto var(--upaep-aether-controls-inset) var(--upaep-aether-controls-inset);
}

.upaep__aether-controls.__top-right {
    inset: var(--upaep-aether-controls-inset) var(--upaep-aether-controls-inset) auto auto;
}

.upaep__aether-controls.__top-left {
    inset: var(--upaep-aether-controls-inset) auto auto var(--upaep-aether-controls-inset);
}

.upaep__aether-controls.__bottom-center {
    inset: auto auto var(--upaep-aether-controls-inset) 50%;
    translate: -50% 0;
}

.upaep__aether-btn {
    --upaep-aether-btn-size: 2.75rem;
    --upaep-aether-btn-radius: 50%;
    --upaep-aether-btn-color: var(--upaep-color-white);
    --upaep-aether-btn-bg: rgb(0 0 0 / 35%);
    --upaep-aether-btn-bg-hover: rgb(0 0 0 / 60%);
    --upaep-aether-btn-border: 1px solid rgb(255 255 255 / 35%);
    --upaep-aether-btn-backdrop: 6px;
    --upaep-aether-btn-transition: 200ms ease;

    /*? Rejilla de una celda: los dos iconos del botón se apilan en el mismo
        sitio y solo cambia cuál se muestra. Sin `position: absolute`, así que el
        botón mide lo que mide su icono. */
    display: grid;
    place-items: center;
    inline-size: var(--upaep-aether-btn-size);
    block-size: var(--upaep-aether-btn-size);
    padding: 0;
    border: var(--upaep-aether-btn-border);
    border-radius: var(--upaep-aether-btn-radius);
    background-color: var(--upaep-aether-btn-bg);
    backdrop-filter: blur(var(--upaep-aether-btn-backdrop));
    color: var(--upaep-aether-btn-color);
    font-family: var(--upaep-ff-fira-sans);
    cursor: pointer;
    transition: background-color var(--upaep-aether-btn-transition),
        scale var(--upaep-aether-btn-transition);
}

.upaep__aether-btn:hover {
    background-color: var(--upaep-aether-btn-bg-hover);
    scale: 1.06;
}

.upaep__aether-btn:active {
    scale: .96;
}

.upaep__aether-btn:focus-visible {
    outline: var(--ui-focus-ring-width, 2px) solid var(--ui-focus-ring-color, currentColor);
    outline-offset: var(--ui-focus-ring-offset, 2px);
}

.upaep__aether-btn>svg {
    grid-area: 1 / 1;
    inline-size: 55%;
    block-size: auto;
}

/*? `__off` = estado apagado (silenciado / en pausa); `__on` = encendido. */
.upaep__aether-btn>.__on {
    display: none;
}

.upaep__aether-btn.is-on>.__on {
    display: block;
}

.upaep__aether-btn.is-on>.__off {
    display: none;
}


/* ── Estados ──────────────────────────────────────────────────────────────── */
/*? Bloqueado o fallido: no hay medio que enseñar, se deja el poster. No se
    oculta el stage con `display` para no romper el cruce si luego se desbloquea
    (un giro de pantalla puede sacar a la instancia de `disableOn`). */
.upaep__aether.is-blocked .upaep__aether-stage,
.upaep__aether.is-error .upaep__aether-stage,
.upaep__aether.is-disabled .upaep__aether-stage {
    opacity: 0;
}

/*? Sin poster y sin video queda el color de fondo: mejor eso que un hueco
    blanco donde debería ir el héroe. */
.upaep__aether.is-blocked .upaep__aether-controls,
.upaep__aether.is-error .upaep__aether-controls {
    display: none;
}


/* ── Accesibilidad ────────────────────────────────────────────────────────── */
/*? Quien pide menos movimiento normalmente no llega hasta aquí (el JS ni monta
    el video y deja el poster). Esto cubre el caso `reducedMotion: 'play'`, donde
    la página decidió reproducir de todas formas: al menos que no haya cruces ni
    rebotes de más. */
@media (prefers-reduced-motion: reduce) {
    .upaep__aether-stage {
        transition-duration: 1ms;
    }

    .upaep__aether-btn,
    .upaep__aether-btn:hover,
    .upaep__aether-btn:active {
        transition: none;
        scale: 1;
    }
}

/*? Al imprimir se queda el poster: un iframe vacío o un fotograma en negro
    arruinan la hoja. */
@media print {

    .upaep__aether-stage,
    .upaep__aether-controls {
        display: none;
    }
}


/* ── Responsive ───────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
    .upaep__aether-controls {
        --upaep-aether-controls-inset: .75rem;
    }

    .upaep__aether-btn {
        --upaep-aether-btn-size: 2.5rem;
    }
}
