/* Spoter Design System — Component classes (piloto Conversaciones)
   CORRECCIÓN 2026-07-22 (Flor): este header decía "Traducidos desde
   components/{forms/IconButton,data-display/Tag,data-display/Badge}.jsx" — ese .jsx nunca existió
   en este repo. Spoter es CakePHP + Bootstrap + JS plano/jQuery, no hay React en ningún lado del
   proyecto. La fuente de verdad real de estos componentes son los elements de CakePHP
   (src/Template/Element/DesignSystem/{iconButton,tag,badge}.ctp) + este CSS — no un componente
   .jsx externo. Coincide con lo ya aclarado en guidelines/ds-audit.md línea 4 ("No hay fuente
   externa (.jsx / docs previos) que se deba importar — confirmado con Flor"); este archivo se
   había quedado con el comentario viejo sin corregir.
   Solo referencian tokens de tokens.css (o, donde el diseño original usaba paleta cruda para el
   hover de "primary", el token de paleta correspondiente). */

/* ── IconButton ───────────────────────────────────────── */
.sp-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex: none;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}

.sp-icon-btn:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

.sp-icon-btn.sp-icon-btn-round {
  border-radius: 50%;
}

.sp-icon-btn-sm {
  width: 30px;
  height: 30px;
  font-size: 15px;
}

.sp-icon-btn-lg {
  width: 44px;
  height: 44px;
}

.sp-icon-btn-base {
  background: transparent;
  color: var(--text-secondary);
}

.sp-icon-btn-base:hover:not(:disabled) {
  background: var(--surface-hover);
  color: var(--text-primary);
}

.sp-icon-btn-primary {
  background: var(--accent-primary); /* 2026-07-30: era var(--indigo-900) fijo — no invertía a pink en dark, a diferencia de .btn-primary (que ya usa var(--bs-primary), theme-aware) */
  color: var(--text-on-brand);
}

.sp-icon-btn-primary:hover:not(:disabled) {
  background: var(--btn-primary-hover-bg); /* 2026-07-30: era var(--indigo-800) fijo — mismo fix que .btn-primary:hover, ver theme-{light,dark}.css */
  color: var(--text-on-brand);
}

.sp-icon-btn-pink {
  background: var(--pink-500);
  color: var(--text-on-brand);
}

.sp-icon-btn-pink:hover:not(:disabled) {
  background: var(--pink-600);
  color: var(--text-on-brand);
}

/* 2026-08-14: nace para #btn-cancelar-plan (.sp-card-ai, "Plan de Acción IA") — 1er consumidor real
   de .sp-icon-btn en todo el repo (ver spec.json de IconButton, usedIn corregido). Transparente en
   reposo como -base, pero ícono en --status-danger — mismo criterio semántico que ya usa
   --stage-cancelado. Contraste verificado (WCAG, ícono gráfico 3:1): 4.53:1 claro / 6.03:1 oscuro
   contra --surface-default, hover 3.57:1 / 5.49:1 contra --status-danger-bg. Reemplaza el antipatrón
   disperso de `text-danger` + ícono crudo (>15 ocurrencias relevadas, fuera de alcance de hoy). */
.sp-icon-btn-danger {
  background: transparent;
  color: var(--status-danger);
}

.sp-icon-btn-danger:hover:not(:disabled) {
  background: var(--status-danger-bg);
  color: var(--status-danger);
}

.sp-icon-btn-white {
  /* --grey-0 (no --surface-default): esta variante debe quedar blanca SIEMPRE, incluso en dark
     theme (pensada para superficies con sombra propia, ver DIR-ICONBUTTON-07) — --surface-default
     cambia a un tono oscuro en dark, --grey-0 es el valor crudo de paleta, no semántico, y no se
     remapea por tema. 2026-07-22, corrección de color hardcodeado (#fff → token). */
  background: var(--grey-0);
  color: var(--grey-700);
  border-color: var(--border-default);
  box-shadow: var(--shadow-xs);
}

.sp-icon-btn-white:hover:not(:disabled) {
  background: var(--grey-25);
  color: var(--grey-900);
  box-shadow: none;
}

/* ── Tag ──────────────────────────────────────────────── */
.sp-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 24px;
  padding: 0 10px;
  font-family: var(--font-ui);
  font-size: var(--text-caption);
  font-weight: 500;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}

.sp-tag-indigo {
  background: var(--indigo-100);
  color: var(--indigo-900);
}

.sp-tag-pink {
  background: var(--pink-50);
  color: var(--pink-700);
}

.sp-tag-mint {
  /* Corrección 2026-07-22: mismos valores de antes, ahora con token (mint-50/mint-700, agregados a
     tokens.css el mismo día — no existían como par nombrado, solo como hex crudo acá). */
  background: var(--mint-50);
  color: var(--mint-700);
}

