* { box-sizing: border-box; }

[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: var(--font-family);
  /* Tres luces muy sutiles (azul, turquesa, violeta) sobre #081225 —
     regla explícita: mantener el fondo, sin fotografías ni animación. */
  background:
    radial-gradient(circle at 15% 15%, rgba(var(--accent-purple-rgb), 0.14), transparent 35%),
    radial-gradient(circle at 50% 100%, rgba(var(--accent-cyan-rgb), 0.08), transparent 40%),
    radial-gradient(circle at 85% 85%, rgba(var(--accent-violet-rgb), 0.1), transparent 38%),
    var(--bg-page);
  background-attachment: fixed;
  color: var(--text-primary);
}

button {
  font-family: inherit;
  cursor: pointer;
}

input[type="text"],
input[type="password"],
input[type="email"],
select {
  font-family: inherit;
  /* font-size explícito en 1rem (16px): sin esto, los campos heredan el
     tamaño por defecto del navegador para controles de formulario (a veces
     menor a 16px) y iOS hace zoom automático al enfocar — mobile-first. */
  font-size: 1rem;
  min-height: 50px;
  background: var(--input-bg);
  border: 1px solid var(--border-color);
  color: var(--text-primary);
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.8rem;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

input[type="text"]::placeholder,
input[type="password"]::placeholder,
input[type="email"]::placeholder {
  color: var(--text-muted);
}

input[type="text"]:focus,
input[type="password"]:focus,
input[type="email"]:focus,
select:focus {
  outline: none;
  border-color: var(--accent-purple);
  box-shadow: 0 0 0 3px var(--accent-purple-soft);
}

.btn-primary {
  background: var(--accent-purple);
  color: var(--accent-contrast, #fff);
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.7rem 1.2rem;
  font-size: 1rem;
  font-weight: 600;
  white-space: nowrap;
  /* Mobile-first: 52px de alto, fácil de tocar con el pulgar en cualquier
     botón principal de la app, no solo el de entrar. */
  min-height: 52px;
  transition: background 0.18s ease, transform 0.08s ease;
}

.btn-primary:hover {
  background: var(--accent-purple-hover);
}

.btn-primary:active {
  background: var(--accent-purple-active);
}

.btn-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.btn-primary:disabled:hover {
  background: var(--accent-purple);
}

.icon-btn {
  background: var(--bg-surface-2);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.9rem;
  white-space: nowrap;
  transition: background 0.18s ease, border-color 0.18s ease;
  /* Íconos SVG en vez de emoji (2026-07-24, "íconos más profesionales") —
     centrado real necesario ahora que el contenido es un <svg>, no texto
     que se autocentraba solo. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
}
.icon-btn svg { display: block; flex-shrink: 0; }
.icon-btn:hover svg { color: var(--accent-purple); }

.icon-btn:hover {
  background: var(--bg-elevated);
}

.icon-btn.danger {
  background: var(--accent-danger);
  border-color: var(--accent-danger);
  color: #fff;
}

/* Íconos "premium" de la barra superior (2026-07-25, pedido explícito: "los
   íconos sea más premium") — escopeado a los botones de función del topbar
   (mensajes, amigos, quiz, en línea, muro, mi perfil), no toca el resto de
   usos de .icon-btn en la app (listas de acciones, botones con texto, etc.)
   para no desarmar esos layouts. Círculo con degradado sutil + glow violeta
   al pasar el mouse, mismo acento que ya usan los elementos Premium/VIP. */
.topbar-icon-btn {
  width: 40px;
  height: 40px;
  padding: 0;
  border-radius: 50%;
  background: linear-gradient(145deg, var(--bg-elevated), var(--bg-surface-2));
  border: 1px solid var(--border-soft);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 2px 6px rgba(0, 0, 0, 0.25);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.topbar-icon-btn svg {
  color: var(--text-secondary);
  transition: color 0.18s ease;
}
.topbar-icon-btn:hover {
  background: linear-gradient(145deg, var(--bg-elevated), var(--accent-violet-soft));
  border-color: rgba(var(--accent-violet-rgb), 0.55);
  box-shadow: 0 0 0 3px rgba(var(--accent-violet-rgb), 0.14), 0 6px 16px rgba(var(--accent-violet-rgb), 0.28);
  transform: translateY(-2px);
}
.topbar-icon-btn:hover svg {
  color: var(--accent-violet);
}
.topbar-icon-btn:active {
  transform: translateY(0) scale(0.95);
}
/* Avatar propio ya trae su propio marco/glow — el hueco entre él y el
   siguiente ícono circular quedaba desparejo con el gap general de
   .status-group, se ajusta puntual acá. */
.topbar-icon-btn + .avatar-frame-wrap,
.avatar-frame-wrap + .topbar-icon-btn {
  margin-left: 0.1rem;
}

/* Topbar */
.topbar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-color);
  padding: 0.7rem 1rem;
  /* Área segura (notch/isla dinámica) en PWA standalone — env() da 0 en
     cualquier otro contexto, así que no cambia nada fuera de ese caso. */
  padding-top: max(0.7rem, env(safe-area-inset-top));
}

/* Logo real del topbar (2026-07-27, pedido explícito) — reemplaza el
   ícono cuadrado viejo (logo.svg) + el texto "VacaWeb Chat" por el mismo
   logotipo ancho que ya se ve en la pantalla de entrada. El PNG fuente
   tiene mucha transparencia arriba/abajo (ver comentario junto a
   .entry-logo-crop) así que se recorta con la misma técnica: la caja
   (topbar-logo-crop) solo deja ver el alto real del contenido, la imagen
   completa va desplazada hacia arriba adentro. height = width * 0.1738,
   top = -width * 0.25 — misma proporción del logo, cualquier ancho nuevo
   solo hay que multiplicar por esos dos factores. */
.topbar-logo-crop {
  width: 150px;
  height: 26.07px;
  overflow: hidden;
  position: relative;
  flex-shrink: 0;
}
.topbar-logo {
  display: block;
  position: absolute;
  top: -37.5px;
  left: 0;
  width: 150px;
}

.status-group {
  margin: 0 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.status-pill {
  font-size: 0.8rem;
  color: var(--text-secondary);
  margin: 0;
}

.avatar {
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  color: #fff;
  flex-shrink: 0;
  box-shadow: 0 0 0 2px var(--bg-surface);
  overflow: hidden;
}

.avatar-photo {
  background: var(--bg-elevated);
}

.avatar-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Marco de avatar (2026-07-22) — aro decorativo alrededor de la foto/
   iniciales. El wrapper mide lo mismo que el avatar (para no descuadrar el
   layout de roster/mensajes que ya asumen ese tamaño), pero con overflow
   visible: el marco se dibuja más grande (1.43x, ver avatar.js) y sobresale
   a propósito, como en las apps de referencia. */
.avatar-frame-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: visible;
}
.avatar-frame-overlay {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  object-fit: contain;
  pointer-events: none;
}
/* Marco "corona" de la tienda (2026-07-24) — imagen sólida (no un anillo
   con hueco), se apoya arriba del avatar como un sombrero en vez de
   centrarse tapándolo entero. */
.avatar-frame-overlay.avatar-frame-crown {
  top: -8%;
  left: 50%;
  transform: translate(-50%, -55%);
}

/* Aros Premium (2026-07-26, pedido explícito: "que tenga movimiento o algún
   efecto especial") — brillo pulsante suave, no rotación (la corona/detalle
   de arriba del diseño se vería rara girando). */
@keyframes premium-frame-pulse {
  0%, 100% { filter: drop-shadow(0 0 3px rgba(139, 108, 255, 0.55)) brightness(1); transform: translate(-50%, -50%) scale(1); }
  50% { filter: drop-shadow(0 0 11px rgba(139, 108, 255, 0.95)) brightness(1.08); transform: translate(-50%, -50%) scale(1.035); }
}
.avatar-frame-premium {
  animation: premium-frame-pulse 2.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .avatar-frame-premium { animation: none; }
}

/* Picker de marcos en el modal de perfil — celdas más grandes que
   .badge-option normal (38px) porque los diseños de los aros tienen mucho
   detalle fino que se pierde a ese tamaño. */
.profile-frame-grid {
  grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
}

.sidebar-toggle {
  display: none;
  background: transparent;
  border: none;
  color: var(--text-primary);
  font-size: 1.2rem;
  padding: 0.2rem 0.4rem;
}

/* Layout */
.layout {
  display: flex;
  height: calc(100vh - 58px);
  /* Se descuenta también env(safe-area-inset-top): el topbar ahora suma esa
     misma área segura a su padding-top (PWA standalone con notch/isla
     dinámica), así que .layout debe encoger lo mismo que el topbar creció,
     o el fondo de la pantalla (formulario de mensaje, controles) queda
     recortado. En cualquier dispositivo sin notch, env() es 0 y no cambia
     nada. */
  height: calc(100dvh - 58px - env(safe-area-inset-top));
}

.sidebar {
  width: var(--sidebar-width);
  flex-shrink: 0;
  background: var(--bg-surface);
  border-right: 1px solid var(--border-color);
  padding: 1rem;
  overflow-y: auto;
}

#create-room-form {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.admin-login-toggle {
  display: block;
  width: 100%;
  padding: 0.4rem 0.5rem;
  margin-bottom: 0.5rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-color);
  background: transparent;
  color: var(--text-secondary);
  font-size: 0.75rem;
  cursor: pointer;
}

.admin-login-form {
  display: flex;
  gap: 0.4rem;
  margin-bottom: 0.75rem;
}

.admin-login-form input {
  flex: 1;
  min-width: 0;
}

/* Subtítulo "Chatea sin registro" (2026-07-22, pedido explícito) — debajo
   del título de bienvenida. Bug real reportado (captura marinela.png/2):
   .modal-header es flex con justify-content:space-between (pensado para
   título + botón cerrar a los costados) — el subtítulo, como hermano del
   h3, quedaba al lado en vez de abajo, con las letras encimadas/cortadas en
   pantallas angostas. Se scopea SOLO a este modal (el resto de modal-header
   del proyecto, con botón de cerrar, sigue igual). */
/* Composición logo + título + descripción como un solo bloque centrado
   (2026-07-24, integración visual pedida explícitamente) — antes quedaba
   alineado a la izquierda, con el logo y el título separados por poco
   espacio; ahora todo el header se lee como una sola unidad. */
.entry-modal-backdrop .modal-header {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-bottom: none;
  /* Tarjeta compacta (ajuste fino 2026-07-24): menos padding arriba/abajo
     del que tenía el modal-header genérico. */
  padding: 1.1rem 1.2rem 0.2rem;
}
/* Logo — ajuste fino 2026-07-24: +30% sobre el tamaño anterior (135px/165px
   → 175px/215px), mobile-first (base = móvil, min-width lo agranda en
   escritorio). vacawebcha-transparente.png es el isotipo+wordmark completo
   (no un ícono cuadrado) — se deja la altura automática para no
   deformarlo. Margen inferior reducido a 24px (antes 28px) y el h3 pierde
   su margen por defecto del navegador (ver regla de abajo) para que
   logo+título+descripción se lean como un solo bloque, sin el espacio
   vacío extra que sumaban ambos márgenes together. */
