/* css/feed.css — vista inmersiva del feed (rediseño 2026-10, aprobado por el
   dueño; RE-PIN CONSCIENTE en scripts/feed.test.mjs). Reemplaza la grilla de
   cards por UN panel por viewport: media full-bleed, autoría en overlay,
   riel derecho de acciones y barra CTA contextual. Mismo mundo que el perfil
   inmersivo (negro profundo + champán). Tokens: global.css (--morbo-*). */

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

:root {
    /* Alto de la barra CTA contextual: lo consumen el overlay de autoría y el
       riel para no quedar nunca debajo de la barra. */
    --feed-cta-h: 84px;
    /* Alto de la BARRA SUPERIOR RESERVADA (Slice A, rediseño catálogo/feed
       2026-10): ocupa espacio propio en el flujo — el escenario y los paneles
       lo descuentan. Sin tira de historias: las historias viven SOLO en el
       catálogo (decisión del dueño, 2026-10). */
    --topbar-h: calc(env(safe-area-inset-top) + 62px);
}

body {
    background: var(--morbo-bg);
    color: var(--morbo-text);
    font-family: "Poppins", system-ui, sans-serif;
    font-size: 15px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
    /* Guard de página (defecto del dueño 2026-10: "el multimedia se desborda
       horizontalmente"): esta hoja SOLO la carga el feed, así que el eje
       horizontal queda cerrado acá. Ningún estado del media puede abrir un
       scroll lateral en el teléfono. */
    overflow-x: hidden;
}

button {
    font: inherit;
    cursor: pointer;
    border: 0;
    background: none;
    color: inherit;
}

a {
    color: inherit;
    text-decoration: none;
}

img,
video {
    display: block;
}

:focus-visible {
    outline: 2px solid var(--morbo-accent);
    outline-offset: 2px;
}

/* ============ Escenario: un panel por viewport ============================== */

.feed-stage {
    height: calc(100dvh - var(--topbar-h));
    overflow-y: auto;
    scroll-snap-type: y mandatory;
    overscroll-behavior-y: contain;
    scrollbar-width: none;
}

.feed-stage::-webkit-scrollbar {
    display: none;
}

.feed-lista {
    /* contenedor plano: los paneles son los hijos que snappean */
}

.feed-panel {
    position: relative;
    height: calc(100dvh - var(--topbar-h));
    scroll-snap-align: start;
    scroll-snap-stop: always;
    overflow: hidden;
    isolation: isolate;
}

/* ============ Media full-bleed: el media ES el fondo del panel ============== */