.sp-tag-grey {
  background: var(--surface-sunken);
  color: var(--text-muted);
}

.sp-tag-amber {
  background: var(--status-warning-bg);
  color: var(--text-on-warning);
}

/* ── Badge ────────────────────────────────────────────── */
.sp-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 7px;
  font-family: var(--font-ui);
  font-size: var(--text-micro);
  font-weight: 700;
  line-height: 1;
  border-radius: var(--radius-pill);
}

.sp-badge-neutral {
  background: var(--surface-sunken);
  color: var(--text-muted); /* era --text-secondary, 4.46:1 en claro (no llega a AA) — DIR-BADGE-04. Mismo token que ya usa .sp-tag-grey para este par fondo/texto. */
}

.sp-badge-primary {
  background: var(--indigo-900);
  color: var(--text-on-brand);
}

.sp-badge-pink {
  background: var(--pink-500);
  color: var(--text-on-brand);
}

.sp-badge-success {
  background: var(--status-success-bg);
  color: var(--text-on-success); /* era --status-success, 2.76:1 en claro (no llega a AA) — DIR-BADGE-04. */
}

.sp-badge-danger {
  background: var(--status-danger);
  color: var(--text-on-brand);
}

/* En dark, --status-danger es --red-bright (pensado para íconos/acentos visibles sobre fondo
   oscuro) — como fondo SÓLIDO de badge, con texto blanco encima, da 2.82:1 (no llega a AA,
   DIR-BADGE-04). Fix a pedido de Flor: oscurecer el rojo del badge en vez de oscurecer el texto,
   para mantener la lectura "blanco sobre rojo" típica de danger. Reusa --red-600 (ya en la
   paleta) en vez de tocar --status-danger global, que sigue necesitando ser brillante en sus
   otros usos (íconos, --stage-cancelado, etc.). 6.33:1 con texto blanco. */
[data-theme="dark"] .sp-badge-danger,
.dark .sp-badge-danger {
  background: var(--red-600);
}

.sp-badge-warning {
  background: var(--status-warning-bg);
  color: var(--text-on-warning);
}

.sp-badge-info {
  background: var(--status-info-bg);
  color: var(--status-info);
}

/* ── JsonList (lista con bullets para arrays de la IA — Riesgos, Verificaciones del operador) ──
   2026-08-04 (Flor, Panel de Control): el <ul><li> que arma panel-control.js
   (_renderizarValorJson, genérico para cualquier campo JSON tipo array del plan de acción IA) no
   mostraba el marcador de bullet — hay un reset global en custom.css (`li { list-style-type: none
   !important; }`, línea ~21) que saca el bullet de TODOS los <li> de la app (necesario en
   menús/dropdowns, no se toca). Esta clase reactiva el bullet SOLO en listas explícitamente
   marcadas así — hoy Riesgos y Verificaciones del operador, y cualquier otro array JSON futuro que
   use la misma función de render. */
.sp-json-list > li {
  list-style-type: disc !important;
}

.sp-json-list > li::marker {
  color: var(--text-secondary);
}

/* ── IconMenuItem (ícono + etiqueta, acción secundaria en grilla) ──
   Nace del drawer "Opciones" (Element/Modals/opciones.ctp, clase .setting-option,
   ~19 usos) y se generaliza a propósito para reusarse en la grilla del panel de
   cliente (Bloque 3: Comprobantes/Eventos/Notas/Tickets), que hoy solo existe como
   mockup. .setting-option sigue existiendo solo para layout (ancho 25%, wrap) y
   como selector del filtro de búsqueda en whatsapp.js — no se toca ni se elimina.
   Antes: color de texto = var(--bs-gray-light), el mismo token que --text-placeholder
   (grey-300, #abb0c1) — por eso se leía "inhabilitado". Ahora usa --text-secondary,
   pensado para texto/ícono accionable de menor jerarquía (mismo contrato que
   .sp-icon-btn-base). Hover: antes background muy sutil (--surface-app, el fondo
   general de la página) + border-radius 50% (círculo forzado sobre un bloque
   rectangular ícono+texto). Ahora --surface-hover (el token pensado para hover,
   no el de fondo de página) + --radius-lg (12px), que da un "squircle" prolijo
   sobre un contenido que no es circular — más coherente que forzar un 50%. */
/* 2026-08-04: el color quedaba sin aplicar en el Bloque 3 del panel de cliente — ahí
   .sp-icon-menu-item va directo en el <a> (necesario para el data-bs-toggle="dropdown"
   de Bootstrap), no en un <div> que envuelve un <a> como en Opciones. El selector
   ".sp-icon-menu-item > a" nunca matcheaba en ese caso y el ícono quedaba con el color
   de link por defecto de Bootstrap. Se agrega el selector propio (sin >a) en paralelo,
   cubre los dos usos reales del componente. */
