/* ==========================================================================
   LeadSync - Adornos de la burbuja
   Acuses de entrega, formato de WhatsApp, divisor de no leidos, carga por lotes,
   badge de no leidos, menu contextual, citas, reacciones y marcas de editado.

   OJO CON EL ORDEN: estos ficheros se enlazan en index.html en un orden concreto y
   ese orden ES la cascada. Mover un enlace de sitio cambia que regla gana entre dos
   de la misma especificidad. Si anades uno nuevo, ponlo donde le toque, no al final.
   ========================================================================== */

/* --- Estados de entrega (checks) --- */
.bubble .meta {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 3px;
}
.msg-status {
  display: inline-flex;
  color: var(--text-dim);
}
.msg-status .ic {
  width: 15px;
  height: 15px;
}
.msg-status.read { color: #53bdeb; }
.msg-status.training {
  align-items: center;
  gap: 3px;
  color: var(--modo-entrenamiento);
  font-size: 11px;
  font-weight: 600;
}
/* Respuesta del bot en entrenamiento: fondo y borde morados translúcidos para
   que la respuesta fantasma no se confunda con un mensaje enviado al cliente. */
.bubble.bot:has(.msg-status.training) {
  background: color-mix(in srgb, var(--modo-entrenamiento) 15%, transparent);
  border: 1px dashed var(--modo-entrenamiento);
  color: var(--text);
  --author-bot: var(--modo-entrenamiento);
  --text-dim: color-mix(in srgb, var(--text) 70%, transparent);
}
.bubble[data-entrenamiento="true"] {
  animation: trainingBubbleEntra 180ms ease-out;
}
@keyframes trainingBubbleEntra {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .bubble[data-entrenamiento="true"] { animation: none; }
}
.training-actions {
  margin-top: 8px;
  padding: 7px 9px;
  border-left: 2px solid var(--modo-entrenamiento);
  background: var(--modo-entrenamiento-bg);
  border-radius: 4px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-size: 12px;
}
.training-actions strong { display: block; margin-bottom: 3px; }
.training-actions ul { margin: 0; padding-left: 18px; }
.training-actions li + li { margin-top: 4px; }

/* --- Acciones y notas del modo entrenamiento (descartar y evaluar) --- */
.training-bubble-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px dashed color-mix(in srgb, var(--modo-entrenamiento) 35%, transparent);
}

.training-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: 6px;
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  color: var(--text);
  line-height: 1.3;
  transition: background-color 140ms ease, border-color 140ms ease, color 140ms ease;
}

.training-btn .ic {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
}

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

.training-btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

.training-btn-discard {
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 30%, transparent);
}

.training-btn-discard:hover {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border-color: var(--danger);
}

.training-btn-eval {
  color: var(--modo-entrenamiento);
  border-color: color-mix(in srgb, var(--modo-entrenamiento) 40%, transparent);
}

.training-btn-eval:hover {
  background: color-mix(in srgb, var(--modo-entrenamiento) 15%, transparent);
  border-color: var(--modo-entrenamiento);
}

.training-note-panel {
  margin-top: 4px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
}

.training-saved-card {
  padding: 8px 11px;
  border-radius: 8px;
  background: var(--modo-entrenamiento-bg);
  border: 1px solid color-mix(in srgb, var(--modo-entrenamiento) 40%, transparent);
  border-left: 3px solid var(--modo-entrenamiento);
  transition: background-color 140ms ease, border-color 140ms ease;
}

.training-saved-card.is-mine {
  cursor: pointer;
}

.training-saved-card.is-mine:hover {
  background: color-mix(in srgb, var(--modo-entrenamiento-bg) 85%, var(--modo-entrenamiento));
}

.training-author-me {
  font-weight: 700;
  opacity: 0.85;
}

.training-saved-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 11px;
  color: var(--modo-entrenamiento);
  margin-bottom: 4px;
}