.feed-media {
    position: absolute;
    inset: 0;
    z-index: -2;
    background: linear-gradient(160deg, #17171b 0%, #0c0c0e 60%, #101014 100%);
    /* Contrato de fit (defecto del dueño 2026-10): la caja del media SIEMPRE
       recorta. El media entra en el panel por los dos ejes; nada se sale de
       la caja, venga del tamaño natural del archivo o del motor que sea. */
    overflow: hidden;
}

/* El media LLENA la caja y nunca la excede: width/height 100% + cover, y los
   topes max-* como segunda línea de defensa (un archivo de tamaño natural
   mayor no puede ganarle a un max-*). object-position center 20% conserva el
   encuadre del prototipo. */
.feed-media img,
.feed-media video {
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    object-fit: cover;
    object-position: center 20%;
}

/* Fit por ORIENTACIÓN (defecto del dueño, 2026-10): "los contenidos en formato
   horizontal se ajustan al alto y no al ancho, lo cual deja contenido fuera de
   la vista". El panel es ~9:19, así que un 16:9 con cover pierde los costados.
   La clase la pone js/feed.js con las dimensiones REALES del archivo (img
   'load', video 'loadedmetadata'): acá no se adivina nada.

   Horizontal (w/h > 1.15) → contain: el ancho ENTERO queda visible y las
   bandas de arriba/abajo caen sobre el fondo oscuro del panel (el media lleva
   su propio fondo para que esas bandas no destellen blanco mientras carga).
   La clase vive en el ELEMENTO, no en la caja: en un carrusel mixto cada slide
   resuelve su fit sin arrastrar al de al lado. */
.feed-media img.media-horizontal,
.feed-media video.media-horizontal,
.feed-media-carrusel img.media-horizontal,
.feed-media-carrusel video.media-horizontal,
.feed-video video.media-horizontal {
    object-fit: contain;
    object-position: center;
    background: #0c0c0e;
}

/* Carrusel (posts con más de un medio): tira horizontal con snap propio.
   El overflow-x:auto es INTENCIONAL (el snap horizontal que pidió el dueño)
   y el eje vertical queda cerrado por el overflow:hidden de .feed-media. */
.feed-media-carrusel {
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
}

.feed-media-carrusel::-webkit-scrollbar {
    display: none;
}

/* Cada slide mide EXACTAMENTE la caja del media: 100%×100%, sin que el tamaño
   natural del archivo se escape (si no, un slide ancho empujaría la tira). */
.feed-media-carrusel > * {
    flex: 0 0 100%;
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    overflow: hidden;
    scroll-snap-align: center;
}

/* La caja del reproductor nace `position:absolute; inset:0` (fondo del panel).
   Dentro del carrusel cada slide tiene que ser un ítem EN FLUJO de 100%×100%:
   si no, todas las cajas se apilarían sobre el primer slide y el snap nunca
   podría llevarte a la segunda. */
.feed-media-carrusel > .feed-video {
    position: relative;
    inset: auto;
    height: 100%;
}

.feed-media-carrusel img,
.feed-media-carrusel video {
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    object-fit: cover;
    object-position: center;
}

/* Unavailable media: la misma familia de visible-state que el placeholder de
   historias — gris, centrado, con ícono y copy claro. Llena el panel (el
   estado debe ser estable; la media real nunca va a un cajón de ratio). */
.feed-media-placeholder {
    display: grid;
    place-items: center;
    gap: 8px;
    height: 100%;
    background: linear-gradient(160deg, #17171b 0%, #0c0c0e 60%, #101014 100%);
    color: var(--morbo-text-muted);
    text-align: center;
    font-size: 0.95rem;
    padding: 24px;
}

.feed-media-placeholder i {
    font-size: 1.9rem;
    opacity: 0.75;
}

.feed-media-placeholder p {
    margin: 0;
}

/* ---- Video inline del feed (S1 #192) ---------------------------------------
   SIN controls nativos (decisión del dueño): el primer cuadro pinta
   (preload=metadata) bajo un velo de reproducción propio; tap reproduce
   inline y el velo se va, otro tap pausa y vuelve. Barra de progreso fina
   abajo (pointer-events:none: el tap cerca del borde llega al video). */

.feed-video {
    position: absolute;
    inset: 0;
    /* La caja del reproductor tampoco deja salir al elemento (defecto 1): el
       video hereda el fit de .feed-media y acá se recorta cualquier exceso. */
    overflow: hidden;
}

.feed-video video {
    cursor: pointer;
    /* Reafirmado a propósito: ninguna regla posterior del feed cambia el fit
       del video (width/height 100% + cover lo hereda de .feed-media video). */
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    object-fit: cover;
    object-position: center 20%;
}

/* Dentro del carrusel el encuadre vuelve al centro (el slide ya está
   encuadrado por el snap: acá no se recorta la cabeza del retrato). */
.feed-media-carrusel .feed-video video {
    object-position: center;
}

.feed-video-velo {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    border: 0;
    padding: 0;
    background: rgba(0, 0, 0, 0.35);
    color: #fff;
    cursor: pointer;
}

.feed-video-velo i {
    display: grid;
    place-items: center;
    width: 64px;
    height: 64px;
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.5);
    font-size: 1.6rem;
}

.feed-video.reproduciendo .feed-video-velo {
    display: none;
}

.feed-video-progreso {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3px;
    background: rgba(255, 255, 255, 0.25);
    pointer-events: none;
}

.feed-video-progreso span {
    display: block;
    width: 0;
    height: 100%;
    background: var(--morbo-accent);
}

/* Altavoz accesible del video (defecto del dueño 2026-10): "los videos deben
   reproducirse en silencio pero debe existir algún botón accesible que permita
   activar el sonido". 44px de toque, arriba a la IZQUIERDA — la derecha la
   ocupa la píldora .feed-cta-top — y por ENCIMA del velo, que cubre toda la
   caja. pointer-events explícito: el velo de abajo no puede robarselo. */
.feed-video-sonido {
    position: absolute;
    top: 10px;
    left: 12px;
    z-index: 2;
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.24);
    border-radius: 999px;
    background: rgba(7, 7, 7, 0.62);
    backdrop-filter: blur(8px);
    color: #fff;
    font-size: 1rem;
    pointer-events: auto;
    cursor: pointer;
    transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.feed-video-sonido:hover {
    border-color: rgba(255, 255, 255, 0.45);
}

.feed-video-sonido:active {
    transform: scale(0.92);
}

/* Estado "se escucha": el botón se enciende (mismo lenguaje que el fuego
   activo, pero blanco pleno: es un control de estado, no una reacción). */
.feed-video-sonido.activo {
    background: rgba(255, 255, 255, 0.93);
    border-color: #fff;
    color: #0b0b0d;
}

/* ---- Fondo de la publicación SOLO TEXTO (defecto del dueño 2026-10) -------
   Sin media, la foto de perfil vuelve como FONDO difuminado (capa .feed-media,
   la misma del media real) con un velo oscuro encima, para que el texto se lea
   sin importar el color de la foto. Si el perfil no tiene foto —o la foto
   falla— la capa no existe y queda el fondo oscuro del panel. */

/* Revelación completa (decisión del dueño 2026): la capa nace invisible y se
   muestra RECÍEN cuando la foto terminó de cargar (js/feed.js agrega
   .feed-fondo-lista en el evento load) — sin transition: el fondo aparece
   completo de golpe, nunca renderizándose progresivo (que se leía como
   'una imagen por cargar'). La capa fallida (display:none) gana sobre lista. */
.feed-media.feed-media-fondo {
    opacity: 0;
}

.feed-media.feed-media-fondo.feed-fondo-lista {
    opacity: 1;
}

.feed-media.feed-media-fondo img {
    /* Encuadre centrado: acá la foto no es retrato de nadie, es textura. */
    object-fit: cover;
    object-position: center;
    /* El scale tapa los bordes transparentes que el blur deja en el marco. */
    filter: blur(24px) saturate(1.05);
    transform: scale(1.15);
}

/* Velo oscuro sobre la foto difuminada (rgba(7,7,7,.55) de base, más denso al
   centro: el texto nunca compite con una foto clara). */
.feed-media.feed-media-fondo::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background:
        radial-gradient(circle at 50% 42%, rgba(7, 7, 7, 0.35) 0%, rgba(7, 7, 7, 0.72) 100%),
        rgba(7, 7, 7, 0.55);
}