.sp-icon-menu-item,
.sp-icon-menu-item > a {
  color: var(--text-secondary);
}

.sp-icon-menu-item:hover {
  background: var(--surface-hover);
  border-radius: var(--radius-lg);
}

.sp-icon-menu-item:hover,
.sp-icon-menu-item:hover > a {
  color: var(--accent-primary);
}

/* Variante "indexed": ícono con contador superpuesto (grilla del panel de cliente, bloque 3).
   Reusa el componente Badge ya documentado (.sp-badge/.sp-badge-primary) en vez de inventar un
   estilo de número nuevo — solo se agrega el posicionamiento absoluto sobre el ícono. */
.sp-icon-menu-item-icon {
  position: relative;
  display: inline-flex;
}

.sp-icon-menu-item-icon > .sp-badge {
  position: absolute;
  top: -5px;
  right: -12px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  font-size: 9px;
}

/* ── Bootstrap .btn-outline-secondary — override de color únicamente ────
   Pedido de Flor 2026-07-20: "solo quería cambiar el color" — no reemplazar la clase de Bootstrap
   por una propia (eso cambiaba tamaño/padding/radio y se notaba como una forma distinta, no solo
   un color distinto). Este override toca solo color/borde/fondo, deja intactos tamaño, padding,
   radio, focus-ring y disabled de Bootstrap. Aplica automáticamente a las ~28 apariciones ya
   existentes en 13 pantallas sin tocar ningún .ctp — el problema (gris casi invisible sobre blanco,
   se leía como deshabilitado) era de contraste, no de forma.

   Corregido 2026-07-21 (causa raíz real, no solo el token): el fix anterior usaba el selector
   `.btn-outline-secondary` (1 clase, especificidad 0,1,0). `styles-light.min.css` (Bootstrap
   compilado, carga ANTES que este archivo) trae `.btn.btn-outline-secondary` (2 clases,
   especificidad 0,2,0) con `border-color:#e9eaf1;color:#dee0e8` hardcodeado — más específico que mi
   regla, así que ganaba la cascada sin importar qué token le pusiera acá. Por eso el hover sí se veía
   bien (esa regla mía ya tenía más pseudo-clases, ganaba) pero el estado normal seguía gris/lavanda
   pálido pase lo que pase. Selector ahora igualado a `.btn.btn-outline-secondary` (misma
   especificidad, gana por orden de carga posterior) — recién ahí el token --border-strong
   corregido (grey-500, 4.04:1) se ve reflejado de verdad. */
.btn.btn-outline-secondary {
  color: var(--text-secondary);
  border-color: var(--border-accent);
}

.btn-outline-secondary:hover:not(:disabled):not(.disabled) {
  color: var(--text-primary);
  background-color: var(--surface-hover);
  border-color: var(--text-secondary);
}

/* Íconos dentro del botón — styles-light.min.css trae `.btn.btn-outline-secondary i{color:#dee0e8}`
   (mismo tono pálido que el texto/borde original), con especificidad 2 clases + elemento: gana a
   `color` heredado del botón igual que pasaba con el texto. Mismo criterio de especificidad
   igualada que arriba (ver nota de components.css 2026-07-21). */
.btn.btn-outline-secondary i {
  color: var(--text-secondary);
}

.btn-outline-secondary:hover:not(:disabled):not(.disabled) i {
  color: var(--text-primary);
}

/* ── ViewSwitch — grupo de vistas (Tickets: Integrada/Kanban/Embudo/Backlog) ──
   Nace 2026-07-23, pedido de Flor: "la vista activa debe diferenciarse del resto". Antes las 4
   se veían casi iguales — la única diferencia era un efecto lateral no intencional en
   Element/barraTickets.ctp (a la activa se le olvidaba aplicar el style inline de color).
   Clase propia (no se tocó el `.btn.btn-outline-secondary.active` genérico de Bootstrap/qr.css —
   ese selector es demasiado amplio, cambiar su estilo hubiera afectado cualquier otro
   .btn-outline-secondary.active sin auditar en el resto de la app). Mismo criterio de "fondo
   sólido = seleccionado" que ya usan .sp-icon-btn-primary/.sp-badge-primary. */
.sp-view-switch-btn.active {
  background: var(--indigo-900);
  border-color: var(--indigo-900);
  color: var(--text-on-brand);
}

.sp-view-switch-btn.active i,
.sp-view-switch-btn.active span {
  color: var(--text-on-brand);
}