/* Bug real corregido (2026-07-24): vacawebcha-transparente.png es un
   lienzo de 1536x1024 pero el logo real (ícono+texto) solo ocupa una franja
   angosta en el medio — medido con precisión (pixel por pixel, canvas +
   getImageData): el contenido visible va de 384px a 651px de alto (267px
   de 1024, ~26%). El resto es transparencia — a esto se debía el "espacio
   enorme" entre el logo y "Bienvenido": la CAJA de la imagen (con toda su
   transparencia) sí tenía el margen correcto (24px), pero la imagen
   entera ocupaba ~117px de alto aunque el logo visible mide ~30px.
   .entry-logo-crop recorta la caja al tamaño real del contenido visible
   (overflow:hidden + la imagen completa desplazada hacia arriba adentro) —
   los números sos fracciones exactas del ancho (topOffset = -25% del
   ancho, altura recortada = 17.38% del ancho), así escalan solos entre
   los dos tamaños de logo (móvil/escritorio) sin recalcular a mano. */
.entry-logo-crop {
  width: 175px;
  height: 30.42px;
  overflow: hidden;
  position: relative;
  margin: 0 auto 14px;
}
.entry-logo {
  display: block;
  position: absolute;
  top: -43.75px;
  left: 0;
  width: 175px;
  height: auto;
}
@media (min-width: 700px) {
  .entry-logo-crop {
    width: 215px;
    height: 37.37px;
  }
  .entry-logo {
    width: 215px;
    top: -53.75px;
  }
}
.entry-modal-backdrop h3 {
  margin: 0;
  font-size: 1.4rem;
  font-weight: 700;
}
.entry-subtitle {
  margin-top: 0.3rem;
  color: var(--text-secondary);
  font-size: 0.9rem;
}

/* Texto de ayuda bajo el botón principal ("No necesitas registrarte") */
.entry-helper-text {
  margin: 0.6rem 0 0;
  text-align: center;
  /* Ajuste fino 2026-07-24: text-muted se leía como "desactivado" —
     text-secondary es más luminoso y sigue siendo un tono secundario, no
     compite con el título. */
  color: var(--text-secondary);
  font-size: 0.8rem;
}

/* Iniciar sesión con Google (2026-07-24) */
.entry-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 16px 0 12px;
  color: var(--text-muted);
  font-size: 0.78rem;
}
.entry-divider::before,
.entry-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border-color);
}
.entry-google-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  min-height: 48px;
  background: var(--bg-elevated);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.18s ease, border-color 0.18s ease;
}
.entry-google-btn:hover {
  background: var(--accent-purple-soft);
  border-color: var(--accent-purple);
}

/* Acciones secundarias — "Iniciar sesión" / "Crear una cuenta", lado a lado
   en escritorio; en móvil se acomodan sin perder el mínimo táctil (44px). */
.entry-secondary-actions {
  display: flex;
  gap: 0.6rem;
}
.entry-secondary-btn {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  padding: 0.5rem 0.8rem;
  /* Ajuste fino 2026-07-24: fondo transparente + borde tenue se leía como
     "desactivado". Con bg-elevated de reposo y borde/texto más presentes
     se distingue como acción real, sin dejar de ser secundario ni de verse
     oscuro (Aurora Premium). */
  background: var(--bg-elevated);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, transform 0.08s ease;
}
.entry-secondary-btn:hover {
  /* --bg-elevated ya es el tono más claro de la escala Aurora — para un
     hover más luminoso "sin exagerar" se suma un tinte azul translúcido en
     vez de buscar un gris más claro que no existe en la paleta. */
  background: var(--accent-purple-soft);
  border-color: var(--accent-purple);
  color: var(--accent-select);
}
.entry-secondary-btn:active {
  transform: scale(0.98);
}

/* Franja de login/crear cuenta — vive FUERA de .modal-body/.modal-footer,
   como su propia sección al pie del modal. */
.entry-login-section {
  padding: 0.75rem 1.5rem 1rem;
  border-top: 1px solid var(--border-color);
  text-align: center;
}
#entry-login-fields {
  margin-top: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
#entry-login-fields input {
  width: 100%;
}
/* "¿Olvidaste tu contraseña?" (2026-07-24) — mismo criterio visual que ya
   usaba .entry-admin-toggle (retirado en la integración visual de Aurora
   Premium): texto chico, subrayado, sin fondo, no compite con los botones
   reales de la fila de arriba. */
.entry-forgot-link {
  display: block;
  margin-top: 0.5rem;
  padding: 0;
  border: none;
  background: none;
  color: var(--text-secondary);
  font-size: 0.78rem;
  text-decoration: underline;
  cursor: pointer;
}

/* Banner para apartar el nick (2026-07-22, pedido explícito) — barra
   angosta debajo del topbar, no un modal: no debe interrumpir a nadie que
   ya está chateando, solo ofrecer la opción hasta que la cierren o se
   registren. */
/* Bug real reportado (2026-07-22, captura deleter.png en móvil): el banner
   vivía DENTRO del flujo normal (debajo del topbar, empujando .layout hacia
   abajo) — en pantallas angostas el texto pasa a 2 líneas, ocupa más alto
   de lo que .layout (`height: calc(100vh - 58px)`, solo descuenta el
   topbar) esperaba, y el input de mensaje/controles de abajo quedaban
   recortados fuera de la pantalla. Fix: flotante con position:fixed, mismo
   criterio que #vip-toast (ya probado en esta app) — no empuja nada, solo
   se superpone arriba del contenido; z-index menor al de los modales
   (100) para no taparlos si hay uno abierto. */
.claim-nick-banner {
  position: fixed;
  top: 58px;
  left: 0;
  right: 0;
  z-index: 90;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 1rem;
  background: var(--accent-purple-soft);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border-color);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
  font-size: 0.85rem;
}
.claim-nick-banner span {
  flex: 1;
  min-width: 200px;
  color: var(--text-primary);
}
.claim-nick-banner .btn-primary {
  padding: 0.35rem 0.9rem;
  font-size: 0.82rem;
}

.room-type-toggle {
  display: flex;
  gap: 0.4rem;
}

.room-type-btn {
  flex: 1;
  padding: 0.45rem 0.5rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-color);
  background: var(--bg-surface-2);
  color: var(--text-secondary);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.room-type-btn.active {
  background: var(--accent-purple-soft);
  border-color: var(--accent-select);
  color: var(--text-primary);
}

.room-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.room-list li {
  padding: 0.55rem 0.6rem 0.55rem 0.5rem;
  border-radius: var(--radius-md);
  border-left: 3px solid transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--text-secondary);
  transition: background 0.12s ease, border-color 0.12s ease;
}

.room-list li:hover {
  background: var(--bg-surface-2);
}

/* Barra de acento a la izquierda en la sala activa (2026-07-24, ajuste
   visual pedido) — mismo criterio que la referencia. */
.room-list li.active {
  background: var(--accent-purple-soft);
  border-left-color: var(--accent-purple);
  color: var(--text-primary);
}

.room-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.room-type-icon {
  flex-shrink: 0;
  display: inline-flex;
  color: var(--text-muted);
}

.room-list li small {
  color: var(--text-muted);
  white-space: nowrap;
  font-size: 0.72rem;
  display: flex;
  align-items: center;
  gap: 0.3rem;
}
.room-online-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-success);
  flex-shrink: 0;
}

/* Main room column */
.room {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.room-header {
  /* 2026-07-25, pedido explícito: "el chat puede estar más arribita" —
     recorte vertical chico, el horizontal queda igual. */
  padding: 0.6rem 1.2rem;
  border-bottom: 1px solid var(--border-color);
}

.room-header h2 {
  margin: 0;
  font-size: 1.1rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

#room-title .room-type-icon {
  color: var(--accent-violet);
}

/* Tira compacta de hablantes (Sala de Audio/Video) — visible en TODAS las
   resoluciones (2026-07-21: antes solo en móvil, ahora las pestañas Video/Chat
   son universales, ver .mobile-tabs abajo) para no perderse quién habla
   mientras se está en la pestaña de Chat. Mostrar/ocultar según haya
   hablantes lo controla el atributo HTML "hidden" desde stage.js (regla
   global [hidden]{display:none!important}); acá solo el layout de cuando
   SÍ está visible. */
.mini-stage-strip {
  /* inline-flex (2026-07-25, bug real reportado con captura silla1aq.png):
     con `display:flex` a secas, el contenedor sin ancho propio se estira a
     ocupar TODO el ancho del header — inofensivo mientras no tenía fondo,
     pero ahora que es un fondo degradado visible quedaba una barra violeta
     enorme casi vacía con un solo círculo perdido a la izquierda. inline-flex
     hace que la píldora se ajuste al contenido real (crece con más
     hablantes), max-width evita que se pase del ancho del header. */
  display: inline-flex;
  max-width: 100%;
  align-items: center;
  gap: 0.4rem;
  overflow-x: auto;
  margin-top: 0.5rem;
  /* 2026-07-25, bug real reportado (captura core.jpeg, móvil y desktop): el
     círculo del hablante se veía con la parte de arriba cortada en seco.
     Causa: `overflow-x:auto` sin un `overflow-y` explícito hace que el
     navegador fuerce overflow-y a `auto` también (regla real del spec de
     CSS Overflow — no se puede scrollear en un eje y dejar el otro
     "visible" sin clip). El marco premium de avatar_frame se dibuja más
     grande que el avatar (hasta 1.67x, ver avatar.js) y sobresale arriba/
     abajo a propósito — con el alto de esta fila ajustado al avatar de
     30px nomás, ese sobrante quedaba recortado por el overflow-y forzado.
     Fix: suficiente padding vertical para que el marco más grande (30px *
     1.67 ≈ 50px, ~10px de sobra arriba y abajo) quepa entero dentro de la
     caja, así el overflow-y:auto nunca tiene nada que recortar. */
  padding: 11px 0.6rem;
  /* "Asientos" premium, segunda vuelta (2026-07-25) — el violeta-morado del
     primer intento "no combinaba" (pedido del usuario). Paleta nueva:
     dorado/negro estilo salón VIP (mismo tono que --accent-gold, ya usado
     en la insignia de corona/diamante VIP de esta app), pendiente de
     reemplazar por una silla ilustrada real cuando el usuario la genere. */
  background: linear-gradient(135deg, #0b0805, #241a08);
  border: 1px solid rgba(var(--accent-gold-rgb), 0.45);
  box-shadow: inset 0 0 16px rgba(var(--accent-gold-rgb), 0.12);
  border-radius: 999px;
}

/* Pestañas Video/Chat (2026-07-21: el nombre quedó de cuando esto era solo
   para móvil — ahora se usan en todas las resoluciones, pedido explícito del
   usuario para no repetir en desktop el problema de que el chat quedaba
   apretado en una cajita chica debajo del video). */
.mobile-tabs {
  display: flex;
  border-bottom: 1px solid var(--border-color);
}

.tab-btn {
  flex: 1;
  background: var(--bg-surface);
  color: var(--text-secondary);
  border: none;
  padding: 0.7rem;
  font-weight: 600;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}

.tab-btn.active {
  color: var(--text-primary);
  border-bottom-color: var(--accent-purple);
}

.room-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 1rem 1.2rem;
  /* Área segura inferior (home indicator de iPhone en PWA standalone) —
     el formulario de mensaje es el último elemento de este contenedor, así
     que basta con sumarla acá una sola vez en vez de tocar cada barra
     inferior (mensaje, controles de karaoke, etc.) por separado. */
  padding-bottom: max(1rem, env(safe-area-inset-bottom));
  gap: 1rem;
  overflow-y: auto;
}

/* Video/Chat — solo se muestra el panel activo (2026-07-21: antes ambos
   convivían apilados en desktop, dejando el chat apretado en una cajita
   chica debajo del video; ahora se comportan igual que en móvil en todas
   las resoluciones, un panel a pantalla completa por vez). */
.video-panel,
.chat-panel,
.karaoke-panel {
  display: none;
}

.video-panel.active,
.chat-panel.active,
.karaoke-panel.active {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.video-panel.active {
  gap: 0.6rem;
}

.karaoke-panel.active {
  gap: 0.6rem;
  overflow-y: auto;
  padding-bottom: 1rem;
}

/* Sala de Karaoke: pantalla única, sin pestañas (2026-07-21, pedido
   explícito con capturas de referencia) — .karaoke-merged se agrega en
   .room-body (stage.js, applyRoomTypeTabs) cuando la sala es de tipo
   karaoke. Fuerza los dos paneles a convivir apilados, ignorando la
   exclusividad normal de .active de arriba (que sigue rigiendo para
   Audio/Video, sin tocar). El video/cola/mic quedan arriba con su tamaño
   natural; el chat real (con emoticones, GIFs, regalos, respuestas) ocupa
   el resto, scrolleable, exactamente como en las apps de karaoke de
   referencia. */
.room-body.karaoke-merged {
  overflow-y: auto;
}
.room-body.karaoke-merged .karaoke-panel,
.room-body.karaoke-merged .chat-panel {
  display: flex !important;
  flex-direction: column;
}
.room-body.karaoke-merged .karaoke-panel {
  flex: 0 0 auto;
  gap: 0.6rem;
}
.room-body.karaoke-merged .chat-panel {
  flex: 1;
  min-height: 320px;
}

.video-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 0.6rem;
}

.video-tile {
  position: relative;
  background: #000;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
}

.video-tile video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.video-tile .tile-label {
  position: absolute;
  bottom: 6px;
  left: 8px;
  color: #fff;
  font-size: 0.75rem;
  background: rgba(0, 0, 0, 0.45);
  padding: 0.1rem 0.4rem;
  border-radius: var(--radius-sm);
}

/* Cámara apagada (Sala de Video, Fase 3): avatar circular en vez del <video>
   crudo — informado por el peer remoto vía call:media-state, no algo que
   este cliente pueda inferir del stream por su cuenta. */
.tile-avatar-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-elevated);
}
.tile-avatar-overlay .avatar {
  width: 30% !important;
  height: auto !important;
  aspect-ratio: 1 / 1;
  font-size: 1.4rem !important;
}