/* Foto de fondo inservible: la capa se retira (js/feed.js marca la clase en el
   evento error). El texto no se toca: el post sigue siendo texto. */
.feed-media.feed-media-fondo.feed-fondo-fallido {
    display: none;
}

/* ============ Barra superior RESERVADA (no flota sobre el contenido) ======= */
/* Slice A del rediseño catálogo/feed (2026-10): la barra tiene alto propio en
   el flujo (--topbar-h), fondo sólido y borde inferior. UNA sola fila: flecha
   atrás anclada a la izquierda y logo MORBO centrado. Los chips de género se
   retiraron del feed: el filtro llega arrastrado desde el catálogo (?genero=).
   La clase .header se conserva (contrato externo de la vista). */

.feed-topbar {
    position: relative;
    z-index: 40;
    height: var(--topbar-h);
    padding: calc(10px + env(safe-area-inset-top)) 14px 10px;
    display: flex;
    align-items: center;
    background: #0b0b0d;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

/* La fila ocupa el ancho: logo centrado, flecha absoluta a la izquierda. */
.feed-marca-fila {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    height: 42px;
}

.logo {
    height: 42px;
    width: auto;
}

/* Flecha estilo app bar de Android: SIN recuadro, solo el chevron. */
.btn-back {
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 42px;
    height: 42px;
    display: grid;
    place-items: center;
    color: var(--morbo-text);
    transition: color 0.2s;
}

/* Touch: Android Chrome mantiene :hover pegado tras el tap (sticky hover) y el
   chevron quedaba dorado permanentemente. El hover real solo existe con
   puntero fino; en touch el feedback de presión lo da :active de abajo. */
@media (hover: hover) {
    .btn-back:hover {
        color: var(--morbo-accent);
    }
}

/* El press global (global.css: a:active { transform: translateY(1px) })
   REEMPLAZA el shorthand transform y destruye el centrado translateY(-50%):
   el botón saltaba 22px hacia abajo al tocarlo (diagnóstico CDP 2026). Se
   compone acá: mismo centrado + el empujón de 1px del press. Especificidad
   (0,2,0) gana al (0,1,1) de global. */
.btn-back:active {
    transform: translateY(calc(-50% + 1px));
}

.btn-back svg {
    width: 22px;
    height: 22px;
}

/* Scrim inferior del panel (defecto del dueño 2026-10: "el texto está muy
   perdido"): gradiente que oscurece la base para que la autoría y la
   descripción se lean sobre CUALQUIER media. Vive por encima del media
   (z-index 4, el media es -2) y POR DEBAJO del texto (autoría 10, riel 12) y
   es transparente al toque: el velo del video y el tap siguen llegando al
   media. */
.feed-panel::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 46%;
    z-index: 4;
    pointer-events: none;
    background: linear-gradient(to top, rgba(7, 7, 7, 0.95) 0%, rgba(7, 7, 7, 0.8) 38%, rgba(7, 7, 7, 0) 100%);
}