/* Botón "confirmar acción sugerida por IA" ────────────
   Nace 2026-07-17 en la card Recomendación IA (botón "Resolver"). RESERVADO a propósito para ese
   único caso de uso — confirmar/aceptar una acción que la IA sugirió — no es un botón primario
   genérico. Ajustado en la ronda de comparación con el mockup de referencia: color sólido
   --accent-primary (indigo), NO el gradiente --gradient-ai-action — ese gradiente quedó reservado
   para el ícono del título de la card únicamente, el botón usa el mismo azul/indigo sólido de la
   propuesta intermedia que Flor aprobó. */
.sp-btn-ai-confirm {
  background: var(--accent-primary);
  color: var(--text-on-brand);
  border: none;
}

.sp-btn-ai-confirm:hover {
  filter: brightness(0.95);
}

/* Excepción puntual al criterio de arriba — pedido explícito de Flor 2026-07-29 para el botón de
   acción primaria del Stepper (plan_de_accion, ej. "Enviar Respuesta Sugerida"): ahí SÍ usa el
   gradiente --gradient-ai-action como relleno, no el sólido --accent-primary. Es una excepción
   contemplada para este caso puntual, no un cambio de criterio general — "Resolver" (card
   Recomendación IA) y cualquier otro consumidor existente de .sp-btn-ai-confirm sigue sólido, no se
   toca. Si aparece un tercer caso que también pida gradiente, ahí sí conviene revisar si el criterio
   general debería invertirse en vez de seguir sumando excepciones una por una. */
.sp-btn-ai-confirm-gradient {
  background: var(--gradient-ai-action);
  color: var(--text-on-brand);
  border: none;
}

.sp-btn-ai-confirm-gradient:hover {
  filter: brightness(1.05);
}

/* Foco de teclado — gap encontrado en la autoauditoría de Flor 2026-07-29: el botón dependía del
   outline default del navegador, sin verificar que se note sobre el gradiente. :focus-visible (no
   :focus) para que el anillo aparezca solo con teclado, no en cada click de mouse — mismo criterio
   que --shadow-focus-indigo ya define en tokens.css (primer consumidor real de ese token). */
.sp-btn-ai-confirm-gradient:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus-indigo);
}

/* ── Card "look de IA" ──
   Nace 2026-07-29 para retocar #resolver-resultado (PanelControl/index.ctp, card "Recomendación IA").
   Línea de acento de 3px arriba = --gradient-ai-action, sin cambios desde el origen — es la que lleva
   el peso de marca IA. El FONDO pasó por 3 versiones (ver Changelog): 1) --gradient-ai-card pastel,
   idéntico en los 2 temas → rompía contraste de texto en dark (1.02:1–1.4:1). 2) --gradient-ai-card-subtle,
   token nuevo theme-aware → rechazado en demo del 2026-08-14: los badges/tags anidados (fondo
   --surface-sunken) no se recortaban del fondo de la card por quedar en el mismo valor tonal.
   3) Actual: fondo plano --surface-default (mismo token que cualquier card normal → los chips vuelven
   a contrastar) + box-shadow inset en los mismos tonos de --gradient-ai-action (indigo-900/pink-500,
   mismo triplete que --shadow-focus-indigo/--pink-500-rgb, ningún hex nuevo) como señal de "esto es
   IA" en vez de un relleno de color. 3 propuestas de sombra mockeadas, esta (glow bajo la línea de
   acento) fue la elegida. --gradient-ai-card-subtle quedó sin uso — removido de tokens.css. */
.sp-card-ai {
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-default);
  background-color: var(--surface-default);
  background-image: var(--gradient-ai-action);
  background-repeat: no-repeat;
  background-size: 100% 3px;
  background-position: top;
  overflow: hidden;
  box-shadow: var(--shadow-sm),
              inset 0 10px 18px -8px rgba(228, 45, 127, 0.35),
              inset 0 -8px 16px -10px rgba(46, 36, 90, 0.30);
}

/* 2026-08-14: padding-bottom pasa de 0 a --space-2 — Flor comparó este header contra el de
   "Resumen Ejecutivo" (.card-header de Bootstrap, py-2 simétrico) y el ícono de recalcular quedaba
   pegado arriba del todo acá (0 abajo vs simétrico allá), aunque el botón en sí ya era idéntico
   (mismo .sp-icon-btn, mismo hover). No se tocó --space-4 de .sp-card-ai-body para no duplicar el
   espacio — 8px acá + 16px de .sp-card-ai-body alcanza, verificado por render que no queda hueco de
   más antes de "Criticidad: Bajo". */
.sp-card-ai-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4) var(--space-2);
  font-size: var(--text-caption);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--accent-primary);
}

.sp-card-ai-body {
  padding: var(--space-4);
}