/* Anfitrión en grande, resto en una tira chica abajo (Sala de Video, Fase 3).
   Flex-wrap simple (sin contenedor aparte para las tiras chicas): el tile
   del host ocupa el 100% del ancho y fuerza el salto de línea; el resto
   queda en la(s) línea(s) siguientes en fila. */
.video-grid.host-layout {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.6rem;
}
.video-grid.host-layout .video-tile.host-tile {
  width: 100%;
  aspect-ratio: 16 / 9;
}
.video-grid.host-layout .video-tile:not(.host-tile) {
  width: 130px;
  aspect-ratio: 4 / 3;
}

.video-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  text-align: center;
  background: var(--bg-surface);
  border: 1px dashed var(--border-color);
  border-radius: var(--radius-lg);
  padding: 2.5rem 1.5rem;
  color: var(--text-secondary);
}

.video-empty-icon {
  font-size: 2.2rem;
  margin-bottom: 0.3rem;
  opacity: 0.85;
}

.video-empty-state p {
  margin: 0;
  font-weight: 600;
  color: var(--text-primary);
}

.video-empty-state small {
  color: var(--text-muted);
  max-width: 32ch;
}

.call-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.call-controls {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* Sala de Audio (Fase 2) */
.audio-stage {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* "Asientos" premium, segunda vuelta (2026-07-25) — el violeta-morado del
   primer intento "no combinaba" (pedido del usuario). Paleta nueva:
   dorado/negro estilo salón VIP (mismo --accent-gold ya usado en la
   insignia de corona/diamante VIP de esta app), cada hablante en su propia
   tarjeta tipo "asiento" de vidrio. Pendiente: reemplazar por una silla
   ilustrada real (el usuario la va a generar) detrás de cada avatar. */
.audio-stage-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 0.5rem;
  padding: 1rem;
  background: linear-gradient(160deg, #0b0805, #241a08);
  border: 1px solid rgba(var(--accent-gold-rgb), 0.4);
  border-radius: var(--radius-lg);
  min-height: 120px;
}

.stage-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
  text-align: center;
}

/* Silla dorada real (2026-07-25, imagen generada por el usuario,
   silladorado.png -- ya viene con transparencia real tanto afuera como en
   el hueco circular del centro, verificado con Python/Pillow, sin
   reprocesar). Medido a mano: el hueco mide ~266px de diámetro dentro del
   lienzo de 1024px, centrado en x=50.05% / y=39.3% -- de ahí sale la escala
   (avatar 56px * 1024/266 ≈ 216px) y el porcentaje de posición del avatar
   adentro. .stage-seat ES del tamaño final de la silla (no un ancla de
   56px) para no tener que calcular offsets negativos a mano. */
.stage-seat {
  position: relative;
  width: 216px;
  height: 216px;
  flex-shrink: 0;
}
.stage-seat-chair {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.stage-seat .avatar-with-dot {
  position: absolute;
  left: 50.05%;
  top: 39.3%;
  transform: translate(-50%, -50%);
}

.stage-tile .avatar {
  position: relative;
  box-shadow: 0 0 0 3px rgba(var(--accent-gold-rgb), 0.4), 0 0 0 5px transparent;
  transition: box-shadow 0.15s ease;
}

.stage-tile.speaking .avatar {
  box-shadow: 0 0 0 3px rgba(var(--accent-gold-rgb), 0.4), 0 0 0 5px var(--accent-cyan);
}

/* Punto verde/rojo persistente de estado del mic (2026-07-26) — distinto
   del aro ".speaking" de arriba (ese es transitorio, solo mientras hay
   sonido real detectado). avatar-with-dot es un wrapper propio (no lo
   arma renderAvatarHTML) solo para tener un ancla de tamaño exacto sin
   depender del ancho del .stage-tile completo. */
.avatar-with-dot {
  position: relative;
  display: inline-block;
  line-height: 0;
}
.mic-status-dot {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 2px solid var(--bg-surface);
  box-sizing: content-box;
}
.mic-status-dot.mic-dot-on { background: var(--accent-success); }
.mic-status-dot.mic-dot-off { background: var(--accent-danger); }
.mini-avatar .mic-status-dot { width: 10px; height: 10px; }
.video-tile .mic-status-dot { width: 14px; height: 14px; bottom: 8px; right: 8px; }

/* Estado "Activo/Ocupado" del usuario (2026-07-24, pedido explícito) — mismo
   wrapper .avatar-with-dot y mismas medidas que .mic-status-dot de arriba,
   solo cambia qué representa el color (disponibilidad elegida a mano, no
   estado del micrófono). */
.presence-dot {
  display: inline-block;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  box-sizing: content-box;
  flex-shrink: 0;
}
.presence-dot.presence-active { background: var(--accent-success); }
.presence-dot.presence-busy { background: var(--accent-danger); }
/* Sobre el avatar (roster): posicionado en la esquina, con borde para
   separarse del avatar de abajo — mismo criterio que .mic-status-dot. */
.avatar-with-dot .presence-dot {
  position: absolute;
  bottom: 0;
  right: 0;
  border: 2px solid var(--bg-surface);
}
/* Suelto en el picker del modal de perfil: sin posición absoluta, solo el
   puntito de color junto al texto del botón. */
.presence-toggle {
  display: flex;
  gap: 0.6rem;
  margin-bottom: 0.9rem;
}
.presence-toggle-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.5rem 0.7rem;
  background: var(--bg-surface-2);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease;
}
.presence-toggle-btn:hover { background: var(--bg-elevated); }
.presence-toggle-btn.selected {
  border-color: var(--accent-select);
  background: var(--bg-elevated);
}

.stage-tile .stage-tile-name {
  font-size: 0.75rem;
  font-weight: 600;
  /* Blanco fijo (2026-07-25): antes tomaba var(--text-secondary) para el
     fondo oscuro liso de siempre -- sobre el degradado violeta-morado
     nuevo de .audio-stage-grid, ese gris quedaba con poco contraste. */
  color: #fff;
  max-width: 90px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stage-tile .stage-tile-icons {
  display: flex;
  gap: 0.2rem;
  font-size: 0.7rem;
}

.mini-stage-strip .mini-avatar {
  position: relative;
  flex-shrink: 0;
  /* Aro oscuro neutro (2026-07-25) — antes usaba var(--bg-surface), pensado
     para mimetizarse con el fondo oscuro liso de siempre; con el fondo
     dorado/negro nuevo, ese azul marino desentonaba un poco. */
  box-shadow: 0 0 0 2px #100b06, 0 0 0 3px transparent;
  transition: box-shadow 0.15s ease;
}
.mini-stage-strip .mini-avatar.speaking {
  box-shadow: 0 0 0 2px #100b06, 0 0 0 3px var(--accent-cyan);
}

#mini-mic-btn.pending {
  color: var(--accent-gold);
}
#mini-mic-btn.speaking-off {
  color: var(--accent-danger);
}

.audio-audience-count {
  margin: 0;
  font-size: 0.8rem;
  color: var(--text-muted);
  text-align: center;
}

.stage-actions {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.7rem;
}

/* Mismo degradado violeta→morado de la tarjeta "Hazte Premium" (2026-07-26,
   pedido explícito) — para que estos botones se vean igual de vistosos. */
.stage-cta-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: linear-gradient(135deg, var(--accent-violet), var(--accent-purple));
}
.stage-cta-btn:hover { filter: brightness(1.1); }

.pending-hand-note {
  margin: 0;
  font-size: 0.85rem;
  color: var(--text-secondary);
  font-style: italic;
}

.host-pending-hands {
  background: var(--bg-surface-2);
  border-radius: var(--radius-md);
  padding: 0.75rem 1rem;
}

.host-pending-hands h4 {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  color: var(--text-primary);
}

.pending-hands-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.pending-hand-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.pending-hand-row .pending-hand-name {
  flex: 1;
  font-size: 0.85rem;
  color: var(--text-primary);
}

/* Chat — display real (flex/none) lo controla el bloque de arriba junto
   con .video-panel, según cuál pestaña está activa. */
.chat-panel {
  flex: 1;
  flex-direction: column;
  min-height: 200px;
}