.training-saved-author {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.training-saved-author .ic {
  width: 12px;
  height: 12px;
}

.training-saved-time {
  font-size: 10px;
  color: var(--text-dim);
}

.training-saved-body {
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--text);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.training-note-editor {
  padding: 9px 11px;
  border-radius: 8px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-left: 3px solid var(--modo-entrenamiento);
  box-shadow: 0 4px 12px var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 6px;
  animation: trainingNoteEntra 180ms ease-out;
}

.training-note-editor.sin-animacion {
  /* Al restaurar tras un repintado por SSE, el editor abierto conserva su estado
     sin parpadeo ni repetición de la animación de despliegue (item B19). */
  animation: none;
}

.training-note-editor.saliendo {
  animation: trainingNoteSale 160ms ease-in forwards;
}

@keyframes trainingNoteEntra {
  from { opacity: 0; transform: translateY(-4px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}

@keyframes trainingNoteSale {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-4px) scale(0.98); }
}

@media (prefers-reduced-motion: reduce) {
  .training-note-editor,
  .training-note-editor.saliendo {
    animation: none;
  }
}

.training-note-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
}

.training-note-label {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--modo-entrenamiento);
}

.training-note-counter {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.training-note-textarea {
  width: 100%;
  box-sizing: border-box;
  resize: none;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  line-height: 1.4;
  outline: none;
  transition: border-color 140ms ease;
}

.training-note-textarea:focus {
  border-color: var(--modo-entrenamiento);
}

.training-note-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
}

.training-btn-save {
  background: #734bb0;
  border-color: #734bb0;
  color: #fff;
  font-weight: 600;
}

.training-btn-save:hover {
  background: #845ac4;
  border-color: #845ac4;
  color: #fff;
}

:root[data-theme="light"] .training-btn-save {
  background: #6b3fa0;
  border-color: #6b3fa0;
  color: #fff;
}

:root[data-theme="light"] .training-btn-save:hover {
  background: #572f87;
  border-color: #572f87;
  color: #fff;
}

.training-btn-delete {
  color: var(--danger);
  border-color: transparent;
  margin-right: auto;
}

.training-btn-delete:hover {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}

.msg-status.failed {
  color: var(--danger);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: transparent;
  margin: -2px 0;
  transition: background-color .15s ease, color .15s ease;
}
.msg-status.failed:hover {
  background: var(--bg-hover);
  color: #ff4b5f;
}
.msg-status.failed:focus-visible {
  outline: 2px solid var(--danger);
  outline-offset: 1px;
}

/* Mensaje saliente optimista (item 10): relojito mientras sale, (!) si falla. */
.msg-status.pending .ic {
  width: 13px;
  height: 13px;
}
.bubble.pending { opacity: .92; }
.bubble.pending.error { cursor: pointer; }
.bubble.pending.error .bubble-text { opacity: .8; }

/* --- Formato de WhatsApp dentro de las burbujas (item 7, f_29.png) --- */
.bubble-text b { font-weight: 700; }
.bubble-text i { font-style: italic; }
.bubble-text s { text-decoration: line-through; }
/* Un enlace del chat (item B112): subrayado siempre, como pidió, y con el azul de WhatsApp.
   `overflow-wrap: anywhere` porque una dirección larga no tiene espacios por donde partirse
   y, sin esto, empujaría la burbuja fuera del chat. El cambio al pasar por encima es un
   micro-estado: 120 ms. */
.msg-enlace {
  color: var(--enlace);
  text-decoration: underline;
  text-underline-offset: 2px;
  overflow-wrap: anywhere;
  cursor: pointer;
  transition: text-decoration-thickness 120ms ease, opacity 120ms ease;
}
.msg-enlace:hover { text-decoration-thickness: 2px; }
.msg-enlace:focus-visible { outline: 2px solid var(--enlace); outline-offset: 1px; border-radius: 2px; }
@media (prefers-reduced-motion: reduce) {
  .msg-enlace { transition: none; }
}
/* La ventana que pregunta antes de abrirlo enseña la dirección ENTERA y el dominio resaltado:
   es lo que dice a dónde se va de verdad (un «google.com@otro.sitio» lleva a otro.sitio). */