/* ============ Autoría: overlay inferior izquierdo =========================== */

.feed-autoria {
    position: absolute;
    left: 16px;
    right: 92px;
    bottom: calc(var(--feed-cta-h) + 16px + env(safe-area-inset-bottom));
    z-index: 10;
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.feed-autoria.vista {
    opacity: 1;
    transform: translateY(0);
}

.feed-card-header {
    margin: 0;
}

/* Slice A: sin avatar en la autoría — el link es SOLO el bloque de texto:
   nombre + meta apilados. (La tira de historias que heredaba el avatar también
   salió del feed, decisión del dueño 2026-10.) */
.feed-autor {
    display: flex;
    align-items: center;
    gap: 0;
    width: fit-content;
}

.feed-quien {
    display: grid;
    line-height: 1.25;
    min-width: 0;
}

.feed-autor-nombre {
    font-size: 15.5px;
    font-weight: 600;
    display: block;
    line-height: 1.25;
}

.feed-autor-meta {
    display: block;
    font-size: 12px;
    color: var(--morbo-text-muted);
    line-height: 1.3;
}

.feed-autor:hover .feed-autor-nombre {
    text-decoration: underline;
    text-underline-offset: 3px;
}

.feed-descripcion {
    margin: 8px 0 0;
    /* Blanco pleno (antes --morbo-text-soft) + 17px (antes 15px) y 3 líneas
       (antes 2): la descripción del post con media se lee sin esfuerzo sobre
       el scrim densificado del panel (decisión del dueño 2026). */
    color: #fff;
    font-size: 17px;
    line-height: 1.5;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9), 0 2px 14px rgba(0, 0, 0, 0.65);
    white-space: pre-wrap;
    word-break: break-word;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    cursor: pointer;
}

.feed-descripcion.abierta {
    display: block;
    -webkit-line-clamp: unset;
    overflow: visible;
}

/* Ver más / Ver menos (descripción con media): nace OCULTO y js/feed.js le
   agrega la variante --visible SOLO cuando la medición post-layout confirma
   que el clamp truncó (scrollHeight > clientHeight). Pill discreto alineado
   al texto, no compite con el media. */
.feed-ver-mas {
    display: none;
    margin-top: 8px;
    padding: 6px 14px;
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    cursor: pointer;
}

.feed-ver-mas.feed-ver-mas--visible {
    display: inline-block;
}

.feed-ver-mas:active {
    background: rgba(255, 255, 255, 0.2);
}

.feed-fecha {
    display: block;
    margin-top: 4px;
    font-size: 11.5px;
    color: var(--morbo-text-muted);
    opacity: 0.75;
}

/* Solo-texto: la palabra ES el medio — cita ALINEADA A LA IZQUIERDA sobre la
   foto de perfil difuminada (decisión del dueño 2026: el centrado se leía
   desalineado; las comillas dobles que envuelven el texto van en el
   textContent, js/feed.js). Verticalmente centrada (top 50% + translateY,
   nunca pisa el overlay de autoría de abajo) y la caja vuelve a ser
   asimétrica: izquierda 24px, derecha 92px despejando el riel.
   z-index 6: encima del scrim del panel (4). */

.feed-descripcion.feed-cita {
    position: absolute;
    z-index: 6;
    top: 50%;
    bottom: auto;
    left: 24px;
    right: 92px;
    margin: 0;
    transform: translateY(-50%);
    text-align: left;
    font-size: clamp(1.15rem, 5vw, 1.7rem);
    font-weight: 600;
    line-height: 1.4;
    letter-spacing: -0.01em;
    color: #fff;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95), 0 8px 28px rgba(0, 0, 0, 0.65);
    display: block;
    -webkit-line-clamp: unset;
    overflow: visible;
    cursor: default;
}

/* Cita BREVE (una o dos palabras, umbral en js/feed.js: <= 24 caracteres):
   al tamaño estándar se pierde en el panel (feedback del dueño 2026). Sube un
   poco respecto del centro (42%) y crece la tipografía, con tope moderado:
   presencia, no exageración. Mismos insets y sombra que la cita estándar. */

.feed-descripcion.feed-cita.feed-cita-breve {
    top: 42%;
    font-size: clamp(1.6rem, 8vw, 2.3rem);
}