.messages {
  flex: 1;
  overflow-y: auto;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 0.9rem;
  margin-bottom: 0.6rem;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.02);
}

.msg {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  margin: 0.85rem 0;
}

.msg-body {
  max-width: 75%;
  min-width: 0;
}

.msg-body strong {
  display: flex;
  align-items: center;
  font-size: 0.85rem;
  margin-bottom: 0.3rem;
}

.msg-body p {
  margin: 0;
  display: inline-block;
  background: var(--bg-surface-2);
  color: var(--text-primary);
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius-md);
  line-height: 1.4;
  word-break: break-word;
}

.message-form {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  border-radius: 999px;
  padding: 0.35rem 0.4rem 0.35rem 0.6rem;
}

.message-form input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  padding: 0.4rem 0.2rem;
}
.message-form input:focus { outline: none; box-shadow: none; }

.message-form .send-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border-radius: 999px;
  padding: 0.5rem 1.1rem;
}

/* Solo para móvil/tablet — en desktop ya está el panel de regalos completo
   al costado (ver responsive.css para el breakpoint que lo muestra). */
.mobile-gift-btn { display: none; }
.mobile-gift-btn svg { color: var(--accent-gold); }

/* Chat enriquecido (Fase 4): menciones, respuestas, reacciones */
.mention {
  background: var(--accent-purple-soft);
  color: var(--accent-purple);
  border-radius: 3px;
  padding: 0 0.2rem;
  font-weight: 600;
}

.msg {
  position: relative;
}
.msg-actions {
  display: flex;
  gap: 0.15rem;
  align-self: flex-start;
  opacity: 0.6;
}
.msg-actions .icon-btn {
  padding: 0.25rem;
  font-size: 0.75rem;
}
.msg-actions:hover {
  opacity: 1;
}

.reply-quote {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  background: var(--bg-elevated);
  border-left: 3px solid var(--accent-purple);
  border-radius: var(--radius-sm);
  padding: 0.3rem 0.5rem;
  margin-bottom: 0.3rem;
  font-size: 0.75rem;
  max-width: fit-content;
}
.reply-quote-name {
  font-weight: 700;
  color: var(--accent-purple);
}
.reply-quote-text {
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 40ch;
}

.reaction-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-top: 0.3rem;
}
.reaction-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  background: var(--bg-elevated);
  border: 1px solid var(--border-color);
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
  font-size: 0.75rem;
  color: var(--text-secondary);
  cursor: pointer;
}
.reaction-pill img {
  width: 14px;
  height: 14px;
}
.reaction-pill.mine {
  background: var(--accent-purple-soft);
  border-color: var(--accent-purple);
  color: var(--accent-purple);
}

.reaction-picker {
  display: flex;
  gap: 0.3rem;
  background: var(--bg-elevated);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 0.35rem;
  margin-top: 0.3rem;
  width: fit-content;
}
.reaction-picker-option {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.2rem;
  border-radius: var(--radius-sm);
}
.reaction-picker-option:hover {
  background: var(--bg-surface-2);
}
.reaction-picker-option img {
  width: 20px;
  height: 20px;
}

.reply-preview {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  background: var(--bg-elevated);
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.6rem;
  margin-bottom: 0.4rem;
  font-size: 0.78rem;
  color: var(--text-secondary);
}
#reply-preview-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Panel de regalos */
.gifts-panel {
  width: var(--gifts-width);
  flex-shrink: 0;
  background: var(--bg-surface);
  border-left: 1px solid var(--border-color);
  padding: 1rem;
  overflow-y: auto;
}

.gifts-title {
  margin: 0 0 0.2rem;
  font-size: 1rem;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: 0.45rem;
}
.gifts-title svg { color: var(--accent-gold); flex-shrink: 0; }


.gift-grid {
  display: grid;
  /* auto-fill/minmax (mismo criterio que .emoticon-grid/.profile-badge-grid
     abajo) en vez de 3 columnas fijas — 44px es el mínimo táctil pedido
     explícitamente. */
  grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));
  gap: 0.5rem;
}

.gift-option {
  position: relative;
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 0.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
}
.gift-option:hover { background: var(--bg-elevated); }
.gift-option img { width: 100%; height: 100%; object-fit: contain; }
/* Seleccionar-y-confirmar (2026-07-26) — el regalo elegido se resalta acá,
   el envío real pasa por .gift-send-btn de abajo. */
.gift-option.selected {
  background: var(--accent-purple-soft);
  border-color: var(--accent-purple);
  box-shadow: 0 0 0 1px var(--accent-purple);
}
.gift-send-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  margin-top: 1rem;
  border: none;
  border-radius: var(--radius-md);
  padding: 0.75rem 0.9rem;
  background: linear-gradient(135deg, var(--accent-violet), var(--accent-purple));
  color: #fff;
  font-weight: 700;
  font-size: 0.92rem;
  cursor: pointer;
  transition: filter 0.15s ease, opacity 0.15s ease;
}
.gift-send-btn:hover:not(:disabled) { filter: brightness(1.1); }
.gift-send-btn:disabled {
  opacity: 0.45;
  cursor: default;
}
.gift-price {
  position: absolute;
  left: 50%;
  bottom: -0.5rem;
  transform: translateX(-50%);
  background: var(--accent-gold);
  color: #241a00;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1;
  padding: 0.18rem 0.4rem;
  border-radius: 999px;
  white-space: nowrap;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}

/* Comprar monedas (Fase C, 2026-07-24) */
.coin-store-modal { max-width: 480px; }
.coin-store-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 0.75rem;
}
.coin-package-card {
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 1rem 0.8rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}
.coin-package-amount {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--accent-gold);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
}
.coin-package-amount span {
  font-size: 0.7rem;
  font-weight: 500;
  color: var(--text-muted);
}
.coin-package-price {
  color: var(--text-secondary);
  font-size: 0.9rem;
}
.coin-package-buy-btn {
  width: 100%;
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.8rem;
  background: var(--accent-purple);
  color: #fff;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease;
}
.coin-package-buy-btn:hover { background: var(--accent-purple-hover); }
.coin-package-buy-btn:disabled { opacity: 0.6; cursor: default; }
.coin-store-error {
  background: rgba(var(--accent-danger-rgb), 0.15);
  color: var(--accent-danger);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.7rem;
  font-size: 0.82rem;
  margin: 0 0 0.8rem;
}

/* Header con botón "volver" opcional (coin-store-back-btn) — el título
   crece para ocupar el espacio libre en vez de quedar pegado al botón. */
.coin-store-modal .modal-header h3 { flex: 1; }

/* Formulario de pago embebido con Stripe Elements (Fase C/D, 2026-07-26) */
.payment-embed-summary {
  text-align: center;
  color: var(--text-secondary);
  font-size: 0.92rem;
  font-weight: 600;
  margin: 0 0 1rem;
}
.payment-embed-view { display: flex; flex-direction: column; gap: 1rem; }
.payment-embed-pay-btn {
  width: 100%;
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.7rem 0.8rem;
  background: var(--accent-purple);
  color: #fff;
  font-weight: 700;
  font-size: 0.95rem;
  cursor: pointer;
  transition: background 0.15s ease;
}
.payment-embed-pay-btn:hover { background: var(--accent-purple-hover); }
.payment-embed-pay-btn:disabled { opacity: 0.6; cursor: default; }

.premium-benefits-list {
  margin: 0 0 1rem;
  padding: 0 0 0 1.2rem;
  color: var(--text-secondary);
  font-size: 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

/* Fila horizontal tipo "log" para regalo enviado (2026-07-24, ajuste
   visual pedido) — icono + texto en línea, en vez de columna centrada;
   la franja resaltada la da .msg:has(.gift-message) sobre toda la fila. */
.gift-message {
  display: inline-flex !important;
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
  background: transparent !important;
  padding: 0 !important;
  font-size: 0.85rem;
}
.gift-message-img { width: 30px; height: 30px; object-fit: contain; }
.msg:has(.gift-message) {
  background: var(--bg-surface-2);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.7rem;
}

/* Entrada más notoria (2026-07-22, pedido explícito) — más larga, con más
   rebote y un poco de caída/giro de más, en vez del pop simple de antes. */
@keyframes gift-pop {
  0%   { transform: scale(0) rotate(-30deg) translateY(24px); opacity: 0; }
  35%  { transform: scale(1.45) rotate(12deg) translateY(-10px); opacity: 1; }
  55%  { transform: scale(0.85) rotate(-8deg) translateY(6px); }
  72%  { transform: scale(1.15) rotate(5deg) translateY(-4px); }
  86%  { transform: scale(0.96) rotate(-2deg) translateY(1px); }
  100% { transform: scale(1) rotate(0) translateY(0); }
}
.gift-pop {
  animation: gift-pop 1s cubic-bezier(.25, 1.5, .5, 1) both;
}

@keyframes gift-glow {
  0%, 100% { filter: drop-shadow(0 0 2px rgba(var(--accent-gold-rgb), 0.4)); }
  50% { filter: drop-shadow(0 0 12px rgba(var(--accent-gold-rgb), 0.9)); }
}
.gift-pop-special {
  animation: gift-pop 1s cubic-bezier(.25, 1.5, .5, 1) both, gift-glow 1.4s ease-in-out 1s 2;
}

.chat-emoticon {
  width: 20px;
  height: 20px;
  vertical-align: middle;
  object-fit: contain;
}

/* Selector de emoticones — dos pestañas: estáticos y animados */
.emoticon-picker {
  margin-bottom: 0.5rem;
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.emoticon-tabs {
  display: flex;
  border-bottom: 1px solid var(--border-color);
}
.emoticon-tab {
  flex: 1;
  background: none;
  border: none;
  padding: 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
}
.emoticon-tab.active {
  color: var(--text-primary);
  background: var(--bg-surface-2);
}
.emoticon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));
  gap: 0.3rem;
  max-height: 160px;
  overflow-y: auto;
  padding: 0.6rem;
}
.emoticon-option {
  background: none;
  border: none;
  padding: 0.2rem;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
}
.emoticon-option:hover { background: var(--bg-surface-2); }
.emoticon-option img { width: 24px; height: 24px; object-fit: contain; }

.sidebar-backdrop {
  display: none;
}

/* Insignia/mascota junto al nombre, dentro de mensajes y roster */
.name-badge {
  width: 18px;
  height: 18px;
  object-fit: contain;
  vertical-align: middle;
  margin-left: 0.25rem;
}

/* Diamante VIP (2026-07-22) — ícono de rango ANTES del nombre, en mensajes,
   roster y perfil. */
.vip-badge {
  width: 26px;
  height: 26px;
  object-fit: contain;
  vertical-align: middle;
  margin-right: 0.3rem;
}

/* Insignia VIP/Premium propia del usuario (2026-07-26) — vip-normal.png es
   una píldora ancha (relación 3:1), vip-premier.png un medallón cuadrado;
   por eso cada una tiene su propia altura en vez de compartir un tamaño. */
.tier-badge {
  vertical-align: middle;
  object-fit: contain;
  margin-left: 0.3rem;
}
.tier-badge-vip { height: 22px; width: auto; }
.tier-badge-premium { height: 28px; width: auto; }