/* ── Dialog (Modal) — reemplazo de confirm()/prompt() nativos del navegador ──
   Nace 2026-07-21 a pedido de Flor. Reusa el Modal de Bootstrap (foco/ESC/backdrop-click, ya
   cargado global) — solo override visual, sin reinventar la mecánica de overlay. Ver ds-dialog.js.
   alert() nativo bloqueante → dsAlert() (mismo .sp-dialog, un botón). msgAlert()/ds-alert sigue
   siendo el camino para feedback no bloqueante. Ver ds-audit.md 2026-08-11.

   z-index: primer consumidor real de --z-modal/--z-modal-backdrop (tokens.css, mismo día). Se
   sobreescribe GLOBAL (no solo .sp-dialog) a propósito: Bootstrap trae .modal/.modal-backdrop en
   ~1055/1050, muy por debajo de #nav-menu (9999, custom.css) — mismo bug de fondo que ya se corrigió
   puntualmente para #cola-drawer (PanelControl/index.ctp) y que motivó la escala de z-index nueva.
   Sube TODOS los modales Bootstrap existentes (~36 candidatos GAP-DS en Modals/, ver ds-audit.md),
   no solo el Dialog nuevo — beneficio, no riesgo: un overlay nunca se ve peor por quedar más arriba. */
.modal-backdrop {
  z-index: var(--z-modal-backdrop);
}

.modal {
  z-index: var(--z-modal);
}

/* Mismo bug, componente distinto — 2026-07-29: #cola-offcanvas (PanelControl/index.ctp, la cola
   priorizada convertida a offcanvas de Bootstrap) quedaba atrás del navbar principal. .offcanvas es
   un componente separado de .modal en Bootstrap, con su propio z-index (~1045/1040 por default) —
   el fix de arriba no lo cubre. Mismo criterio: reusa los tokens de modal (un offcanvas es
   conceptualmente la misma capa, un overlay que debe quedar arriba de todo el chrome de la app),
   sube TODOS los offcanvas existentes, no solo el nuevo. */
.offcanvas-backdrop {
  z-index: var(--z-modal-backdrop);
}

.offcanvas {
  z-index: var(--z-modal);
}

.sp-dialog .modal-content {
  border: none;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
}

/* 2026-08-04 (Flor): preguntó si al Dialog le falta el modal-header que sí llevan otros modales de
   la app. No hay ninguna regla del DS que exija header en todo modal — la convención documentada
   (retrofit de .modal-title, 2026-07-23) solo dice que CUALQUIER .modal-title que exista debe
   llevar ícono antes del texto; no dice que todo modal deba tener uno. Este Dialog nace a propósito
   sin header (mismo criterio minimalista que el confirm()/prompt() nativo que reemplaza — mensaje +
   2 botones, nada más). Como no lleva header, se le sube el padding-top específicamente (24px→32px,
   --space-6→--space-8) para que el mensaje no arranque pegado al borde redondeado de arriba — el
   resto del padding (lados/abajo) queda igual.
   2do intento (Flor probó y no vio el cambio): custom.css tiene `.modal-body { padding-top: 6px
   !important; }` GLOBAL (reduce el padding gigante que trae qr.css vendor, 4rem, para el resto de
   los modales de la app). Un selector más específico sin !important NO le gana a un !important,
   sin importar cuántas clases tenga — hace falta !important acá también, mismo patrón que
   feedback_bootstrap_compound_selector_specificity. */
.sp-dialog .modal-body {
  padding: var(--space-8) var(--space-6) var(--space-4) !important;
}

.sp-dialog-message {
  color: var(--text-primary);
  font-size: var(--text-body);
}

.sp-dialog-input {
  margin-top: var(--space-3);
}

.sp-dialog .modal-footer {
  border-top: none;
  padding: 0 var(--space-6) var(--space-6);
}

.sp-dialog-btn-primary {
  background: var(--accent-primary);
  color: var(--text-on-brand);
  border: none;
}

.sp-dialog-btn-primary:hover {
  filter: brightness(0.95);
  color: var(--text-on-brand);
}

.sp-dialog-btn-danger {
  background: var(--status-danger);
  color: var(--text-on-brand);
  border: none;
}

.sp-dialog-btn-danger:hover {
  filter: brightness(0.92);
  color: var(--text-on-brand);
}

/* ── Skeleton ──
   Nace 2026-07-29 a pedido de Flor: "Ver conversación" (reabrir el chat) y el panel de cliente
   arrancaban en blanco o con texto plano "Cargando..." — reemplazado por un shimmer real. Genérico
   a propósito (no es solo para PanelControl) — cualquier pantalla que necesite un estado de carga
   puede armar su forma combinando estos bloques, no hace falta un componente nuevo por caso de uso. */