/* ============ Riel derecho de acciones (prototipo aprobado) ================= */
/* Botones circulares con SVG + etiqueta debajo; jamás tapan el media. */

.feed-riel {
    position: absolute;
    right: 12px;
    bottom: calc(var(--feed-cta-h) + 16px + env(safe-area-inset-bottom));
    z-index: 12;
    display: grid;
    justify-items: center;
    gap: 18px;
}

.feed-fuego-btn {
    display: grid;
    justify-items: center;
    gap: 4px;
    padding: 0;
    border: 0;
    background: none;
    color: #fff;
    white-space: nowrap;
}

.feed-accion-nucleo {
    width: 50px;
    height: 50px;
    border-radius: 999px;
    display: grid;
    place-items: center;
    background: rgba(17, 17, 17, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.14);
    transition: transform 0.15s, border-color 0.2s, background 0.2s, color 0.2s;
}

.feed-accion-nucleo svg {
    width: 23px;
    height: 23px;
}

.feed-fuego-cuenta {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--morbo-text);
    font-variant-numeric: tabular-nums;
}

.feed-fuego-btn:not(:disabled):hover .feed-accion-nucleo {
    box-shadow: 0 0 14px rgba(255, 0, 110, 0.35);
}

.feed-fuego-btn:not(:disabled):active .feed-accion-nucleo {
    transform: scale(0.9);
}

.feed-fuego-btn.activo {
    color: var(--morbo-accent);
}

/* Aro de contraste (pedido del dueño, 2026): el círculo del fuego viste el
   MISMO degradé del aro de historias (gold→pink→violet, css/inicio.css
   .historia-ring) para competir con ellas en la vista; el interior queda
   oscuro para que la llama respire. Técnica doble-fondo: relleno en
   padding-box + degradé en border-box (el borde transparente muestra el
   degradé, jamás un border-color sólido encima). */
.feed-fuego-btn .feed-accion-nucleo {
    border: 2px solid transparent;
    background:
        linear-gradient(rgba(17, 17, 17, 0.72), rgba(17, 17, 17, 0.72)) padding-box,
        linear-gradient(45deg, var(--morbo-story-gold), var(--morbo-story-pink), var(--morbo-story-violet)) border-box;
}

.feed-fuego-btn.activo .feed-accion-nucleo {
    color: var(--morbo-accent);
    background:
        linear-gradient(rgba(229, 197, 91, 0.16), rgba(229, 197, 91, 0.16)) padding-box,
        linear-gradient(45deg, var(--morbo-story-gold), var(--morbo-story-pink), var(--morbo-story-violet)) border-box;
    box-shadow: 0 0 16px rgba(229, 197, 91, 0.35);
}

/* ---- Fuego vivo (pedido del dueño, 2026) ---------------------------------
   La llama arde SIEMPRE con flicker propio (anclada abajo, como una llama
   real); en hover arde más rápido; al reaccionar hay burst de UN disparo
   (clase puesta por js/feed.js, retirada en animationend) y el estado activo
   queda con glow dorado sostenido. Apagable con prefers-reduced-motion. */
.feed-fuego-btn .feed-accion-nucleo svg {
    transform-origin: 50% 82%;
    animation: fuegoLlamaVive 1.7s ease-in-out infinite alternate;
}

.feed-fuego-btn:not(:disabled):hover .feed-accion-nucleo svg {
    animation-duration: 0.7s;
}

.feed-fuego-btn.fuego-burst .feed-accion-nucleo {
    animation: fuegoBurst 0.6s cubic-bezier(0.2, 1.6, 0.4, 1) 1;
}

@keyframes fuegoLlamaVive {
    0% { transform: scale(1, 1) skewX(0deg); filter: brightness(1); }
    35% { transform: scale(1.1, 0.93) skewX(-4deg); filter: brightness(1.15); }
    70% { transform: scale(0.94, 1.12) skewX(3deg); filter: brightness(0.92); }
    100% { transform: scale(1.06, 1.03) skewX(-1.5deg); filter: brightness(1.18); }
}

@keyframes fuegoBurst {
    0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 0, 110, 0); }
    45% { transform: scale(1.35); box-shadow: 0 0 24px rgba(255, 0, 110, 0.55); }
    100% { transform: scale(1); box-shadow: 0 0 16px rgba(229, 197, 91, 0.35); }
}

.feed-fuego-btn:disabled {
    cursor: default;
}

.feed-cta {
    display: grid;
    justify-items: center;
    gap: 4px;
    padding: 0;
    border: 0;
    background: none;
    color: #fff;
    white-space: nowrap;
}