/* Aviso flotante de recompensa VIP (2026-07-22) — independiente de cualquier
   sala/panel porque puede dispararse antes de unirse a ninguna (justo al
   completar la pantalla de entrada). z-index por encima de los modales
   (200) para que se vea aunque justo se esté cerrando uno. */
#vip-toast {
  position: fixed;
  top: 1rem;
  left: 50%;
  transform: translate(-50%, -140%);
  z-index: 300;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: linear-gradient(135deg, var(--bg-elevated), var(--bg-page));
  border: 1px solid rgba(var(--accent-violet-rgb), 0.4);
  border-radius: 999px;
  padding: 0.5rem 1rem 0.5rem 0.5rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4), 0 0 24px rgba(var(--accent-violet-rgb), 0.18);
  color: var(--text-on-accent);
  font-size: 0.85rem;
  max-width: 90vw;
  opacity: 0;
  transition: transform 0.4s ease, opacity 0.4s ease;
  pointer-events: none;
}
#vip-toast.vip-toast-show {
  transform: translate(-50%, 0);
  opacity: 1;
}
#vip-toast img {
  width: 34px;
  height: 34px;
  object-fit: contain;
  filter: drop-shadow(0 0 6px rgba(var(--accent-violet-rgb), 0.6));
}

/* Mensajito de monedas ganadas/gastadas (2026-07-26, pedido explícito) —
   pila en la esquina inferior derecha (no choca con #vip-toast, que vive
   arriba centrado), más de uno se apila con column-reverse en vez de
   reemplazarse. */
#coin-toast-stack {
  position: fixed;
  bottom: 1.2rem;
  right: 1.2rem;
  z-index: 300;
  display: flex;
  flex-direction: column-reverse;
  gap: 0.5rem;
  pointer-events: none;
  max-width: min(320px, calc(100vw - 2.4rem));
}
.coin-toast {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: linear-gradient(135deg, var(--bg-elevated), var(--bg-page));
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 0.6rem 0.9rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  color: var(--text-primary);
  font-size: 0.82rem;
  opacity: 0;
  transform: translateY(8px);
  transition: transform 0.3s ease, opacity 0.3s ease;
}
.coin-toast.coin-toast-show { opacity: 1; transform: translateY(0); }
.coin-toast-amount {
  flex-shrink: 0;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.coin-toast-spend { border-color: rgba(var(--accent-danger-rgb), 0.35); }
.coin-toast-spend .coin-toast-amount { color: var(--accent-danger); }
.coin-toast-credit { border-color: rgba(var(--accent-success-rgb), 0.4); }
.coin-toast-credit .coin-toast-amount { color: var(--accent-success); }
.coin-toast-warning {
  border-color: rgba(var(--accent-gold-rgb), 0.45);
  background: linear-gradient(135deg, rgba(var(--accent-gold-rgb), 0.12), var(--bg-elevated));
}

/* Efectos animados del nick (2026-07-21) — todos usan currentColor (el
   mismo color que ya eligió el usuario), nunca un color aparte. 'neon' y
   'sway' animan el <span> completo; 'wave'/'run' animan letra por letra
   (cada letra es su propio <span> con animation-delay creciente, armado en
   avatar.js) para que se vea la ola/carrera en cascada. */
.decorated-name.name-effect-neon {
  font-weight: 700;
  animation: name-neon-pulse 1.8s ease-in-out infinite;
}

@keyframes name-neon-pulse {
  0%, 100% {
    text-shadow: 0 0 4px currentColor, 0 0 8px currentColor;
  }
  50% {
    text-shadow: 0 0 8px currentColor, 0 0 18px currentColor, 0 0 28px currentColor;
  }
}

.decorated-name.name-effect-sway {
  display: inline-block;
  transform-origin: center bottom;
  animation: name-effect-sway-kf 1.1s ease-in-out infinite;
}

@keyframes name-effect-sway-kf {
  0%, 100% { transform: translateX(-3px) rotate(-3deg); }
  50% { transform: translateX(3px) rotate(3deg); }
}

.decorated-name.name-effect-wave span {
  display: inline-block;
  animation: name-effect-wave-kf 1s ease-in-out infinite;
}

@keyframes name-effect-wave-kf {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-5px); }
}

.decorated-name.name-effect-run span {
  display: inline-block;
  animation: name-effect-run-kf 0.5s ease-in-out infinite;
}

@keyframes name-effect-run-kf {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  25% { transform: translateY(-3px) rotate(-6deg); }
  75% { transform: translateY(2px) rotate(6deg); }
}

/* 'mariposa' (2026-07-25, pedido explícito, imagen propia lamari.png) — a
   diferencia de los otros 4 efectos (que animan el texto), este agrega una
   imagen real revoloteando arriba del nick. El wrap es el ancla de
   posicionamiento; el nombre en sí no se mueve. */
.name-mariposa-wrap {
  position: relative;
  display: inline-block;
}
.name-mariposa-icon {
  position: absolute;
  /* 2026-07-25, pedido explícito: "se mira muy chido, pero debe verse más
     grande" — subida de 18px a 30px. */
  width: 30px;
  height: 30px;
  top: -22px;
  left: 50%;
  pointer-events: none;
  animation: name-mariposa-fly 3.2s ease-in-out infinite;
}
@keyframes name-mariposa-fly {
  0%   { transform: translate(-90%, 0) rotate(-6deg); }
  25%  { transform: translate(-40%, -6px) rotate(8deg); }
  50%  { transform: translate(10%, 0) rotate(-4deg); }
  75%  { transform: translate(-40%, -4px) rotate(6deg); }
  100% { transform: translate(-90%, 0) rotate(-6deg); }
}
@media (prefers-reduced-motion: reduce) {
  .name-mariposa-icon { animation: none; }
}

/* Fondito de color del nombre, tipo "pill" (2026-07-21) — combinable con
   cualquier name-effect-* al mismo tiempo (son clases distintas, no se
   pisan). display:inline-block ya lo ponen wave/run/sway si están activos
   a la vez; si el fondito va solo (sin efecto), esta regla se lo da. */
.decorated-name.name-has-bg {
  display: inline-block;
  padding: 0.05rem 0.55rem;
  border-radius: 999px;
  font-weight: 700;
}

.profile-effect-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
  gap: 0.4rem;
  margin-bottom: 1rem;
}

.effect-option {
  aspect-ratio: unset;
  padding: 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
}

.effect-option.selected {
  background: var(--accent-purple-soft);
  border-color: var(--accent-select);
  color: var(--accent-purple);
}

/* Roster (quién está en la sala) */
.roster-section {
  margin-top: 1.2rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border-color);
}

/* Tarjeta "Hazte Premium" (2026-07-24) — visual, apunta al diamante VIP
   que ya existe en el perfil; no es un pago nuevo. */
.vip-sidebar-card {
  margin-top: 1.2rem;
  padding: 1rem;
  border-radius: var(--radius-md);
  background: linear-gradient(160deg, var(--accent-violet-soft), var(--accent-purple-soft));
  border: 1px solid rgba(var(--accent-violet-rgb), 0.35);
  text-align: center;
}
.vip-sidebar-icon {
  color: var(--accent-violet);
  margin-bottom: 0.4rem;
}
.vip-sidebar-title {
  margin: 0 0 0.25rem;
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--text-primary);
}
.vip-sidebar-hint {
  margin: 0 0 0.8rem;
  font-size: 0.76rem;
  color: var(--text-secondary);
  line-height: 1.4;
}
.vip-sidebar-btn {
  width: 100%;
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.55rem 0.8rem;
  background: linear-gradient(135deg, var(--accent-violet), var(--accent-purple));
  color: #fff;
  font-weight: 600;
  font-size: 0.82rem;
  cursor: pointer;
  transition: filter 0.15s ease;
}
.vip-sidebar-btn:hover { filter: brightness(1.1); }

/* Tienda de efectos/marcos (2026-07-24) */
.store-sidebar-btn {
  margin-top: 0.7rem;
  width: 100%;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: 0.55rem 0.8rem;
  background: var(--bg-surface-2);
  color: var(--text-primary);
  font-weight: 600;
  font-size: 0.82rem;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.store-sidebar-btn:hover { background: var(--bg-elevated); border-color: var(--accent-select); }

.store-modal { max-width: 640px; }
.store-tabs {
  display: flex;
  gap: 0.4rem;
  padding: 0 1.2rem;
  border-bottom: 1px solid var(--border-color);
  overflow-x: auto;
}
.store-tab-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 0.7rem 0.5rem;
  color: var(--text-secondary);
  font-weight: 600;
  font-size: 0.82rem;
  cursor: pointer;
  white-space: nowrap;
}
.store-tab-btn:hover { color: var(--text-primary); }
.store-tab-btn.active { color: var(--accent-select); border-bottom-color: var(--accent-select); }

.store-hint {
  font-size: 0.78rem;
  color: var(--text-secondary);
  margin: 0 0 0.8rem;
}