.sp-skeleton {
  background: linear-gradient(90deg, var(--surface-sunken) 25%, var(--border-default) 37%, var(--surface-sunken) 63%);
  background-size: 400% 100%;
  animation: sp-skeleton-shimmer 1.4s ease infinite;
  border-radius: var(--radius-sm);
}

@keyframes sp-skeleton-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

.sp-skeleton-text { height: 11px; margin-bottom: var(--space-2); }
.sp-skeleton-title { height: 15px; margin-bottom: var(--space-3); }
.sp-skeleton-avatar { width: 38px; height: 38px; border-radius: var(--radius-pill); flex-shrink: 0; }
.sp-skeleton-block { height: 70px; margin-bottom: var(--space-3); }
.sp-skeleton-chip { height: 22px; width: 90px; border-radius: var(--radius-pill); display: inline-block; margin-right: var(--space-2); }

/* Burbujas de chat — alterna alineación como una conversación real para que se lea como "esto va a
   ser un chat", no un bloque genérico. */
.sp-skeleton-bubble { height: 34px; border-radius: var(--radius-md); margin-bottom: var(--space-2); }
.sp-skeleton-bubble.in { width: 70%; border-bottom-left-radius: 2px; }
.sp-skeleton-bubble.out { width: 55%; margin-left: auto; border-bottom-right-radius: 2px; }

/* ── Stepper / Timeline ──
   Nace 2026-07-29 a pedido de Flor (rediseño Panel de Control — plan_de_accion de MASS,
   AiComponent::resolverPanelControl). Confirmado por grep en toda webroot/css antes de escribir esto:
   no existía NINGÚN componente de stepper/timeline en la base — es el primer gap 100% nuevo de esta
   ronda de trabajo (a diferencia de Card/Badge de la sección "Recomendación IA" arriba, que ya
   existían y solo faltaba aplicarles la receta correcta).

   Dos variantes de layout, misma estructura de estados (comparten .sp-action-step/.sp-action-step-num):
   - .sp-action-stepper-horizontal: fila de círculos conectados por una línea, label debajo. Pensado para
     progreso tipo pipeline (reuso sugerido: "Renovación Plan anual" del panel de cliente, hoy
     resuelto ahí con markup propio sin tokens — candidato a migrar a este componente después).
   - .sp-action-stepper-vertical: línea a la izquierda, contenido libre a la derecha de cada número. Pensado
     para checklist de pasos con contenido rico (plan_de_accion: label + resultado esperado + mensaje
     sugerido + botón, o el aviso de bloqueado).

   Estados (clase adicional en .sp-action-step): sin clase = pendiente/bloqueado, .active = paso actual,
   .done = completado. Mismo criterio que .sp-view-switch-btn.active ("fondo sólido = estado
   marcado"), pero acá el color de "active" usa --accent-primary (variable semántica) en vez de fijar
   --indigo-900 como hace .sp-view-switch-btn.active a propósito — ahí la decisión fue "marca fija en
   los 2 temas", acá es solo progreso/estado, no una superficie de marca, así que hereda el swap a
   pink-500 en dark como el resto de los acentos semánticos (--border-focus, etc.).
   "done" usa --status-success (no --accent-primary) para diferenciarse visualmente de "active" —
   mismo verde que ya usa el resto de la app para "completado/resuelto".

   Bloqueado: no es una clase de estado del número (ya cubierto por "sin clase = pendiente"), es un
   bloque de contenido propio (.sp-action-step-locked) para el texto de condición — candado + texto en
   --text-secondary sobre --surface-sunken, mismo tratamiento que .sp-tag-grey.

   Mobile (media query al final del bloque): en vez de duplicar markup, la horizontal esconde el label
   de los pasos no-activos y achica el diámetro del punto — evita que 4-5 pasos con label largo
   fuercen scroll horizontal en una pantalla angosta. La vertical no necesita ajuste de layout (ya es
   de una columna), solo reduce paddings. */

.sp-action-stepper-horizontal,
.sp-action-stepper-vertical {
  --sp-action-step-dot: 22px; /* 2026-07-29: -4px a pedido de Flor tras ver el demo (26px se sentía grande) */
  --sp-action-step-line: 2px;
  font-family: var(--font-ui);
}

.sp-action-step-num {
  width: var(--sp-action-step-dot);
  height: var(--sp-action-step-dot);
  min-width: var(--sp-action-step-dot);
  border-radius: var(--radius-pill);
  background: var(--surface-sunken);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-micro);
  font-weight: 700;
  z-index: 1;
}

.sp-action-step.active .sp-action-step-num {
  background: var(--accent-primary);
  color: var(--text-on-brand);
}

.sp-action-step.done .sp-action-step-num {
  background: var(--status-success);
  color: var(--text-on-brand);
}