.feed-cta:hover .feed-accion-nucleo {
    border-color: rgba(255, 255, 255, 0.32);
}

.feed-cta:active .feed-accion-nucleo {
    transform: scale(0.9);
}

.feed-cta-wa {
    color: #fff;
}

/* WhatsApp en VERDE marca (defecto del dueño 2026-10: "el botón de whatsapp
   del feed debe estar verde para que destaque"): el círculo del riel se viste
   con #25D366 e icono blanco, para que salte sobre el fondo oscuro del panel.
   El fuego queda dorado y el círculo de perfil neutro: el verde identifica UNA
   acción, no una familia. */
.feed-cta-wa .feed-accion-nucleo {
    background: #25D366;
    border-color: rgba(255, 255, 255, 0.35);
    color: #fff;
}

.feed-cta-wa:hover .feed-accion-nucleo {
    background: #1FBE5A;
    border-color: #fff;
}

/* ---- Latido del WhatsApp tras reaccionar con fuego (pedido del dueño) ----
   3 latidos FUERTES en el momento de la reacción (js/feed.js agrega la clase
   y animationend la cambia por la suave) y latido SUAVE permanente mientras
   el post esté reaccionado (también en render inicial de posts ya reaccionados).
   El glow usa el verde marca: el latido identifica la acción de contacto. */
.feed-cta-wa.feed-wa-late-suave .feed-accion-nucleo {
    animation: waLatidoSuave 2.8s ease-in-out infinite;
}

.feed-cta-wa.feed-wa-late-fuerte .feed-accion-nucleo {
    animation: waLatidoFuerte 4.5s ease-in-out 1;
}

@keyframes waLatidoSuave {
    0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(37, 211, 102, 0); }
    50% { transform: scale(1.07); box-shadow: 0 0 10px 1px rgba(37, 211, 102, 0.4); }
}

@keyframes waLatidoFuerte {
    /* 6 latidos en ~4.5s (ajuste del dueño: la fase fuerte dura más) antes
       del swap a suave por animationend. */
    0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(37, 211, 102, 0); }
    4% { transform: scale(1.28); box-shadow: 0 0 20px 3px rgba(37, 211, 102, 0.6); }
    9% { transform: scale(1); }
    14% { transform: scale(1.24); box-shadow: 0 0 18px 2px rgba(37, 211, 102, 0.55); }
    19% { transform: scale(1); }
    25% { transform: scale(1.22); box-shadow: 0 0 16px 2px rgba(37, 211, 102, 0.5); }
    31% { transform: scale(1); }
    37% { transform: scale(1.21); box-shadow: 0 0 16px 2px rgba(37, 211, 102, 0.5); }
    43% { transform: scale(1); }
    49% { transform: scale(1.2); box-shadow: 0 0 15px 2px rgba(37, 211, 102, 0.48); }
    55% { transform: scale(1); }
    61% { transform: scale(1.19); box-shadow: 0 0 14px 2px rgba(37, 211, 102, 0.46); }
    67% { transform: scale(1); }
    73% { transform: scale(1.18); box-shadow: 0 0 13px 1px rgba(37, 211, 102, 0.44); }
    79% { transform: scale(1); }
}

.feed-cta-perfil .feed-accion-nucleo {
    border: 2px solid var(--morbo-accent);
    background: var(--morbo-surface);
    color: var(--morbo-accent);
    font-weight: 700;
    font-size: 17px;
}

/* ---- Avatar del círculo de perfil (decisión del dueño, 2026) --------------
   El círculo del riel muestra la FOTO del perfil (fotos[0] vía MorboMedia);
   la inicial (.feed-accion-inicial) queda SOLO como estado fallback (sin
   foto usable → letra). Con imagen, esta llena el círculo: hereda el
   redondeo, recorta con cover y no hay letra que esconder (la inicial solo
   se renderiza cuando no hay <img>). */
.feed-cta-perfil .feed-accion-nucleo img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: inherit;
}

/* ============ CTA contextual compacta (arriba a la derecha) =================
   El dueño: el botón grande de abajo estorba — pasa a una píldora chica bajo
   la barra superior. El href lo actualiza conectarBarraContextual por post. */

.feed-cta-top {
    position: fixed;
    top: calc(var(--topbar-h) + 10px);
    right: 12px;
    z-index: 45;
    display: inline-flex;
    align-items: center;
    padding: 8px 14px;
    border-radius: 999px;
    background: rgba(7, 7, 7, 0.66);
    border: 1px solid rgba(255, 255, 255, 0.14);
    backdrop-filter: blur(8px);
    color: #fff;
    font-weight: 600;
    font-size: 13px;
    text-decoration: none;
    transition: color 0.2s, border-color 0.2s;
}

