/**
 * Conecta — estilos de cliente. Paleta y tipografías tomadas literalmente
 * de "Conecta - Cara al Cliente.dc.html" (proyecto de Claude Design).
 * Mobile-first, sin marco de dispositivo — columna que llena el ancho en
 * celular y se centra con max-width en pantallas grandes.
 */

:root {
  --bg: #0C0518;
  --bg-soft: #1C0B38;
  --bg-soft-2: #120720;
  --ink: #F3ECFF;
  --ink-muted: #B7A6DB;
  --ink-faint: #8E7CB8;
  --accent-1: #FF2E93;
  --accent-2: #8A2BE8;
  --accent-gradient: linear-gradient(90deg, var(--accent-1), var(--accent-2));
  --green: #3DE38A;
  --amber: #FFB35C;
  --card-border: rgba(255,255,255,.12);
  --card-bg: rgba(255,255,255,.08);
  /* Fondo animado de puntos+ondas (assets/js/auth-grid.js) — mismo sistema
     que miplanes.com/login/ (site/public/assets/css/auth-scene.css), acá
     con el naranja de marca de Conecta (--auth-bg = mismo #FF5A1F que ya
     se usa en body.brand-active/.brand-close/.icon-accent-right) en vez
     del naranja propio de ese sitio. auth-grid.js lee estos nombres de
     variable tal cual — no se tocó el script. */
  --auth-bg: #FF5A1F;
  --auth-dot-idle: #FF9D6E;
  --auth-dot-hot: #C2410C;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: 'Plus Jakarta Sans', system-ui, sans-serif;
  /* Sin zoom en ninguna pantalla. OJO: "manipulation" NO sirve para esto —
     por spec permite pinch-zoom, solo quita el doble-tap. pan-x/pan-y (sin
     "pinch-zoom" en la lista) es lo que realmente lo bloquea, y cubre
     Android/Chrome. iOS Safari además ignora user-scalable=no del viewport
     desde hace años — el bloqueo real ahí lo hacen los listeners de
     gesturestart en app.js. */
  touch-action: pan-x pan-y;
  /* position:fixed + overflow:hidden en el documento entero (no solo en
     #app) — esta app es una sola pantalla siempre (ver index.html, #app es
     el único hijo de <body>), así que nunca hace falta que el documento
     scrollee. Sin esto, al enfocar el input del composer, iOS Safari hace
     su propio "scroll to bring focused element into view" sobre el
     DOCUMENTO (no sobre #app), empujando todo hacia arriba — el topbar
     queda fuera de la pantalla y el composer enfocado abajo del todo, aun
     cuando #app ya está correctamente acotado a 100dvh. Con el documento
     fijo, no hay dónde scrollear: el teclado solo puede achicar el visual
     viewport, que 100dvh en #app ya sigue. */
  position: fixed;
  inset: 0;
  overflow: hidden;
  width: 100%;
}

/* #app tiene max-width:480px centrado — en cualquier pantalla más ancha
   (o cuando el alto del viewport no coincide con el del fondo), el body
   se ve por fuera de esa columna. Con la sala de chat y el escaneo QR esa
   franja es el --bg morado oscuro a propósito. Para cualquier pantalla de
   marca sobre fondo naranja (home + .brand-screen: validando, pedir
   nickname, errores/bloqueos del flujo de acceso) esa misma franja
   morada desentonaba — se corrige con esta clase, que cada una de esas
   pantallas agrega a <body> al renderizarse (ver markBrandBackground()
   en app.js). */
body.brand-active { background: var(--auth-bg); }

/* auth-grid.js dibuja siempre (el canvas vive permanente en index.html,
   fuera del ciclo de innerHTML de #app — recrearlo en cada pantalla
   reiniciaría la animación y apilaría varios requestAnimationFrame) pero
   solo se ve en las pantallas de marca — mismo interruptor que ya prende
   el fondo naranja de body (body.brand-active, ver arriba y
   markBrandBackground() en app.js). Oculto por defecto para no taparle
   nada a la sala de chat / escaneo QR. */