.sp-action-step-label {
  font-size: var(--text-body-sm);
  font-weight: 600;
  color: var(--text-primary);
}

.sp-action-step-result {
  font-size: var(--text-caption);
  color: var(--text-secondary);
  margin-bottom: var(--space-2);
}

.sp-action-step-locked {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  background: var(--surface-sunken);
  color: var(--text-secondary);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-caption);
  line-height: var(--leading-normal);
}

.sp-action-step-locked i {
  margin-top: 2px;
  color: var(--text-placeholder);
}

/* Horizontal */
.sp-action-stepper-horizontal {
  display: flex;
  align-items: flex-start;
}

.sp-action-stepper-horizontal .sp-action-step {
  flex: 1;
  position: relative;
  text-align: center;
  padding-top: calc(var(--sp-action-step-dot) + var(--space-2));
}

.sp-action-stepper-horizontal .sp-action-step-num {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
}

/* Línea conectora centro-a-centro: arranca en -50% del step actual y mide 100% de su ancho, así
   la punta cae siempre en el centro del dot anterior/siguiente, no en su borde. */
.sp-action-stepper-horizontal .sp-action-step:not(:first-child)::before {
  content: '';
  position: absolute;
  top: calc((var(--sp-action-step-dot) - var(--sp-action-step-line)) / 2);
  left: -50%;
  width: 100%;
  height: var(--sp-action-step-line);
  background: var(--border-default);
}

.sp-action-stepper-horizontal .sp-action-step.active:not(:first-child)::before,
.sp-action-stepper-horizontal .sp-action-step.done:not(:first-child)::before {
  background: var(--accent-primary);
}

.sp-action-stepper-horizontal .sp-action-step-label {
  display: block;
  margin-top: var(--space-2);
  color: var(--text-secondary);
}

.sp-action-stepper-horizontal .sp-action-step.active .sp-action-step-label,
.sp-action-stepper-horizontal .sp-action-step.done .sp-action-step-label {
  color: var(--text-primary);
}

/* Vertical */
.sp-action-stepper-vertical .sp-action-step {
  display: flex;
  gap: var(--space-3);
  position: relative;
  border-left: var(--sp-action-step-line) solid var(--border-default);
  margin-left: calc(var(--sp-action-step-dot) / 2);
  padding: 0 0 var(--space-5) var(--space-5);
}

.sp-action-stepper-vertical .sp-action-step.active,
.sp-action-stepper-vertical .sp-action-step.done {
  border-left-color: var(--accent-primary);
}

.sp-action-stepper-vertical .sp-action-step:last-child {
  border-left-color: transparent;
  padding-bottom: 0;
}

.sp-action-stepper-vertical .sp-action-step-num {
  position: absolute;
  left: calc(var(--sp-action-step-dot) / -2);
  top: 0;
  border: 2px solid var(--surface-default);
}

.sp-action-stepper-vertical .sp-action-step-body {
  flex: 1;
  min-width: 0;
}

/* Contenido libre dentro de un paso activo — mensaje sugerido (texto plano de la IA, mismo
   tratamiento que .sp-tag-grey/fondo) y la fila de botones (acción primaria + confirmar). */
.sp-action-step-suggestion {
  background: var(--surface-sunken);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  font-size: var(--text-body-sm);
  line-height: 1.55;
  margin-bottom: var(--space-2);
  white-space: pre-wrap;
}

.sp-action-step-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: 2px;
}

/* Botón de confirmación de paso — reemplaza el checkbox "Confirmar resultado esperado" que hoy usa
   panel-control.js/_dibujarPlanDeAccion (POST a /panel-control/confirmar-paso, avanza _pasoConfirmadoHasta).
   Pedido de Flor 2026-07-29: un checkbox se lee como "marcar para mis registros" (pasivo/de archivo),
   pero el click dispara una llamada real que avanza el plan — es una acción con efecto, no una
   anotación. A propósito MENOS prominente que el botón de acción primaria del paso (paso.codigo en
   ACCIONES_PANEL — enviar mensaje, cerrar conversación, etc., ver .sp-btn-ai-confirm más abajo): ese
   ejecuta el trabajo, este solo atestigua que el resultado ya se dio. Mismo criterio de jerarquía
   "sólido = principal, outline = secundario" que ya usa el resto del DS, un escalón más chico todavía
   (texto micro, padding mínimo, pill) para que se lea como gesto rápido, no como un segundo CTA
   compitiendo con el primero.
   El botón de acción primaria de cada paso (hoy `btn btn-sm btn-outline-primary` genérico de
   Bootstrap en panel-control.js) debería pasar a usar .sp-btn-ai-confirm — esa clase ya existe
   reservada exactamente para "confirmar acción sugerida por IA" (nace en la card Recomendación IA,
   botón "Resolver") y hoy solo tiene ese único consumidor; el plan_de_accion es el mismo tipo de
   gesto, no hace falta una clase nueva. */