.feed-cta-top:hover {
    color: var(--morbo-accent);
    border-color: rgba(229, 197, 91, 0.4);
}

.feed-cta-top[hidden] {
    display: none;
}

/* ============ Estados: vacío / error / gate / cargar más ==================== */
/* Sin callejón sin salida: siempre hay salida útil (catálogo) y legal visible.
   Arrancan DEBAJO de la barra reservada: la flecha atrás y el logo siguen
   visibles y usables en el gate (la salida nunca se tapa). */

.feed-vacio,
.feed-error,
.feed-proximamente {
    position: fixed;
    top: var(--topbar-h);
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 30;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 6px;
    padding: 24px;
    text-align: center;
    background: var(--morbo-bg);
}

.feed-vacio[hidden],
.feed-error[hidden],
.feed-proximamente[hidden] {
    display: none;
}

.feed-vacio i,
.feed-proximamente > i {
    font-size: 2rem;
    color: var(--morbo-accent);
    margin-bottom: 8px;
}

.feed-vacio i {
    font-size: 2rem;
    color: var(--morbo-accent);
    margin-bottom: 8px;
}

.feed-error {
    color: var(--morbo-text-muted);
}

.feed-proximamente h2 {
    font-size: 22px;
    font-weight: 700;
}

.feed-proximamente p {
    max-width: 40ch;
    color: var(--morbo-text-muted);
    font-size: 13.5px;
}

.feed-proximamente-cta,
.feed-vacio-cta {
    margin-top: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 44px;
    padding: 12px 24px;
    border-radius: 999px;
    background: var(--morbo-accent);
    color: var(--morbo-on-accent);
    font-weight: 600;
    font-size: 14px;
    transition: background 0.2s;
}

.feed-proximamente-cta {
    margin-top: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 44px;
    padding: 12px 24px;
    border-radius: 999px;
    background: var(--morbo-accent);
    color: var(--morbo-on-accent);
    font-weight: 600;
    font-size: 14px;
    transition: background 0.2s;
}

.feed-proximamente-cta:hover,
.feed-vacio-cta:hover {
    background: var(--morbo-accent-deep);
}

.feed-proximamente-cta:hover {
    background: var(--morbo-accent-deep);
}

.feed-proximamente-cta:focus-visible,
.feed-vacio-cta:focus-visible {
    outline: 2px solid var(--morbo-accent);
    outline-offset: 2px;
}

.feed-proximamente-cta:focus-visible {
    outline: 2px solid var(--morbo-accent);
    outline-offset: 2px;
}

.feed-legal {
    margin-top: 18px;
    font-size: 11px;
    color: var(--morbo-text-muted);
    display: flex;
    gap: 8px;
}

.feed-legal a {
    text-decoration: underline;
    text-underline-offset: 3px;
}