.enlace-destino {
  display: block;
  margin-top: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--bg-hover);
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: 12px;
  overflow-wrap: anywhere;
  color: var(--text-dim);
}
.enlace-destino b { color: var(--text); font-weight: 700; }
.bubble-text code {
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: .92em;
  background: color-mix(in srgb, currentColor 12%, transparent);
  border-radius: 4px;
  padding: 0 4px;
}
.wa-num,
.wa-bullet {
  display: inline-block;
  min-width: 14px;
  text-align: right;
}
.wa-bullet { font-weight: 700; }

/* Citación: barra vertical y texto atenuado, como en WhatsApp. */
.wa-quote {
  display: inline-block;
  border-left: 3px solid color-mix(in srgb, currentColor 45%, transparent);
  padding-left: 8px;
  opacity: .85;
}

/* --- Divisor "Mensajes no leídos" --- */
.unread-divider {
  align-self: center;
  background: var(--bg-elev);
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  padding: 4px 14px;
  border-radius: 10px;
  margin: 6px 0;
  text-transform: uppercase;
  letter-spacing: .4px;
}

/* --- Carga por lotes e indicador de carga del chat (items 15 y 16) --- */
.load-older {
  align-self: center;
  background: var(--bg-elev);
  color: var(--text-dim);
  border: 1px solid var(--border);
  font-size: 12px;
  font-weight: 600;
  padding: 6px 14px;
  border-radius: 12px;
  margin: 4px 0 8px;
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.load-older:hover {
  background: var(--bg-hover);
  color: var(--text);
}
.chat-loading {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--text-dim);
  font-size: 14px;
}

/* Fallo al cargar la conversación (item 49): se apila para que el botón de reintentar
   quede debajo del aviso y no compita con él en la misma línea. */
.chat-error {
  flex-direction: column;
  gap: 12px;
  text-align: center;
  padding: 0 20px;
}
.chat-error-ic {
  display: inline-flex;
  color: var(--warn);
}
.chat-error-ic .ic {
  width: 26px;
  height: 26px;
}
.spinner {
  width: 18px;
  height: 18px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

/* --- Badge de mensajes no leídos en la lista --- */
.session-meta-right {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}

/* LOS SIN LEER VAN EN ROJO (petición tuya, repetida): el verde de marca es el color de "todo
   en orden" y lo lleva media consola, así que el numerito se perdía entre lo demás. Rojo es
   la convención de "esto está sin atender" en cualquier bandeja, y aquí además coincide con
   el color de las notificaciones, que es lo mismo dicho en otro sitio. */
.unread-badge {
  background: var(--notif);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Marcado "no leído" a mano: círculo verde sin número, como WhatsApp (item 42). */
.unread-badge.dot {
  min-width: 0;
  width: 12px;
  height: 12px;
  padding: 0;
  border-radius: 50%;
}

/* Indicadores de silencio (junto al nombre) y de fijado (junto a la hora). */
.session-name-wrap .mute-ic {
  display: inline-flex;
  color: var(--text-dim);
  flex: 0 0 auto;
}
.session-name-wrap .mute-ic .ic {
  width: 14px;
  height: 14px;
}
.session-meta-right .pin-ic {
  display: inline-flex;
  color: var(--text-dim);
  flex: 0 0 auto;
}
.session-meta-right .pin-ic .ic {
  width: 14px;
  height: 14px;
}

/* Cabecera del chat: nombre clicable para ver la información del contacto. */
.chat-titles.clickable { cursor: pointer; }
.chat-titles.clickable:hover .chat-name { text-decoration: underline; }

/* Menú contextual de un chat: la misma base visual que el de mensajes, solo que más ancho.
   Que flote ya lo dice .msg-menu; aquí estaba repetido. */
.chat-menu {
  min-width: 232px;
}

/* --- Modal de información del contacto --- */
.contact-info {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 4px 0 16px;
}
.contact-name-big {
  font-size: 20px;
  font-weight: 600;
  text-align: center;
}
.contact-number {
  color: var(--text-dim);
  font-size: 15px;
}
.contact-badges { margin-top: 4px; }
.contact-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  border-top: 1px solid var(--border);
  padding-top: 14px;
}
.contact-actions .modal-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  justify-content: flex-start;
}
.contact-actions .modal-btn .ic {
  width: 17px;
  height: 17px;
}