#auth-grid, .auth-vignette { display: none; }
body.brand-active #auth-grid, body.brand-active .auth-vignette { display: block; }
#auth-grid { position: fixed; inset: 0; z-index: -2; width: 100%; height: 100%; }
.auth-vignette {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(255,90,31,0) 0%, rgba(255,90,31,0) 45%, rgba(255,90,31,.92) 100%);
}

h1, h2, h3, .heading {
  font-family: 'Bricolage Grotesque', system-ui, sans-serif;
  margin: 0;
}

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

#app {
  /* height fija (no min-height): así los hijos con flex:1 quedan realmente
     acotados al viewport y el que tenga overflow:auto (.messages) es el
     único que puede scrollear — si fuera min-height, un hijo con contenido
     largo simplemente estira #app más allá del viewport y termina
     scrolleando la página completa (topbar y composer incluidos). */
  height: 100vh;
  height: 100dvh;
  max-width: 480px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
}

@keyframes cnPulse { 0% { box-shadow: 0 0 0 0 rgba(61,227,138,.55); } 70% { box-shadow: 0 0 0 9px rgba(61,227,138,0); } 100% { box-shadow: 0 0 0 0 rgba(61,227,138,0); } }
@keyframes cnScanline { 0% { transform: translateY(0); } 100% { transform: translateY(210px); } }
@keyframes cnSpin { to { transform: rotate(360deg); } }
@keyframes cnFadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }

.dot-live {
  width: 10px; height: 10px; border-radius: 999px; background: var(--green);
  animation: cnPulse 2.4s ease-out infinite; flex-shrink: 0; display: inline-block;
}