.store-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 0.7rem;
}
.store-item-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: 0.6rem;
  border-radius: var(--radius-md);
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  text-align: center;
}
.store-item-card img {
  width: 64px;
  height: 64px;
  object-fit: contain;
}
.store-item-name {
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.2;
}
.store-item-tier {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
}
.store-item-tier.tier-basico { background: var(--bg-elevated); color: var(--text-secondary); }
.store-item-tier.tier-intermedio { background: rgba(var(--accent-cyan-rgb), 0.18); color: var(--accent-cyan); }
.store-item-tier.tier-alto { background: rgba(var(--accent-violet-rgb), 0.2); color: var(--accent-violet); }
.store-item-tier.tier-legendario { background: linear-gradient(135deg, var(--accent-violet), var(--accent-purple)); color: #fff; }
.store-item-price {
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--text-primary);
}
.store-item-btn {
  width: 100%;
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.5rem;
  background: var(--accent-select);
  color: #fff;
  font-weight: 600;
  font-size: 0.76rem;
  cursor: pointer;
}
.store-item-btn:hover { filter: brightness(1.1); }
.store-item-btn.equipped { background: var(--accent-success); }
.store-item-btn.unequip-btn { background: var(--bg-elevated); color: var(--text-primary); border: 1px solid var(--border-color); }
.store-item-expiry {
  font-size: 0.66rem;
  color: var(--text-secondary);
}
.store-item-expiry.expiring-soon { color: var(--accent-danger); font-weight: 600; }
.store-empty {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--text-secondary);
  font-size: 0.82rem;
  padding: 1.5rem 0;
}
.roster-heading {
  margin: 0 0 0.6rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.roster-group-label {
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  margin: 0.7rem 0 0.35rem;
}
.roster-group-label.owner { color: var(--accent-gold); }
.roster-row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.45rem 0.2rem;
}
.roster-row-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  line-height: 1.25;
}
.roster-kick-btn {
  flex-shrink: 0;
  padding: 0.2rem;
  font-size: 0.75rem;
  opacity: 0.6;
}
.roster-kick-btn:hover {
  opacity: 1;
}
.roster-row-name {
  font-size: 0.85rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.roster-row-status {
  color: var(--text-muted);
  font-size: 0.72rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Modal de perfil */
.modal-backdrop {
  position: fixed;
  inset: 0;
  /* Insurance explícita además de position:fixed+inset:0: en iOS Safari
     viejo, un elemento fixed a veces NO se achica cuando aparece el
     teclado (se queda del tamaño de antes), tapando el campo/botón. min-
     height:100dvh sí seguía el viewport visual real. 100vh como fallback
     en navegadores sin soporte de dvh. */
  min-height: 100vh;
  min-height: 100dvh;
  background: rgba(20, 30, 50, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  /* max() con env(safe-area-inset-*): en un iPhone con notch/isla dinámica
     en modo PWA standalone, garantiza que el modal nunca quede recortado
     por debajo de esas zonas; en cualquier otro dispositivo env() da 0 y
     se queda igual que antes (1rem). */
  padding: max(1rem, env(safe-area-inset-top)) max(1rem, env(safe-area-inset-right)) max(1rem, env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left));
}
.modal {
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  width: 100%;
  /* min-width:0 — bug real encontrado y corregido (2026-07-24): .modal es
     hijo de un flex row (.modal-backdrop) y por default un flex item tiene
     min-width:auto, o sea que el navegador nunca lo encoge por debajo del
     ancho mínimo de su contenido (ej. la fila de "Iniciar sesión"/"Crear
     una cuenta"). Eso hacía que la tarjeta se desbordara del viewport en
     cualquier pantalla angosta en vez de respetar max-width — confirmado
     con capturas reales a 320/390px antes del fix. */
  min-width: 0;
  max-width: 380px;
  max-height: 85vh;
  max-height: 85dvh;
  overflow-y: auto;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
}
/* Tarjeta de entrada — escopeada, no toca el resto de modales de la app
   (perfil, mensajes privados, wall, etc.). Radio/ancho/fondo propios según
   la integración visual pedida: fondo de tarjeta #162542 (bg-surface-2, el
   token correcto de "tarjetas y modales" de Aurora Premium — .modal base
   usa bg-surface por herencia histórica, no se toca ahí para no afectar
   los demás modales), borde discreto, radio 20px, ancho 420-460px. */
.entry-modal-backdrop .modal {
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  border-radius: 20px;
  max-width: 440px;
  /* Bug real corregido (2026-07-24): la tarjeta de entrada heredaba el
     max-height:85vh/85dvh + overflow-y:auto de .modal base (pensado para
     modales chicos como "perfil"/"registro") — con Google + "olvidaste tu
     contraseña" sumados, la tarjeta ya no entra en ventanas bajas y
     mostraba scrollbar PROPIA en vez de dejar crecer la página. Achica
     max-height a "sin límite" para esta tarjeta específica (no afecta los
     demás modales, ver comentario de arriba) — quien sí se encarga de
     scrollear si hace falta es .entry-modal-backdrop (regla de abajo). */
  max-height: none;
  overflow-y: visible;
}
/* Compactar un poco más la parte superior (ajuste fino 2026-07-24) — el
   cuerpo (campo de nombre) queda pegado más cerca del header ahora que
   ambos perdieron aire de sobra. */
.entry-modal-backdrop .modal-body {
  padding-top: 0.5rem;
}
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.2rem;
  border-bottom: 1px solid var(--border-color);
}
.modal-header h3 { margin: 0; font-size: 1.05rem; color: var(--text-primary); }
.modal-body { padding: 1rem 1.2rem; }
.modal-footer {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
  padding: 1rem 1.2rem;
  border-top: 1px solid var(--border-color);
}
/* Mismo degradado violeta→morado de "Hazte Premium" (2026-07-26, pedido
   explícito) — Cerrar sesión queda aparte, en rojo, porque es destructiva. */
.profile-footer-premium-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: linear-gradient(135deg, var(--accent-violet), var(--accent-purple));
  border-color: transparent;
  color: #fff;
}
.profile-footer-premium-btn:hover { filter: brightness(1.1); background: linear-gradient(135deg, var(--accent-violet), var(--accent-purple)); }
.modal-label {
  display: block;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-secondary);
  margin: 0.9rem 0 0.5rem;
}
.modal-label:first-child { margin-top: 0; }

/* Ícono discreto dentro del campo de nombre (ajuste fino 2026-07-24) — solo
   posición vía CSS, el <input> real no cambia de id/comportamiento. */
.entry-input-wrap {
  position: relative;
}
.entry-input-wrap .entry-input-icon {
  position: absolute;
  left: 0.8rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--accent-cyan);
  pointer-events: none;
}
#profile-status-input {
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 0.9rem;
}

#entry-name-input {
  width: 100%;
  padding: 0.6rem 0.75rem;
  /* Bug real corregido (2026-07-24): esta regla por ID ya existía de antes
     y le ganaba en especificidad a ".entry-input-wrap input" (una clase
     nunca le gana a un ID) — el padding-left del ícono nunca se aplicaba
     de verdad, y el texto se dibujaba encima del ícono. El fix va acá,
     donde realmente se resuelve la cascada. */
  padding-left: 2.6rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 0.95rem;
  box-sizing: border-box;
}

.entry-modal-backdrop {
  z-index: 200;
  /* Luz radial azul+violeta muy sutil detrás de la tarjeta (ajuste fino
     2026-07-24) — reemplaza el overlay plano genérico de .modal-backdrop
     porque esta pantalla es la primera vista, no un modal "sobre" otro
     contenido. En móvil se aplana a fondo sólido (ver responsive.css) por
     rendimiento, igual criterio que ya se usa en el fondo de <body>. */
  background:
    radial-gradient(ellipse 70% 45% at 50% 42%, rgba(var(--accent-purple-rgb), 0.16), transparent 70%),
    radial-gradient(ellipse 60% 45% at 50% 58%, rgba(var(--accent-violet-rgb), 0.12), transparent 70%),
    var(--bg-page);
  /* Bug real corregido (2026-07-24) — "Iniciar sesión" (y ahora también
     Google + "olvidaste tu contraseña") hacen que la tarjeta pueda superar
     la altura de ventanas bajas. En vez de una scrollbar DENTRO de la
     tarjeta (fea, recorta el borde redondeado), se deja scrollear este
     fondo completo — se ve como "se desplaza la página", tal como se pidió.
     flex-direction:column + los dos pseudo-elementos de abajo (flex:1,
     como resortes) centran la tarjeta cuando sobra espacio y la empujan
     arriba del todo (scrolleable) cuando no alcanza — evita el error
     clásico de "align-items:center recorta el borde de arriba" que tiene
     un center + overflow:auto normal. */
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  overflow-y: auto;
}
.entry-modal-backdrop::before,
.entry-modal-backdrop::after {
  content: '';
  flex: 1 0 20px;
}

/* Destellos a los lados de la tarjeta (2026-07-24, pedido explícito) — dos
   rayas verticales con los mismos 3 colores del logo/burbujas (azul,
   turquesa, violeta), con un movimiento muy sutil, no una animación
   pesada. Solo en escritorio (min-width:700px, mismo criterio ya usado en
   el resto de esta pantalla): en móvil la tarjeta no deja espacio real a
   los lados y no vale la pena el costo. prefers-reduced-motion ya las
   congela solas (regla global en responsive.css). */
.entry-glow-streak {
  display: none;
  position: absolute;
  top: 50%;
  width: 3px;
  height: 55%;
  border-radius: 999px;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    transparent 0%,
    rgba(var(--accent-purple-rgb), 0.55) 25%,
    rgba(var(--accent-cyan-rgb), 0.65) 50%,
    rgba(var(--accent-violet-rgb), 0.55) 75%,
    transparent 100%
  );
  filter: blur(2px);
  animation: entry-glow-drift 7s ease-in-out infinite;
}
@media (min-width: 700px) {
  .entry-glow-streak {
    display: block;
  }
}
.entry-glow-streak-left {
  left: calc(50% - 260px);
}
.entry-glow-streak-right {
  right: calc(50% - 260px);
  animation-delay: -3.5s;
}
@keyframes entry-glow-drift {
  0%, 100% { opacity: 0.45; transform: translateY(-50%) scaleY(1); }
  50% { opacity: 0.9; transform: translateY(-53%) scaleY(1.08); }
}

/* "Crear una cuenta" desde la pantalla de entrada abre este modal (ya
   existente, sin tocar su lógica) por encima — si no, quedaría tapado
   detrás del backdrop de entrada (z-index 200 > 100 de un modal normal). */
#claim-nick-modal-backdrop {
  z-index: 210;
}

/* Botón principal de la pantalla de entrada — ancho completo, mobile-first
   (regla explícita: "el botón principal debe ocupar todo el ancho
   disponible"). Escopeado a este modal, no a .btn-primary en general, para
   no forzar ancho completo en modales con varios botones en fila. */
.entry-modal-backdrop .btn-primary {
  width: 100%;
  /* Degradado sutil reservado al botón destacado de la entrada (regla ya
     establecida en el tema Aurora Premium: "no uses este degradado en
     todos los botones") + sombra azul/violeta suave a juego. */
  background: linear-gradient(135deg, var(--accent-purple), var(--accent-violet));
  box-shadow: 0 10px 24px rgba(var(--accent-purple-rgb), 0.25), 0 10px 24px rgba(var(--accent-violet-rgb), 0.18);
}
.entry-modal-backdrop .btn-primary:hover {
  background: linear-gradient(135deg, var(--accent-purple-hover), var(--accent-violet));
  filter: brightness(1.08);
}
.entry-modal-backdrop .btn-primary:active {
  background: linear-gradient(135deg, var(--accent-purple-active), var(--accent-violet));
  filter: none;
}

.profile-color-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.color-swatch {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
}
.color-swatch.selected {
  border-color: var(--text-primary);
  box-shadow: 0 0 0 2px var(--bg-surface);
}

/* Fondito de color del nombre (2026-07-21) — mismo tamaño/forma que
   .color-swatch, texto "✕" para la opción "Ninguno". */
.bg-swatch {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  background: var(--bg-surface-2);
  color: var(--text-secondary);
  font-size: 0.8rem;
}
.bg-swatch.selected {
  border-color: var(--text-primary);
  box-shadow: 0 0 0 2px var(--bg-surface);
}

.profile-badge-grid {
  display: grid;
  /* auto-fill/minmax en vez de 8 columnas fijas (bug real reportado en
     celulares angostos: con un modal de ~280px de ancho de contenido, 8
     columnas fijas dejaban cada celda diminuta) — así se acomoda solo según
     el ancho real disponible, igual que .emoticon-grid. */
  grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));
  gap: 0.35rem;
  max-height: 160px;
  overflow-y: auto;
}
.badge-option {
  position: relative;
  background: var(--bg-surface-2);
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  padding: 0.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--text-muted);
}
.badge-option img { width: 100%; height: 100%; object-fit: contain; }
.badge-option.selected { border-color: var(--accent-select); }

/* Aros/insignias exclusivos de Premium (2026-07-26) — bloqueados
   visualmente en vez de ocultos, para que se note que existen y tienten a
   suscribirse. */