.feed-cargar-sentinel {
    height: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

.feed-cargar-mas {
    display: block;
    margin: 24px auto;
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 999px;
    background: transparent;
    color: var(--morbo-text);
    font-weight: 600;
    padding: 12px 28px;
    cursor: pointer;
}

.feed-cargar-mas:not(:disabled):hover {
    border-color: var(--morbo-accent);
}

.feed-cargar-mas[hidden] {
    display: none;
}

/* ============ Contenido ENTRE las dos barras (T2, decisión sellada #6) ===== */
/* Con sesión de escort, js/escort-shell.js pone `.escort-shell` en <body> y el
   kit (css/escort-ui.css) pinta la nav inferior: --escort-nav-h (45px) MÁS el
   relleno de safe-area que la propia barra agrega (padding-bottom:
   env(safe-area-inset-bottom)). Ese es el alto real que hay que descontar:
   - el escenario y CADA panel pierden la franja de la nav con el MISMO calc,
     así el snap sigue exacto (un panel = el viewport entre barras, igual que
     en el caso anónimo) — no hace falta tocar el final del scroll;
   - la barra CTA se sube ENCIMA de la nav (nunca debajo) y su degradado deja
     de repetir la safe-area: la aporta la nav, sumarla dos veces abriría un
     hueco de 34px en el teléfono con notch;
   - los estados de pantalla completa (vacío / error / próximamente) cierran
     también sobre la nav, con su bloque centrado y su legal visibles.
   La nav del kit se oculta desde 768px (sección 11 del kit), así que los
   offsets viven en el mismo rango móvil: en desktop el marco sigue cerrando
   EXACTAMENTE con --feed-stage-h (ver el bloque de escritorio). */
@media (max-width: 767px) {
    body.escort-shell .feed-stage,
    body.escort-shell .feed-panel {
        height: calc(100dvh - var(--topbar-h) - var(--escort-nav-h, 45px) - env(safe-area-inset-bottom, 0px));
    }

    body.escort-shell .feed-vacio,
    body.escort-shell .feed-error,
    body.escort-shell .feed-proximamente {
        bottom: calc(var(--escort-nav-h, 45px) + env(safe-area-inset-bottom, 0px));
    }
}

/* ============ Desktop: columna inmersiva centrada =========================== */
/* Slice A: la barra reservada cruza el ancho completo y el escenario enmarcado
   queda centrado debajo (prototipo aprobado). La barra CTA se alinea al ancho
   de la columna. */

@media (min-width: 960px) {
    :root {
        /* Alto del escenario enmarcado: lo consumen el propio stage y el borde
           inferior de la barra CTA (que tiene que cerrar EXACTAMENTE ahí). */
        --feed-stage-h: min(calc(100dvh - var(--topbar-h) - 24px), 880px);
    }

    body {
        display: flex;
        flex-direction: column;
        align-items: center;
    }

    .feed-topbar {
        width: 100%;
    }

    .feed-stage {
        width: min(480px, 44vh);
        height: var(--feed-stage-h);
        border-radius: 24px;
        border: 1px solid rgba(255, 255, 255, 0.12);
        box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
    }

    .feed-cta-top {
        left: 50%;
        transform: translateX(-50%);
        width: min(480px, 44vh);
        justify-content: center;
    }
}

/* ============ Movimiento reducido =========================================== */

@media (prefers-reduced-motion: reduce) {
    .feed-autoria {
        opacity: 1;
        transform: none;
        transition: none;
    }

    * {
        transition-duration: 0.01ms !important;
    }

    /* Fuego vivo y latido del WhatsApp (pedido del dueño, 2026): sin
       movimiento para quien pide movimiento reducido. */
    .feed-fuego-btn .feed-accion-nucleo svg,
    .feed-fuego-btn.fuego-burst .feed-accion-nucleo,
    .feed-cta-wa.feed-wa-late-suave .feed-accion-nucleo,
    .feed-cta-wa.feed-wa-late-fuerte .feed-accion-nucleo {
        animation: none;
    }
}

/* ============ Semillas: paneles de perfil tipo post (feed sin posts) ======== */
/* Una semilla es un perfil aprobado presentado como panel de feed cuando no
   hay posts (o al llegar al fin del scroll). El markup reusa la base
   .feed-panel (media full-bleed + autoría overlay + scrim::after heredados)
   y desde WU7 replica el overlay COMPLETO de un post (autoría, descripción,
   riel de CTAs) con el modificador --semilla. Lo ÚNICO que no existe en el
   DOM de la semilla: fuego/contador (sin reacciones), fecha relativa (no hay
   creado_en) y el chip de WU2 (reemplazado por la paridad de post). El panel
   ENTERO es el link al perfil inmersivo. */

.feed-panel--semilla {
    display: block;
    color: inherit;
    text-decoration: none;
}

/* Foco visible para teclado: el panel es un anchor que ocupa todo el viewport
   del stage; el anillo adentro para que no se recorte con el overflow:hidden
   del panel. */
.feed-panel--semilla:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.7);
    outline-offset: -2px;
}

/* La autoría del post aparece con un IntersectionObserver (feed.js agrega
   .vista); una semilla no depende de ese IO: visible SIEMPRE, con la misma
   transición de entrada. Posición y offsets: los de un post (right reserva el
   riel, bottom reserva la altura de CTA del panel) — heredados sin override
   para que la paridad sobreviva a cualquier ajuste del overlay del post. */
.feed-panel--semilla .feed-autoria {
    opacity: 1;
    transform: none;
}

/* El hover del post subraya el nombre via .feed-autor:hover; acá el autor es
   un <span> (el anchor del panel ya cubre la navegación) — el hover del PANEL
   entero hereda la misma affordance. */
.feed-panel--semilla:hover .feed-autor-nombre {
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* Movimiento reducido: la autoría de la semilla ya nace visible; acá solo se
   apaga cualquier transición residual heredada de .feed-autoria. */
@media (prefers-reduced-motion: reduce) {
    .feed-panel--semilla .feed-autoria {
        transition: none;
    }
}