.sp-action-step-confirm-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  background: transparent;
  border: 1px solid var(--border-default);
  color: var(--text-secondary);
  border-radius: var(--radius-pill);
  padding: 3px var(--space-2);
  font-size: var(--text-micro);
  font-weight: 600;
  cursor: pointer;
}

.sp-action-step-confirm-btn:hover:not(:disabled) {
  border-color: var(--accent-primary);
  color: var(--accent-primary);
}

/* Mismo gap y mismo criterio que .sp-btn-ai-confirm-gradient de arriba — :focus-visible con el token
   de foco real, en vez de confiar en el outline default del navegador. */
.sp-action-step-confirm-btn:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus-indigo);
}

.sp-action-step-confirm-btn:disabled {
  opacity: .55;
  cursor: default;
}

/* Estado intermedio mientras espera la respuesta del POST — mismo color que hover, para que el click
   se sienta "tomado" de inmediato en vez de quedar mudo hasta que vuelva el ajax. */
.sp-action-step-confirm-btn.is-confirming {
  color: var(--accent-primary);
  border-color: var(--accent-primary);
}

/* Mobile — achica el punto y esconde el label de los pasos no-activos en la horizontal para no
   forzar scroll horizontal con 4-5 pasos. La vertical solo reduce el padding entre pasos. */
@media (max-width: 576px) {
  .sp-action-stepper-horizontal,
  .sp-action-stepper-vertical {
    --sp-action-step-dot: 20px;
  }

  .sp-action-stepper-horizontal .sp-action-step-label {
    display: none;
  }

  .sp-action-stepper-horizontal .sp-action-step.active .sp-action-step-label {
    display: block;
    font-size: var(--text-micro);
  }

  .sp-action-stepper-vertical .sp-action-step {
    gap: var(--space-2);
    padding-bottom: var(--space-4);
  }

  .sp-action-step-label {
    font-size: var(--text-caption);
  }
}

/* ============================================================================================
   EMPTY STATE — 2026-08-05, a pedido de Flor. Primer consumidor real: Panel de Control cuando la
   cola de conversaciones está vacía (src/Template/PanelControl/index.ctp). Estructura fija en 5
   partes (Illustration / Status / Heading / Description / Action), pensada para reusarse en
   cualquier pantalla que necesite comunicar "no hay nada acá" — no es exclusivo de Panel de
   Control. Action es opcional (algunos estados vacíos no van a tener adónde mandar al usuario).
   ============================================================================================ */
.sp-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-4);
}

/* Illustration: contenedor del <img>, no el SVG en sí — el ancho manda acá, no en el archivo, para
   poder reusar la misma ilustración en contextos más chicos (mobile) sin duplicarla. */
.sp-empty-state-illustration {
  width: 100%;
  max-width: 280px;
  margin-bottom: var(--space-2);
}

.sp-empty-state-illustration img,
.sp-empty-state-illustration svg {
  width: 100%;
  height: auto;
  display: block;
}

/* Status: ícono + label chico, aparte del Heading — comunica el "tono" (todo bien / atención /
   error) independiente del título, que es más descriptivo. El ícono reusa siempre el vocabulario
   ya existente en la pantalla (ej. fa-circle-check + var(--status-success) para el caso "todo
   bien" de Panel de Control) — este componente no define un ícono propio. */
.sp-empty-state-status {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 16px; /* 2026-08-05: pedido explícito de Flor ("cuerpo 16"), antes var(--text-caption).
                       Default pensado para desktop — en mobile el Heading se achica a 15px (inline,
                       ver index.ctp), así que ahí el Status necesita su propio override más chico
                       para no terminar más grande que el Heading e invertir la jerarquía. */
  font-weight: 600;
  color: var(--text-secondary);
}

.sp-empty-state-status-icon {
  font-size: 15px;
  line-height: 1;
}

.sp-empty-state-heading {
  font-size: 17px;
  font-weight: 700;
  color: var(--text-primary);
  margin: 0;
}

/* Description: puede tener más de una línea (2 <p> en vez de <br>, más fácil de mantener
   jerarquía si el día de mañana una de las líneas necesita distinto peso). Todas al mismo nivel
   visual a propósito — Flor pidió explícitamente no competir con el Heading. */
.sp-empty-state-description {
  max-width: 340px;
  font-size: var(--text-caption);
  color: var(--text-secondary);
  margin: 0;
}

.sp-empty-state-description + .sp-empty-state-description {
  margin-top: 2px;
}

.sp-empty-state-action {
  margin-top: var(--space-4);
}