.badge-option.locked img { opacity: 0.35; filter: grayscale(0.6); }
.locked-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
}
.profile-locked-hint {
  margin: 0 0 0.5rem;
  font-size: 0.78rem;
  color: var(--text-muted);
  background: var(--accent-violet-soft);
  border: 1px solid rgba(var(--accent-violet-rgb), 0.3);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.7rem;
}
.profile-locked-link {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent-violet);
  font-weight: 700;
  font-size: 0.78rem;
  cursor: pointer;
  text-decoration: underline;
}

/* Mensajes privados + ver perfil (2026-07-21) */
.pm-inbox-btn {
  position: relative;
}
.pm-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  background: var(--accent-danger);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  border-radius: 999px;
  padding: 0.05rem 0.35rem;
  min-width: 16px;
  line-height: 1.3;
}

/* Saldo de monedas en la barra superior (2026-07-24) — pastilla dorada,
   mismo estilo que la referencia visual. El número real y la lógica de
   compra llegan en la Fase B/C (por ahora el elemento existe hidden en el
   HTML hasta que haya un saldo real que mostrar). */
.coin-balance-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  border-radius: 999px;
  padding: 0.35rem 0.5rem 0.35rem 0.7rem;
  color: var(--accent-gold);
  font-weight: 700;
  font-size: 0.85rem;
}
.coin-balance-pill svg { color: var(--accent-gold); flex-shrink: 0; }
#coin-balance-amount { color: var(--text-primary); font-variant-numeric: tabular-nums; }
.coin-buy-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  background: var(--accent-purple);
  color: #fff;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background 0.18s ease;
}
.coin-buy-btn:hover { background: var(--accent-purple-hover); }

.roster-row-clickable {
  cursor: pointer;
}
.roster-row-clickable:hover {
  background: var(--bg-elevated);
  border-radius: var(--radius-sm);
}

.user-action-modal {
  max-width: 320px;
}
.user-action-buttons {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.user-action-buttons .icon-btn {
  width: 100%;
  text-align: left;
  padding: 0.7rem 0.9rem;
}

.view-profile-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.4rem;
  padding: 1rem 0;
}
.view-profile-avatar .avatar {
  margin: 0 auto 0.5rem;
}
.view-profile-name {
  font-size: 1.1rem;
  font-weight: 700;
}
.view-profile-status {
  color: var(--text-secondary);
  font-size: 0.9rem;
}
.view-profile-meta {
  color: var(--text-muted);
  font-size: 0.8rem;
}
/* Solo la ve el admin (2026-07-23) — el servidor ya filtra esto, ver
   privateMessageHandlers.js profile:view. */
.view-profile-ip {
  font-family: monospace;
  color: var(--accent-danger);
}

/* Botón de amistad del modal "Perfil" (2026-07-25) — un solo botón que
   cambia de color/texto según el estado (ver renderFriendButton en
   privateMessages.js), mismo criterio de "una clase por estado" que ya usan
   los estilos de tarjeta de perfil (profileStyle). */
.friend-action-btn.friend-btn-outgoing {
  color: var(--text-secondary);
}
.friend-action-btn.friend-btn-incoming {
  background: linear-gradient(135deg, var(--accent-violet), var(--accent-purple));
  border-color: transparent;
  color: #fff;
}
.friend-action-btn.friend-btn-incoming:hover {
  filter: brightness(1.1);
}
.friend-action-btn.friend-btn-friends {
  color: var(--accent-success);
  border-color: var(--accent-success);
}

.pm-modal {
  max-width: 420px;
  height: 70vh;
  max-height: 560px;
  display: flex;
  flex-direction: column;
}
.pm-modal .modal-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 0;
}
.pm-modal-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.pm-conversations-list {
  display: flex;
  flex-direction: column;
}
.pm-conversation-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1rem;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border-color);
  text-align: left;
  cursor: pointer;
  color: var(--text-primary);
}
.pm-conversation-row:hover {
  background: var(--bg-elevated);
}
.pm-conversation-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.pm-conversation-name {
  font-weight: 600;
  font-size: 0.9rem;
}
.pm-conversation-preview {
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pm-conversation-unread {
  background: var(--accent-danger);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  border-radius: 999px;
  padding: 0.1rem 0.45rem;
}
.pm-empty {
  padding: 1.5rem 1rem;
  color: var(--text-muted);
  text-align: center;
  font-size: 0.85rem;
}

.pm-thread-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.pm-thread-messages {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0.8rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.pm-thread-msg {
  max-width: 80%;
  background: var(--bg-surface-2);
  border-radius: var(--radius-md);
  padding: 0.4rem 0.65rem;
}
.pm-thread-msg.mine {
  align-self: flex-end;
  background: var(--accent-purple-soft);
}
.pm-thread-msg p {
  margin: 0.15rem 0 0;
  font-size: 0.9rem;
}
.pm-modal-footer {
  display: flex;
  gap: 0.5rem;
}
#pm-thread-input {
  flex: 1;
}

/* QuizBot (2026-07-21) — ranking de puntos y panel de configuración del
   admin. Las preguntas del bot en sí no tienen CSS propio: llegan como
   mensajes de chat normales y usan el mismo .msg de siempre. */
.quiz-leaderboard-list {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.quiz-leaderboard-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.2rem;
  border-bottom: 1px solid var(--border-color);
}
.quiz-leaderboard-rank {
  font-weight: 700;
  color: var(--text-muted);
  width: 2rem;
  flex-shrink: 0;
}
.quiz-leaderboard-name {
  flex: 1;
  font-weight: 600;
  font-size: 0.9rem;
}
.quiz-leaderboard-points {
  color: var(--accent-purple);
  font-weight: 700;
  font-size: 0.85rem;
}
.quiz-leaderboard-empty {
  padding: 1.5rem 1rem;
  color: var(--text-muted);
  text-align: center;
  font-size: 0.85rem;
}
#quiz-admin-backdrop select,
#quiz-admin-backdrop input[type='number'] {
  width: 100%;
  margin-bottom: 0.8rem;
}

/* Panel de usuarios en línea (2026-07-21, otro.txt punto 2 / boomx2.png) */
/* Muro de anuncios + peticiones de oración (2026-07-24) — un solo panel con
   dos pestañas, mismo criterio flex-column de .pm-modal para que el feed
   scrollee y el compose quede siempre visible abajo. */
.wall-modal {
  max-width: 480px;
  height: 78vh;
  max-height: 640px;
  display: flex;
  flex-direction: column;
}
.wall-tabs {
  display: flex;
  border-bottom: 1px solid var(--border-color);
  flex-shrink: 0;
}
.wall-tab-btn {
  flex: 1;
  background: none;
  border: none;
  padding: 0.7rem 0.5rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  border-bottom: 2px solid transparent;
}
.wall-tab-btn.active {
  color: var(--accent-purple);
  border-bottom-color: var(--accent-purple);
}
.wall-modal .modal-body,
.wall-body {
  flex: 1;
  min-height: 0;
  padding: 0;
  overflow: hidden;
}
.wall-feed {
  height: 100%;
  overflow-y: auto;
  padding: 0.8rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}