/* --- Menú contextual de mensajes (clic derecho / pulsación larga) --- */
.msg-menu-wrap {
  position: fixed;
  /* POR ENCIMA DE LA MODAL (300): un menú es siempre lo último que se abre, y desde una modal
     —el país del teléfono, un filtro con desplegable— salía DETRÁS de ella. Debajo
     del tooltip (1000), que explica lo que haya encima. */
  z-index: 310;
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* LA ENTRADA VA AQUI, en la UNICA regla de esta clase: dos reglas del mismo nombre donde
     solo una coloca es lo que caza check-clases-duplicadas.py, y con razon — se lee una y se
     da por entera. El porque de la animacion, justo debajo. */
  animation: menuIn var(--dur-pop-in) ease-out;
  transform-origin: top center;
}

/* item 38: el menú de click derecho crece al abrir y se achica al cerrar. Se anima el
   contenedor más externo de cada menú —el wrap del menú de mensaje, o el .msg-menu suelto
   del menú de chat/otros—; el .msg-menu interno del wrap no re-anima. Es animación CSS (no
   disparada desde rAF, que no corre con la pestaña de fondo — escollo ya visto en la F78).

   AQUÍ ESTABA EL FALLO DEL OJITO (item B65bis, reportado dos veces el 22-09: *"cuando se abre
   el tooltip del ojito no tiene transición de apertura, solo de cierre"*).

   El comentario de arriba decía que se anima el wrap… y la regla le quitaba la animación AL
   WRAP TAMBIÉN. O sea que el popover de presencia —que es el único que usa `msg-menu-wrap`—
   no tenía entrada ninguna, y sí salida (`menu-closing`, más abajo). El comentario describía
   la intención y el código hacía otra cosa.

   Ahora el wrap anima al entrar y solo se calla el `.msg-menu` de dentro, que es lo que el
   comentario decía desde el principio: si animaran los dos, el popover crecería dos veces. */
.msg-menu-wrap .msg-menu { animation: none; }
.msg-menu-wrap.menu-closing,
.msg-menu.menu-closing {
  animation: menuOut var(--dur-pop-out) ease forwards;
  pointer-events: none;
}
/* LA ENTRADA SE VE, Y ANTES NO (reportado el 22-09: *"cuando se abre el tooltip del ojito no
   tiene transición de apertura, solo de cierre"*).

   Las dos animaciones EXISTÍAN —medido en el navegador: `menuIn .13s` al abrir y `menuOut
   .12s` al cerrar—, así que no faltaba ninguna: es que la de entrada no se percibía. Dos
   motivos, y los dos se arreglan aquí:

   · CRECÍA DESDE SU CENTRO, así que no contaba de dónde salía: se leía como un fundido y no
     como algo que se despliega desde el botón que acabas de pulsar. Con el origen ARRIBA, el
     popover se abre hacia abajo, que es justo por donde aparece.
   · Y 130 ms es el suelo de la escala. Su regla de siempre son 150-220 ms para paneles; la
     salida se queda más corta a propósito, porque lo que se va tiene que quitarse de en
     medio antes de que lo eches de menos. */