/* ── Top bar (escaneo / sala) ─────────────────────────────────────── */
.topbar {
  display: flex; align-items: center; gap: 12px;
  padding: max(env(safe-area-inset-top), 18px) 16px 12px;
  border-bottom: 1px solid rgba(255,255,255,.08);
  flex-shrink: 0; /* fijo arriba — que no lo achique .messages al crecer */
  /* Personalización por sala (migraciones 037/039) — --room-topbar-text/bg
     los pone applyRoomTheme() en document.body; sin sala personalizada
     caen al var(--ink)/transparente de siempre. .topbar-title .name no
     tiene color propio, lo hereda de acá. */
  color: var(--room-topbar-text, var(--ink));
  background: var(--room-topbar-bg, transparent);
}
.icon-btn {
  width: 38px; height: 38px; border-radius: 999px; border: none;
  background: rgba(255,255,255,.1); color: inherit; /* hereda de .topbar cuando está ahí; fuera de la sala, de body (var(--ink)) — mismo resultado de siempre */
  display: flex; align-items: center; justify-content: center;
  font-size: 16px; flex-shrink: 0;
}
.brand-logo-btn {
  width: 38px; height: 38px; border-radius: 999px; border: none; padding: 0;
  background: transparent; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
}
.brand-logo-btn img { width: 100%; height: 100%; border-radius: 999px; display: block; }
.topbar-title { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.topbar-title .name { font-family: 'Bricolage Grotesque'; font-size: 18px; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topbar-title .presence { display: flex; align-items: center; gap: 7px; font-size: 13px; color: #9FE9C2; }
.badge-event {
  padding: 3px 9px; border-radius: 999px; background: rgba(255,179,92,.18);
  border: 1px solid rgba(255,179,92,.45); font-size: 11px; font-weight: 700; color: #FFCE9B;
}

/* ── Botón primario / secundario ──────────────────────────────────── */
.btn {
  height: 52px; border-radius: 999px; border: none; display: flex; align-items: center;
  justify-content: center; gap: 8px; font-size: 16px; font-weight: 700; width: 100%;
}
.btn-primary { background: var(--accent-gradient); color: #fff; }
.btn-secondary { background: rgba(255,255,255,.1); color: #DCD2F2; font-weight: 600; }

/* ── Home (https://conecta.miplanes.com/, sin joinToken) ──────────────
   Splash de marca: fondo de puntos+ondas animado (#auth-grid, ver arriba)
   detrás — .home-screen en sí queda transparente a propósito — logo
   centrado, un solo CTA abajo — nada de topbar/título/cuerpo como el
   resto de screen-centered genéricas (esas siguen siendo para
   errores/estados, no para acá). */
.home-screen {
  /* position:relative + logo absoluto = centrado real en el medio de la
     pantalla (antes el botón abajo "empujaba" el logo hacia arriba dentro
     del space-between/flex — quedaba centrado en el espacio libre, no en
     la pantalla entera). El botón queda aparte, pegado abajo, sin afectar
     ese centrado. */
  flex: 1; position: relative;
}
.home-logo {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 200px; max-width: 60%; height: auto;
}
.home-actions {
  position: absolute; left: 24px; right: 24px; bottom: max(env(safe-area-inset-bottom), 24px);
}
/* Blanco + naranja/negro, tomados del propio logo-conecta.png (los puntos
   son naranjas, "CONECTA" va en negro sobre su píldora) — no la paleta
   rosa/violeta del resto de la app, que acá no aparece en ningún lado.
   .btn-light: mismo botón, reutilizado en todas las pantallas de marca de
   abajo (no solo la home). */
.btn-light { background: #fff; color: #17120C; }
/* Flechita (icon('arrow-up') rotado) al final del CTA — un solo lugar
   para el estilo, en vez de repetir transform/display inline en cada
   pantalla que arma un botón .btn-light. */
.icon-accent-right { display: flex; color: #FF5A1F; transform: rotate(90deg); }

/* ── Pantallas de marca sobre fondo naranja (flujo de acceso: escaneo QR
   fallido, validando, pedir nickname, sesión vencida, bloqueos, sala
   llena/cerrada, etc.) — mismo lenguaje visual que la home: fondo de
   puntos+ondas animado (#auth-grid), logo horizontal, un recuadro blanco
   con el contenido, CTA .btn-light abajo. NO incluye la vista de cámara
   del escaneo (necesita mostrarse oscura para que el video de la cámara
   se vea bien) ni la sala de chat en sí (mensajería, otro tipo de
   pantalla) — esas siguen con el tema oscuro de siempre. */
.brand-screen {
  /* justify-content:center centra el bloque logo+card COMO UNIDAD en el
     alto disponible (ver .brand-card más abajo — ya no fuerza flex:1, así
     que su alto lo define el propio contenido, no el espacio libre). El
     padding-bottom deja aire para el botón, que va aparte, con
     position:absolute pegado abajo (mismo truco que .home-actions) —
     así no compite por espacio con el centrado de logo+card. .brand-screen
     en sí queda transparente — el fondo lo pone #auth-grid detrás. */
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: max(env(safe-area-inset-top), 20px) 20px calc(max(env(safe-area-inset-bottom), 20px) + 88px);
  position: relative;
  animation: cnFadeIn .25s ease;
}
.brand-close {
  position: absolute; top: max(env(safe-area-inset-top), 20px); left: 20px;
  width: 38px; height: 38px; border-radius: 999px; background: #fff; color: #FF5A1F;
  display: flex; align-items: center; justify-content: center; text-decoration: none; flex-shrink: 0;
}
.brand-logo-h { width: 210px; max-width: 72%; height: auto; margin-bottom: 22px; flex-shrink: 0; }
.brand-card {
  width: 100%; max-width: 380px; max-height: 100%;
  background: #fff; border-radius: 26px; border: 2px solid #FF5A1F;
  box-shadow: 0 20px 44px rgba(70,20,0,.22);
  padding: 30px 24px; display: flex; flex-direction: column; align-items: center;
  justify-content: center; text-align: center; gap: 14px; color: #17120C;
  overflow-y: auto; flex-shrink: 0;
}
.brand-card .icon-circle {
  width: 64px; height: 64px; border-radius: 999px; background: rgba(255,90,31,.12);
  display: flex; align-items: center; justify-content: center; font-size: 30px; flex-shrink: 0;
  color: #FF5A1F; /* antes no hacía falta — el emoji traía su propio color; el ícono SVG hereda currentColor */
}
.brand-card .title { font-family: 'Bricolage Grotesque'; font-size: 21px; font-weight: 700; line-height: 1.2; }
.brand-card .body { font-size: 14px; line-height: 1.55; color: #6B5A4C; max-width: 280px; }
/* Aviso no bloqueante de términos/privacidad al ingresar sin cuenta — pedido
   explícito, ver sección 15 de /terminos y sección 14 de /politicas. */
.entry-legal-hint {
  font-size: 11px; line-height: 1.5; color: #9A8B7E; max-width: 280px; margin-top: 2px;
}
.entry-legal-hint a { color: #C2431A; text-decoration: underline; }
.entry-legal-hint a:hover { color: #FF5A1F; }

/* ── Identidad del establecimiento en la pantalla de entrada (pedido
   explícito) — logo del lugar + ícono de conversación, mismo tamaño,
   con un "+" al medio ("logo más conversación"); debajo, una burbuja con
   el nombre del lugar y el de la sala. Solo aparece si la sala está
   anclada a un establecimiento (ver entryIdentityHtml() en app.js) —
   una sala privada standalone sigue mostrando solo el ícono de siempre. */
.entry-brand-row { display: flex; align-items: center; justify-content: center; gap: 14px; }
.entry-logo-circle {
  width: 64px; height: 64px; border-radius: 999px; background: rgba(255,90,31,.12);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  color: #FF5A1F; font-weight: 700; font-size: 22px; flex-shrink: 0;
}
.entry-logo-circle img { width: 100%; height: 100%; object-fit: cover; }
.entry-plus { display: flex; color: #FF5A1F; flex-shrink: 0; }
.entry-venue-chip {
  display: inline-flex; max-width: 100%; padding: 8px 16px; border-radius: 999px;
  background: rgba(255,90,31,.08); border: 1px solid rgba(255,90,31,.2);
  color: #5B4A3D; font-size: 13px; font-weight: 600; text-align: center;
}
.brand-actions {
  position: absolute; left: 20px; right: 20px; bottom: max(env(safe-area-inset-bottom), 20px);
  max-width: 380px; margin: 0 auto;
}
.brand-secondary-link {
  display: block; text-align: center; margin-top: 14px; color: #fff; font-weight: 600;
  font-size: 14px; text-decoration: none; opacity: .9;
}

/* ── Pantalla centrada genérica (escaneo/validación/errores) ─────────── */
.screen {
  /* flex:1 dentro de #app (ya con height fija) alcanza para llenar el
     alto disponible — min-height aquí competía con esa altura fija y
     podía volver a forzar el scroll de página completa. */
  flex: 1; display: flex; flex-direction: column; min-height: 0;
  animation: cnFadeIn .25s ease;
}
.screen-centered {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: 22px; padding: 24px 24px 40px;
}
.screen-centered .art { width: 120px; height: 120px; border-radius: 32px; display: flex;
  align-items: center; justify-content: center; font-size: 44px; }
.screen-centered .title { font-family: 'Bricolage Grotesque'; font-size: 28px; font-weight: 700;
  line-height: 1.12; letter-spacing: -.02em; }
.screen-centered .body { font-size: 15px; line-height: 1.55; color: var(--ink-muted); max-width: 320px; }
.screen-actions { display: flex; flex-direction: column; gap: 12px; padding: 0 24px 40px; }

/* ── Escaneo QR ────────────────────────────────────────────────────── */
.scan-viewport {
  flex: 1; margin: 0 16px 16px; border-radius: 26px; overflow: hidden; position: relative;
  background: linear-gradient(170deg,#1A1420 0%,#241A2E 45%,#140E1C 100%);
  display: flex; align-items: center; justify-content: center;
}
.scan-viewport video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.scan-frame { position: relative; width: 220px; height: 220px; z-index: 2; }
.scan-frame span { position: absolute; width: 44px; height: 44px; border-color: var(--accent-1); border-style: solid; border-width: 0; }
.scan-frame .tl { top:0; left:0; border-top-width:3px; border-left-width:3px; border-radius:14px 0 0 0; }
.scan-frame .tr { top:0; right:0; border-top-width:3px; border-right-width:3px; border-radius:0 14px 0 0; }
.scan-frame .bl { bottom:0; left:0; border-bottom-width:3px; border-left-width:3px; border-radius:0 0 0 14px; }
.scan-frame .br { bottom:0; right:0; border-bottom-width:3px; border-right-width:3px; border-radius:0 0 14px 0; }
.scan-caption { position: absolute; bottom: 26px; left: 24px; right: 24px; text-align: center; z-index: 2; }
.scan-caption .t { font-family: 'Bricolage Grotesque'; font-size: 22px; font-weight: 700; margin-bottom: 6px; }
.scan-caption .s { font-size: 14px; line-height: 1.5; color: var(--ink-muted); }
.scan-footnote { padding: 20px 24px 0; text-align: center; font-size: 13px; color: var(--ink-faint); line-height: 1.5; }

/* ── Validando — vive dentro de .brand-card (fondo blanco, ver arriba) ── */
.spinner { width: 80px; height: 80px; border-radius: 999px; border: 3px solid rgba(255,90,31,.15);
  border-top-color: #FF5A1F; animation: cnSpin 1.1s linear infinite; }
.checklist { display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 260px; }
.checklist .item { display: flex; align-items: center; gap: 10px; font-size: 14px; color: #4A3D33; }
.checklist .tick-done { width: 22px; height: 22px; border-radius: 999px; background: var(--green);
  color: #0A2A18; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; }
.checklist .tick-pending { width: 22px; height: 22px; border-radius: 999px; border: 2px solid rgba(0,0,0,.15); }

/* ── Sala (chat) ───────────────────────────────────────────────────── */
/* .room llena el alto fijo de #app (flex:1 + min-height:0, mismo motivo que
   .screen arriba); topbar y composer no tienen flex → mantienen su alto
   natural; .messages es el único flex:1 con overflow-y:auto → es el único
   que scrollea. min-height:0 en .messages es imprescindible: sin eso, un
   hijo flex nunca se encoge por debajo de su contenido (regla por defecto
   de flexbox), así que con mensajes largos .messages empujaría a topbar y
   composer fuera de la pantalla en vez de scrollear internamente. */
.room { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.messages {
  flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: 16px 16px 8px; display: flex; flex-direction: column; gap: 14px;
  /* Personalización por sala (migraciones 037/038/039) — la imagen va
     directo en el propio background de .messages (no en un ::before con
     z-index negativo — eso dependía de que .messages formara su propio
     contexto de apilamiento, y no se estaba viendo: más frágil de lo que
     valía la pena). Así, background-image SIEMPRE pinta detrás del
     contenido normal del elemento — es básico de CSS, cero riesgo de que
     se "escape" a pintarse en el lugar equivocado.
     - "cover": la imagen llena el recuadro entero, recortando si hace
       falta, sin deformarse.
     - "fixed" se pidió también (que no se desplace junto con los
       mensajes al scrollear) — pero background-attachment:fixed tiene
       historial de fallar en navegadores móviles (Android/iOS a veces
       directamente NO pintan el fondo con esa propiedad puesta, no es
       solo un tema estético) — justo el tipo de causa que podía explicar
       "en el preview de escritorio se ve, en el chat del celular no". Se
       saca a propósito: la imagen scrollea junto con los mensajes (más
       parecido a un papel tapiz normal que a uno flotante), pero SE VE
       en cualquier navegador — que se vea es lo prioritario acá.
     - Opacidad: applyRoomTheme() en app.js (y su gemelo en
       theme-preview.js) ya arman --room-chat-bg-image mezclando un
       degradado semitransparente NEGRO por encima de la imagen, dentro
       del mismo valor de background-image (varias capas en una sola
       propiedad) — no existe una propiedad CSS de "opacidad de solo el
       background-image sin afectar el contenido" aparte de esta técnica,
       así que el propio valor ya "trae" la opacidad horneada adentro; acá
       solo se pinta tal cual. */
  background-color: var(--room-chat-bg, transparent);
  background-image: var(--room-chat-bg-image, none);
  background-size: cover; background-position: center; background-repeat: no-repeat;
}
.msg-row { display: flex; gap: 10px; }
.msg-row.self { flex-direction: row-reverse; }
.msg-avatar { width: 34px; height: 34px; border-radius: 999px; display: flex; align-items: center;
  justify-content: center; font-size: 12px; font-weight: 700; color: #fff; flex-shrink: 0; cursor: pointer;
  overflow: hidden; }
.msg-avatar img { width: 100%; height: 100%; object-fit: cover; }
.msg-col { display: flex; flex-direction: column; gap: 4px; max-width: 270px; }
.msg-row.self .msg-col { align-items: flex-end; }
.msg-meta { font-size: 12px; color: var(--ink-faint); }
.bubble { padding: 11px 14px; border-radius: 18px 18px 18px 6px; background: var(--card-bg);
  font-size: 15px; line-height: 1.4; word-break: break-word; }
.msg-row.self .bubble { border-radius: 18px 18px 6px 18px; background: var(--accent-gradient); color: #fff; }
.bubble.hidden-report { background: transparent; border: 1px dashed rgba(255,255,255,.14);
  color: var(--ink-faint); font-style: italic; }
.system-note { align-self: center; padding: 6px 14px; border-radius: 999px; background: rgba(255,255,255,.06);
  font-size: 12px; color: var(--ink-faint); }
.event-note { padding: 12px 14px; border-radius: 16px; background: linear-gradient(120deg,rgba(255,179,92,.14),rgba(255,46,147,.1));
  border: 1px solid rgba(255,255,255,.1); font-size: 13px; line-height: 1.5; color: #EBDDF7; }
.typing-bubble { display: flex; gap: 5px; padding: 13px 15px; border-radius: 18px; background: var(--card-bg); width: fit-content; }
.typing-bubble span { width: 6px; height: 6px; border-radius: 999px; background: var(--ink-muted); }

/* ── Mensaje destacado de moderador (mod:broadcast — migración 034) ──── */
.announcement-card { width: 100%; padding: 14px 16px; border-radius: 18px;
  background: var(--accent-gradient); color: #fff; }
.announcement-head { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 13px;
  text-transform: uppercase; letter-spacing: .02em; opacity: .92; margin-bottom: 6px; }
.announcement-body { font-size: 15px; line-height: 1.45; }
.mod-name { display: inline-flex; align-items: center; gap: 3px; color: #FF9F3D; font-weight: 800; }

/* ── Emoji (Twemoji) — mismo dibujo en cualquier dispositivo, ver app.js emojify() ── */
img.emoji { height: 1.2em; width: 1.2em; margin: 0 .05em 0 .1em; vertical-align: -0.2em; }
.typing-bubble span:nth-child(2) { opacity: .6; }
.typing-bubble span:nth-child(3) { opacity: .3; }

.composer { padding: 10px 16px max(env(safe-area-inset-bottom), 16px); border-top: 1px solid rgba(255,255,255,.08);
  display: flex; align-items: center; gap: 10px; flex-shrink: 0; /* fijo abajo, mismo motivo que .topbar */
  background: var(--room-composer-bg, transparent); color: var(--room-composer-text, var(--ink)); /* personalización por sala — migración 037 */ }
.composer input { flex: 1; height: 46px; border-radius: 999px; background: rgba(255,255,255,.08); border: none;
  padding: 0 18px; font-size: 16px; color: inherit; outline: none; } /* 16px — bajo eso iOS hace zoom automático al enfocar; color:inherit toma el de .composer */
/* Antes fijo (#9585BA) sin importar el tema — ahora sigue el color de
   "Texto de la franja inferior" (personalización por sala), atenuado con
   opacity para que se distinga del texto real que sí se escribió; si la
   sala no tiene tema propio, cae al mismo gris de siempre. */
.composer input::placeholder { color: var(--room-composer-text, #9585BA); opacity: .55; }
.composer .send-btn { width: 46px; height: 46px; border-radius: 999px; background: var(--accent-gradient);
  border: none; display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0;
  /* Bug reportado: el color de "Texto de la franja inferior" (--room-composer-text)
     no afectaba a este botón — quedaba siempre blanco fijo, ignorando la
     personalización. #fff sigue siendo el default (buen contraste contra
     el degradado de marca), pero ahora si se personaliza el texto de esa
     franja, el ícono de enviar lo sigue también. */
  color: var(--room-composer-text, #fff); }
.composer .send-btn:disabled { opacity: .5; }

.room.closing .messages { opacity: .4; filter: saturate(.5); pointer-events: none; }
.room.closing .topbar { opacity: .55; }

/* ── Toast ─────────────────────────────────────────────────────────── */
.toast { margin: 14px 16px 0; padding: 14px 16px; border-radius: 20px; background: rgba(61,227,138,.12);
  border: 1px solid rgba(61,227,138,.3); display: flex; align-items: center; gap: 12px; }
.toast .tick { width: 26px; height: 26px; border-radius: 999px; background: var(--green); color: #0A2A18;
  display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; flex-shrink: 0; }
.toast .txt { font-size: 14px; line-height: 1.45; color: #D8FBE8; }

/* ── Hojas modales (acciones / reportar / bloquear / cierre) ─────────── */
.sheet-backdrop { position: fixed; inset: 0; background: rgba(6,3,14,.55); z-index: 40;
  display: flex; align-items: flex-end; }
.sheet { width: 100%; max-width: 480px; margin: 0 auto; padding: 0 12px max(env(safe-area-inset-bottom), 24px);
  display: flex; flex-direction: column; gap: 14px; }
/* Personalización por sala (migración 040) — fondo de las hojas modales
   (este sheet-card y .sheet-confirm-card más abajo). Fijo antes
   (rgba(30,16,52,.97), el morado oscuro de siempre) sin importar el tema. */
.sheet-card { border-radius: 26px; background: var(--room-modal-bg, rgba(30,16,52,.97)); border: 1px solid var(--card-border); overflow: hidden; }
.sheet-card .sheet-label { padding: 14px 20px; font-size: 13px; color: var(--ink-faint); border-bottom: 1px solid rgba(255,255,255,.08); }
.sheet-action { display: flex; align-items: center; gap: 12px; padding: 16px 20px; font-size: 16px;
  border-bottom: 1px solid rgba(255,255,255,.08); background: none; border-left: none; border-right: none; border-top: none;
  color: var(--ink); width: 100%; text-align: left; }
.sheet-action:last-child { border-bottom: none; }
.sheet-action.muted { color: #C6B6E8; }
.sheet-cancel { height: 52px; border-radius: 999px; background: rgba(255,255,255,.1); border: none;
  color: var(--ink); font-size: 16px; font-weight: 600; }
.sheet-confirm-card { padding: 24px 22px; border-radius: 28px; background: var(--room-modal-bg, rgba(30,16,52,.97));
  border: 1px solid var(--card-border); display: flex; flex-direction: column; gap: 12px; align-items: center; text-align: center; }
.sheet-confirm-card .title { font-family: 'Bricolage Grotesque'; font-size: 21px; font-weight: 700; line-height: 1.2; }
.sheet-confirm-card .body { font-size: 14px; line-height: 1.55; color: #C6B6E8; }

/* ── Cierre automático ─────────────────────────────────────────────── */
.close-card { margin: 0 16px 18px; padding: 22px 20px; border-radius: 26px;
  background: linear-gradient(150deg,rgba(123,47,247,.22),rgba(255,46,147,.14)); border: 1px solid var(--card-border);
  display: flex; flex-direction: column; gap: 14px; text-align: center; }
.close-card-icon { display: flex; justify-content: center; color: var(--ink); }
.close-card .title { font-family: 'Bricolage Grotesque'; font-size: 23px; font-weight: 700; line-height: 1.15; }
.close-card .body { font-size: 14px; line-height: 1.5; color: #C6B6E8; }

/* ── Utilidades ────────────────────────────────────────────────────── */
.hidden { display: none !important; }
.center-loader { flex: 1; display: flex; align-items: center; justify-content: center; }