.wall-empty {
  text-align: center;
  color: var(--text-muted);
  padding: 2rem 0;
  font-size: 0.9rem;
}
.wall-post {
  background: var(--bg-elevated);
  border-radius: var(--radius-md);
  padding: 0.8rem;
}
.wall-post-header {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.wall-post-author {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.wall-post-time {
  font-size: 0.7rem;
  color: var(--text-muted);
}
.wall-post-delete-btn {
  flex-shrink: 0;
}
.wall-post-body {
  margin: 0.6rem 0 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 0.92rem;
}
.wall-post-image {
  max-width: 100%;
  border-radius: var(--radius-sm);
  margin-top: 0.6rem;
  display: block;
}
.wall-post-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--border-color);
}
.wall-reaction-btn {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: 999px;
  padding: 0.25rem 0.6rem;
  font-size: 0.8rem;
  cursor: pointer;
  color: var(--text-secondary);
}
.wall-reaction-btn.active {
  background: var(--accent-purple-soft);
  border-color: var(--accent-purple);
  color: var(--accent-purple);
}
.wall-comment-toggle-btn {
  background: none;
  border: none;
  margin-left: auto;
  font-size: 0.8rem;
  color: var(--text-secondary);
  cursor: pointer;
}
.wall-comments {
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--border-color);
}
.wall-comments-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  max-height: 220px;
  overflow-y: auto;
}
.wall-comments-empty {
  color: var(--text-muted);
  font-size: 0.8rem;
  margin: 0;
}
.wall-comment-row {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}
.wall-comment-bubble {
  flex: 1;
  min-width: 0;
  background: var(--bg-surface);
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.wall-comment-name {
  font-size: 0.78rem;
  font-weight: 700;
}
.wall-comment-body {
  font-size: 0.85rem;
  word-break: break-word;
}
.wall-comment-compose {
  display: flex;
  gap: 0.4rem;
  margin-top: 0.5rem;
}
.wall-comment-input {
  flex: 1;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 0.85rem;
}
.wall-compose {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  flex-shrink: 0;
}
.wall-compose-hint {
  margin: 0;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.wall-compose-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
#wall-compose-input {
  flex: 1;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 0.9rem;
}
.wall-compose-image-preview {
  position: relative;
  width: fit-content;
}
.wall-compose-image-preview img {
  max-height: 90px;
  border-radius: var(--radius-sm);
  display: block;
}
.wall-compose-image-preview button {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: none;
  background: var(--accent-danger);
  color: #fff;
  cursor: pointer;
  font-size: 0.75rem;
  line-height: 1;
}

/* Efecto grande de regalo estilo TikTok (2026-07-24, pedido explícito) —
   flota centrado arriba, sobre video/chat/karaoke, pointer-events:none para
   no bloquear clicks debajo. Varios regalos seguidos se apilan (column,
   gap) en vez de reemplazarse. */
.gift-overlay-container {
  position: fixed;
  top: 70px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  pointer-events: none;
}
@keyframes gift-overlay-in {
  0% { opacity: 0; transform: scale(0.4) translateY(-30px); }
  60% { opacity: 1; transform: scale(1.15) translateY(4px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
.gift-overlay-item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  background: linear-gradient(135deg, rgba(27, 44, 75, 0.92), rgba(var(--accent-purple-rgb), 0.35));
  border: 1px solid rgba(var(--accent-purple-rgb), 0.4);
  border-radius: 999px;
  padding: 0.5rem 1.2rem 0.5rem 0.5rem;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  animation: gift-overlay-in 0.5s cubic-bezier(.25, 1.5, .5, 1) both;
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.gift-overlay-item.gift-overlay-leaving {
  opacity: 0;
  transform: translateY(-20px) scale(0.9);
}
.gift-overlay-img {
  width: 64px;
  height: 64px;
  object-fit: contain;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.4));
}
.gift-overlay-text {
  display: flex;
  flex-direction: column;
  color: #fff;
  font-size: 0.9rem;
  line-height: 1.3;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}
.gift-overlay-text strong {
  font-size: 1rem;
}
@media (max-width: 640px) {
  .gift-overlay-container { top: 60px; }
  .gift-overlay-img { width: 52px; height: 52px; }
  .gift-overlay-item { padding: 0.4rem 0.9rem 0.4rem 0.4rem; }
}

.online-users-modal {
  max-width: 420px;
}
.online-users-count-badge {
  background: var(--accent-purple-soft);
  color: var(--accent-purple);
  font-size: 0.8rem;
  font-weight: 700;
  border-radius: 999px;
  padding: 0.1rem 0.55rem;
  margin-left: 0.4rem;
}
.online-users-list {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  max-height: 60vh;
  overflow-y: auto;
}
.online-user-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0.3rem;
  border-radius: var(--radius-md);
}
.online-user-row-clickable {
  cursor: pointer;
}
.online-user-row-clickable:hover {
  background: var(--bg-elevated);
}
.online-user-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.online-user-name {
  font-weight: 600;
  font-size: 0.92rem;
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.online-user-status {
  color: var(--text-muted);
  font-size: 0.78rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.online-user-star {
  color: var(--accent-gold);
  flex-shrink: 0;
}
.online-users-empty {
  padding: 1.5rem 1rem;
  color: var(--text-muted);
  text-align: center;
  font-size: 0.85rem;
}
/* Amigos (2026-07-25) — mismo patrón visual que .online-users-modal/
   .online-user-row (fila con avatar+nombre), con una columna de acciones
   extra para las pestañas de solicitudes recibidas/enviadas. */
.friends-modal {
  max-width: 420px;
  display: flex;
  flex-direction: column;
}
.friends-tab-badge {
  background: var(--accent-danger);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
  margin-left: 0.3rem;
}
.friends-panel {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  max-height: 60vh;
  overflow-y: auto;
  padding: 1rem 1.2rem;
}
.friend-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0.3rem;
  border-radius: var(--radius-md);
}
.friend-row-clickable {
  cursor: pointer;
}
.friend-row-clickable:hover {
  background: var(--bg-elevated);
}
.friend-row-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.friend-row-name {
  font-weight: 600;
  font-size: 0.92rem;
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.friend-row-status {
  color: var(--text-muted);
  font-size: 0.78rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.friend-row-actions {
  display: flex;
  gap: 0.4rem;
  flex-shrink: 0;
}
.friend-row-actions .icon-btn {
  padding: 0.35rem 0.6rem;
  font-size: 0.8rem;
}
.friends-empty {
  padding: 1.5rem 1rem;
  color: var(--text-muted);
  text-align: center;
  font-size: 0.85rem;
}

.country-flag {
  width: 1.1em;
  height: 0.75em;
  object-fit: cover;
  border-radius: 1px;
  flex-shrink: 0;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
}

/* Botón "+" con GIFs/YouTube (2026-07-21, otro.txt puntos 4-5) */
.plus-menu {
  display: flex;
  gap: 0.4rem;
  padding: 0.4rem 0;
}
.plus-menu-option {
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  color: var(--text-primary);
  border-radius: var(--radius-sm);
  padding: 0.45rem 0.8rem;
  font-size: 0.85rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}
.plus-menu-option svg { flex-shrink: 0; color: var(--text-muted); }
.plus-menu-option:hover {
  background: var(--bg-elevated);
}

.media-search-modal {
  max-width: 420px;
}
#gif-search-input,
#youtube-search-input {
  width: 100%;
}
.media-search-empty {
  padding: 1.5rem 1rem;
  color: var(--text-muted);
  text-align: center;
  font-size: 0.85rem;
}

.gif-search-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem;
  margin-top: 0.6rem;
  max-height: 50vh;
  overflow-y: auto;
}
.gif-result {
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 0;
  overflow: hidden;
  aspect-ratio: 1;
  cursor: pointer;
}
.gif-result img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.gif-result:hover {
  border-color: var(--accent-purple);
}

.youtube-search-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.6rem;
  max-height: 50vh;
  overflow-y: auto;
}
.youtube-result {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 0.5rem;
  cursor: pointer;
  text-align: left;
}
.youtube-result:hover {
  border-color: var(--accent-purple);
}
.youtube-result img {
  width: 90px;
  height: 50px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}
.youtube-result-title {
  font-size: 0.85rem;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* GIF/video compartidos, ya dentro de un mensaje de chat */
.shared-gif {
  max-width: 220px;
  max-height: 220px;
  border-radius: var(--radius-md);
  display: block;
}

/* Foto/video subido por el botón "+" (2026-07-22) — un poco más grande que
   el GIF (son fotos reales, no un sticker chico) pero con el mismo tope de
   ancho para no romper el layout del mensaje. */
.shared-media-photo {
  max-width: 280px;
  max-height: 320px;
  border-radius: var(--radius-md);
  display: block;
  object-fit: contain;
}
.shared-media-video {
  max-width: 280px;
  max-height: 320px;
  border-radius: var(--radius-md);
  display: block;
}
.shared-youtube {
  cursor: pointer;
  max-width: 280px;
}
.shared-youtube-thumb {
  position: relative;
  border-radius: var(--radius-md);
  overflow: hidden;
}
.shared-youtube-thumb img {
  width: 100%;
  display: block;
}
.shared-youtube-thumb iframe {
  width: 100%;
  aspect-ratio: 16 / 9;
  border: none;
  display: block;
}
.shared-youtube-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
  color: #fff;
  background: rgba(0, 0, 0, 0.35);
}
.shared-youtube-title {
  font-size: 0.85rem;
  margin: 0.3rem 0 0;
}

/* Aviso de "Fulano entró/salió de la sala" (2026-07-21) */
.msg-system {
  text-align: center;
  color: var(--text-muted);
  font-size: 0.78rem;
  padding: 0.2rem 0;
}

/* Sala de Karaoke (2026-07-21) */
.karaoke-player-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--radius-md);
  overflow: hidden;
}
.karaoke-player-wrap iframe,
#karaoke-player {
  width: 100%;
  height: 100%;
}

/* Chat + regalos flotando sobre el video, estilo TikTok Live (2026-07-21) */
.karaoke-chat-overlay {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 55%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.2rem;
  padding: 0.5rem 0.6rem;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0));
  pointer-events: none;
}
.karaoke-chat-overlay-msg {
  font-size: 0.78rem;
  line-height: 1.3;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
  animation: karaoke-overlay-in 0.2s ease-out;
}
.karaoke-chat-overlay-msg strong {
  color: var(--accent-cyan);
}
.karaoke-chat-overlay-msg.fading {
  opacity: 0;
  transition: opacity 1.5s ease;
}
@keyframes karaoke-overlay-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.karaoke-now-playing {
  font-size: 0.9rem;
  color: var(--text-secondary);
  margin: 0;
}
.karaoke-mic-row {
  display: flex;
}
#karaoke-mic-btn {
  font-weight: 600;
}
.karaoke-controls {
  display: flex;
  gap: 0.5rem;
}
.karaoke-queue-heading {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.karaoke-queue-list {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.karaoke-queue-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--bg-surface-2);
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.6rem;
}
.karaoke-queue-title {
  flex: 1;
  font-size: 0.85rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.karaoke-queue-title small {
  color: var(--text-muted);
}

/* ============================================================
   Personalización de fuentes/estilos (2026-07-23, pedido explícito
   con imágenes de referencia de otra app: letras.png, perfil.png,
   leter-perfil.png)
   ============================================================ */

/* Nick clickeable dentro de un mensaje de chat (2026-07-23) — mismo menú
   (ver perfil/privado/regalo) que ya funcionaba desde el roster/en línea. */
.msg-sender-name {
  cursor: pointer;
}
.msg-sender-name:hover .decorated-name {
  text-decoration: underline;
}

/* Vista previa del modal de "Fuente del chat" (botón "+"). */
.chat-style-preview {
  background: var(--bg-surface-2);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 0.9rem;
  margin: 0.4rem 0 0.9rem;
  font-size: 1.05rem;
  word-break: break-word;
}

/* Picker de estilo de tarjeta de perfil — mismo patrón visual que
   .badge-option (círculo con un puntito de color adentro en vez de un
   ícono), para no inventar un componente nuevo. */
.style-option {
  border-width: 2px;
}
.style-swatch-dot {
  display: block;
  width: 16px;
  height: 16px;
  border-radius: 50%;
}

/* Borde/glow de estilo de perfil sobre el .modal de "ver perfil" — variable
   CSS por instancia (--profile-style-color), puesta desde JS
   (applyProfileStyleToModal en avatar.js). */
.modal.profile-style-active {
  border: 2px solid var(--profile-style-color);
  box-shadow: 0 0 18px var(--profile-style-color);
}
.modal.profile-style-blink {
  animation: profile-style-pulse 1.8s ease-in-out infinite;
}
@keyframes profile-style-pulse {
  0%, 100% { box-shadow: 0 0 8px var(--profile-style-color); }
  50% { box-shadow: 0 0 26px var(--profile-style-color); }
}

/* Fondo decorativo de la tarjeta de perfil (2026-07-23) — puesto como
   background inline por JS (applyCardBackgroundToModal, avatar.js); acá
   solo un leve ajuste para que el contenido siga siendo legible encima. */
.modal.has-card-background .modal-header,
.modal.has-card-background .modal-footer {
  background: rgba(0, 0, 0, 0.2);
}

/* Picker de fondo de tarjeta — mismo patrón que .style-option, pero
   mostrando una franja real del gradiente en vez de un puntito sólido. */
.cardbg-option {
  padding: 2px;
}
.cardbg-swatch {
  display: block;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
}

/* Regalos recibidos en "ver perfil" (2026-07-23). */
.view-profile-gifts-section {
  margin-top: 0.75rem;
  text-align: center;
}
.view-profile-gifts-heading {
  font-size: 0.8rem;
  color: var(--text-secondary);
  margin-bottom: 0.5rem;
}
.view-profile-gifts-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
}
.view-profile-gift-icon {
  width: 32px;
  height: 32px;
  object-fit: contain;
}

/* Overlay de efecto de entrada — animal/vehículo de la tienda al unirse a
   una sala (2026-07-24). Mismo z-index que .gift-overlay-container (ambos
   son celebratorios y efímeros, pointer-events:none, no compiten porque
   ocupan zonas distintas de la pantalla). */
.entrance-effect-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
}
.entrance-effect-overlay.entrance-effect-show {
  opacity: 1;
}
.entrance-effect-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.7rem;
}
.entrance-effect-img {
  width: min(45vw, 280px);
  height: min(45vw, 280px);
  object-fit: contain;
  filter: drop-shadow(0 0 30px rgba(255, 255, 255, 0.35));
}
.entrance-effect-overlay.entrance-effect-show .entrance-effect-img {
  animation: entrance-effect-bounce 0.6s cubic-bezier(0.34, 1.56, 0.64, 1), entrance-effect-shake 0.3s ease 0.5s;
}
.entrance-effect-caption {
  margin: 0;
  padding: 0.4rem 1.1rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-weight: 700;
  font-size: 0.95rem;
  backdrop-filter: blur(4px);
}
@keyframes entrance-effect-bounce {
  0% { transform: scale(0.3); opacity: 0; }
  60% { transform: scale(1.2); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes entrance-effect-shake {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(-6px, 0); }
  40% { transform: translate(6px, 0); }
  60% { transform: translate(-4px, 0); }
  80% { transform: translate(4px, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .entrance-effect-overlay.entrance-effect-show .entrance-effect-img {
    animation: none;
  }
}