@keyframes menuIn {
  from {
    opacity: 0;
    transform: scale(.9) translateY(-4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@keyframes menuOut {
  from {
    opacity: 1;
    transform: none;
  }
  to {
    opacity: 0;
    transform: scale(.9);
  }
}

/* QUIEN PIDE MENOS MOVIMIENTO NO LO RECIBE, y este fichero no lo respetaba: los menús
   crecían igual. Se quita el desplazamiento y queda el fundido, que es lo que recomienda la
   propia especificación —"reduce", no "elimina"—: sin nada, un menú aparece de golpe y
   tampoco se entiende de dónde salió. */
@media (prefers-reduced-motion: reduce) {
  .msg-menu,
  .msg-menu-wrap,
  .msg-menu-wrap.menu-closing,
  .msg-menu.menu-closing {
    animation-duration: .01ms;
  }
}

/* Bandeja de reacciones rápida. */
.msg-reactions {
  align-self: flex-start;
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 24px;
  padding: 5px 8px;
  box-shadow: 0 8px 24px var(--shadow);
}
.msg-reaction {
  background: transparent;
  border: none;
  cursor: pointer;
  font-size: 22px;
  line-height: 1;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color .12s ease, transform .1s ease;
}
.msg-reaction:hover {
  background: var(--bg-hover);
  transform: scale(1.15);
}
.msg-reaction.active { background: var(--accent-dim); }
.msg-reaction.more { color: var(--text-dim); }
.msg-reaction.more .ic {
  width: 20px;
  height: 20px;
}

/* Lista de opciones.
   FLOTA, Y ESO SE DICE AQUI, no en cada menu que lo use. `openMenu` lo cuelga del <body> y
   `positionFloating` le escribe left/top, que con `position: static` no hacen NADA: el menu
   se queda al final del documento, a lo ancho y debajo de todo.
   Estuvo arreglado DOS VECES por separado -.chat-menu y .msg-menu-wrap, cada una cuando
   alguien la vio torcida- y los que nacieron despues (.quien-pop, el popover de las opciones
   de un mensaje) salieron rotos. Es la misma historia que [hidden] y que :disabled: la regla
   va en el elemento, no en sus usos. Lo vigila scripts/check-menu-flotante.py. */
.msg-menu {
  min-width: 210px;
  position: fixed;
  z-index: 310; /* por encima de la modal: ver .msg-menu-wrap */
  /* La animacion de entrada vivia en una regla `.msg-menu` aparte, mas arriba. Dos reglas
     para el mismo nombre donde solo una coloca es lo que caza check-clases-duplicadas.py, y
     tiene razon: se lee una y se da por entera. */
  animation: menuIn var(--dur-pop-in) ease-out;
  /* De dónde CRECE. Se abre hacia abajo, que es donde lo coloca `positionFloating` en la
     mayoría de los casos (`below: true`). Ver el comentario de @keyframes menuIn. */
  transform-origin: top center;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 10px 30px var(--shadow);
  padding: 6px;
}
/* Dentro de un envoltorio, el que flota es el ENVOLTORIO: es a el a quien posiciona
   `positionFloating`, y el menu vuelve a ser una caja mas de su columna (debajo de la
   bandeja de reacciones). Si tambien flotara, se saldria de esa columna. */
.msg-menu-wrap .msg-menu {
  position: static;
  z-index: auto;
}
.msg-menu-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 9px 12px;
  border: none;
  background: transparent;
  color: var(--text);
  font-size: 14px;
  cursor: pointer;
  border-radius: 8px;
  text-align: left;
  transition: background-color var(--dur-hover) ease;
}
.msg-menu-item:hover { background: var(--bg-hover); }
.msg-menu-item.danger { color: var(--danger); }
.msg-menu-ic {
  display: inline-flex;
  color: var(--text-dim);
  flex: 0 0 auto;
}
.msg-menu-item.danger .msg-menu-ic { color: var(--danger); }
.msg-menu-ic .ic {
  width: 18px;
  height: 18px;
}
/* BORDE Y NO FONDO: un fondo de 1px cae en posiciones con decimales cuando la página tiene
   zoom y cada línea sale de un grosor distinto (06-10); el navegador ajusta los bordes a
   píxeles enteros, así que todas miden lo mismo. */
.msg-menu-sep {
  height: 0;
  border-top: 1px solid var(--border);
  margin: 5px 8px;
}

/* Selector completo de reacciones (emoji). */
.reaction-picker {
  position: fixed;
  z-index: 500;

  /* por encima del lightbox (400) al reaccionar desde ahí (item 32) */
  width: 300px;
  max-height: 240px;
  overflow-y: auto;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 8px 24px var(--shadow);
  padding: 8px;
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 2px;
  animation: menuIn .12s ease;
}

/* --- Cita de respuesta dentro de la burbuja --- */
.reply-quote {
  display: flex;
  flex-direction: column;
  gap: 1px;
  border-left: 3px solid var(--accent);
  background: rgba(0, 0, 0, .12);
  border-radius: 5px;
  padding: 4px 8px;
  margin-bottom: 5px;
  max-width: 100%;
  cursor: pointer;

  /* doble-clic salta al mensaje original */
}
:root[data-theme="light"] .reply-quote { background: rgba(0, 0, 0, .05); }

/* Destello breve al saltar a un mensaje (desde la cita de respuesta). */
@keyframes bubble-flash {
  0% { box-shadow: 0 0 0 3px var(--accent-strong); }
  100% { box-shadow: 0 0 0 3px transparent; }
}
.bubble.flash { animation: bubble-flash 1.1s ease-out; }
.reply-quote-author {
  font-size: 11px;
  font-weight: 600;
  color: var(--accent);
}
.reply-quote-text {
  font-size: 12.5px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* item 27 (f_32): la cita de una foto o un video lleva su miniatura a la derecha. La cita
   pasa a fila; el cuerpo (autor + texto) se apila dentro y cede el ancho a la miniatura. */
.reply-quote.with-thumb {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  padding-right: 4px;
}
.reply-quote-body {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}
.reply-quote:not(.with-thumb) .reply-quote-body { display: contents; }
.reply-quote-thumb {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 4px;
  overflow: hidden;
  background: rgba(0, 0, 0, .18);
}
.reply-quote-thumb img,
.reply-quote-thumb video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* --- Chip de reacción sobre la burbuja --- */
.bubble.has-reaction { margin-bottom: 14px; }
.bubble-reaction {
  position: absolute;
  bottom: -12px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1px 5px;
  font-size: 13px;
  line-height: 1.4;
  box-shadow: 0 2px 5px var(--shadow);
}
.bubble.client .bubble-reaction,
.bubble.bot .bubble-reaction {
  left: 8px;
}
.bubble.operator .bubble-reaction { right: 8px; }

/* item 13: con más de una reacción, el número va a la derecha de los emojis. */
.reaction-count {
  margin-left: 3px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-dim);
  vertical-align: middle;
}

/* --- Mensaje eliminado por el cliente (estilo WhatsApp) --- */
.bubble.deleted .bubble-text.deleted-text {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-dim);
  font-style: italic;
}
.bubble.deleted .deleted-text .ic {
  width: 15px;
  height: 15px;
}

/* --- Marcadores (editado / reenviado) --- */
.edited-tag {
  color: var(--text-dim);
  font-style: italic;
  margin-right: 2px;
}

/* item 33: estrellita del mensaje destacado, junto a la hora. Rellena para que se lea de
   un vistazo entre los checks y el texto. */
.star-tag {
  display: inline-flex;
  color: var(--text-dim);
  flex: 0 0 auto;
}
.star-tag .ic {
  width: 12px;
  height: 12px;
  fill: currentColor;
}

/* Indicador de plantilla de Meta y mensaje rápido: icono + etiqueta + viñeta, antes de la hora. */
.bubble-plantilla,
.bubble-rapido,
.bubble-integracion {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  color: inherit;
  flex: 0 0 auto;
  font-size: 10px;
  cursor: pointer;
  user-select: none;
  opacity: 0.9;
  transition: opacity .15s ease;
}
.bubble-plantilla:hover,
.bubble-rapido:hover,
.bubble-integracion:hover {
  opacity: 1;
}
.bubble-plantilla:focus-visible,
.bubble-rapido:focus-visible,
.bubble-integracion:focus-visible {
  outline: 1px dotted currentColor;
  outline-offset: 1px;
}
.bubble-plantilla .ic,
.bubble-rapido .ic,
.bubble-integracion .ic {
  width: 11px;
  height: 11px;
}
/* La insignia de la integración lleva el NOMBRE, que puede ser largo: se recorta en una línea
   para no descolocar la hora. El nombre completo sale en el tooltip. */
.bubble-integracion > span {
  max-width: 16ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Un mensaje de un sistema externo lleva por dentro el rol del bot, pero no es el bot: una
   franja lateral lo distingue de una respuesta del bot sin depender del color (la insignia y
   el autor ya dicen quién es). */
.bubble.de-integracion {
  box-shadow: inset 3px 0 0 var(--info-border);
}
/* Plantilla que no se pudo leer del catálogo: el aviso en gris, debajo de su nombre. */
.bubble-plt-falta {
  display: block;
  font-size: 12px;
  font-style: italic;
  /* Hereda el color de la burbuja: el gris de texto apagado no se lee sobre el verde (NO_REPETIR 67). */
  color: inherit;
  opacity: .8;
}
.meta-sep {
  opacity: 0.6;
  margin: 0 1px;
  font-size: 10px;
  user-select: none;
}
/* DOS FILAS SI NO CABE: el origen (difusión, plantilla, rápido) arriba y la hora con su check
   abajo, a la derecha. En una burbuja estrecha la hora se partía en «09:45 / p. m.». */
.bubble .meta {
  flex-wrap: wrap;
  row-gap: 2px;
}
.meta-origen {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 3px;
  min-width: 0;
}
/* La viñeta de la última insignia separaba de la hora; ahora las separa el hueco, y así no
   queda colgando al bajar la hora a su fila. */
.meta-origen > .meta-sep:last-child {
  display: none;
}
.meta-hora {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: auto;
  padding-left: 6px;
  white-space: nowrap;
}

/* El botón de la tarjeta dentro de un globo de entrenamiento va en morado, como el resto del
   globo. El hover ya sale de `currentColor` (message.css), así que también queda morado. */
.bubble.bot:has(.msg-status.training) .wa-cta {
  color: var(--modo-entrenamiento);
}

/* --- Resumen de herramientas del bot bajo el globo (item activo) ------------
   Línea corta y atenuada que informa qué herramientas ejecutó el bot en su turno
   sin invadir el diseño interior del globo de WhatsApp (NO_REPETIR 35). */
.bot-tool-summary {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: 4px;
  margin-top: 3px;
  padding: 2px 6px;
  border-radius: 6px;
  border: none;
  background: transparent;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 11px;
  line-height: 1.3;
  cursor: pointer;
  text-align: left;
  user-select: none;
  transition: color 140ms ease, background-color 140ms ease;
  animation: botToolSummaryEntra 180ms ease-out;
}

.bot-tool-summary:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--text-dim) 15%, transparent);
}

.bot-tool-summary:focus-visible {
  color: var(--text);
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.bot-tool-summary .ic {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  opacity: 0.85;
}

@keyframes botToolSummaryEntra {
  from {
    opacity: 0;
    transform: translateY(-2px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bot-tool-summary {
    animation: none;
  }
}
