/* ==========================================================================
   Chatper · base.css
   Tokens de diseño (claro / oscuro / acentos), reset y clases utilitarias.
   Los componentes SOLO deben usar estas variables (SPEC §6.2).
   Todo se dimensiona en rem/em: el tamaño de fuente de <html> lo fija prefs.js.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens · tema claro (por defecto)
   -------------------------------------------------------------------------- */
:root {
  color-scheme: light;

  /* SPEC §17.3: los grises llevan un toque del acento (5–9 %; los blancos siguen blancos), así el color
     elegido tiñe toda la app sin bloques de color. --text-subtle sigue ≥ 4,5:1 con todos los acentos. */
  /* Contraste (SPEC §28): cuánto se separan del blanco de los paneles los fondos de alrededor (--cx-bg: --bg y la
     bandeja) y las superficies secundarias (--cx-mid: --surface-2/3), cuánto se marcan los bordes (--cx-line) y
     cuánto se acercan los textos al negro (claro, --ink-k) o al blanco (oscuro, --ink-w). Los fija data-contrast más
     abajo; 0 = el de siempre. Cada par: -k mezcla negro, -w mezcla blanco. */
  --cx-bg-k: 0%;
  --cx-bg-w: 0%;
  --cx-mid-k: 0%;
  --cx-mid-w: 0%;
  --cx-line-k: 0%;
  --cx-line-w: 0%;
  --ink-k: 0%;
  --ink-w: 0%;

  --bg: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 5%, #f6f7f9), #000 var(--cx-bg-k)), #fff var(--cx-bg-w));
  --bg-elevated: #ffffff;
  --surface: #ffffff;
  --surface-2: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 5%, #f1f3f6), #000 var(--cx-mid-k)), #fff var(--cx-mid-w));
  --surface-3: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 7%, #e8ebf0), #000 var(--cx-mid-k)), #fff var(--cx-mid-w));
  --border: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 7%, #e4e7ec), #000 var(--cx-line-k)), #fff var(--cx-line-w));
  --border-strong: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 9%, #d0d5dd), #000 var(--cx-line-k)), #fff var(--cx-line-w));
  --text: color-mix(in srgb, #16181d, #000 var(--ink-k));
  --text-muted: color-mix(in srgb, #5d6472, #000 var(--ink-k));
  --text-subtle: color-mix(in srgb, #5e6572, #000 var(--ink-k));   /* ≥ 4,5:1 sobre --bg, --surface y --surface-2 (también teñidos del acento, §17.3) */

  /* Acento por defecto: verde (= data-accent="green"; boot.js siempre pone data-accent). */
  --accent: #1f883d;
  --accent-hover: color-mix(in srgb, var(--accent) 86%, #000);
  --accent-contrast: #ffffff;
  --accent-soft: color-mix(in srgb, var(--accent) 12%, transparent);
  --accent-text: color-mix(in srgb, color-mix(in srgb, var(--accent) 75%, #000), #000 var(--ink-k));   /* acento legible como texto (≥ 4,5:1) */

  --danger: #e5484d;
  --danger-soft: rgba(229, 72, 77, 0.1);
  --success: #30a46c;
  --success-soft: rgba(48, 164, 108, 0.12);
  --warning: #f5a524;
  --warning-soft: rgba(245, 165, 36, 0.14);
  /* Variantes legibles como TEXTO (≥ 4,5:1 sobre las superficies y su fondo -soft, en ambos temas:
     se mezclan con --text, que cambia con el tema). Los colores base quedan para fondos e iconos. */
  --danger-text: color-mix(in srgb, color-mix(in srgb, var(--danger) 75%, var(--text)), #000 var(--ink-k));
  --success-text: color-mix(in srgb, color-mix(in srgb, var(--success) 65%, var(--text)), #000 var(--ink-k));
  --warning-text: color-mix(in srgb, color-mix(in srgb, var(--warning) 50%, var(--text)), #000 var(--ink-k));

  /* Burbuja propia: tinte suave del acento con el texto normal (no el acento puro). Medido con
     WCAG sobre la burbuja propia de cada acento: texto ≥ 14,5:1, --text-muted ≥ 4,8:1 y
     --accent-text (enlaces, menciones) ≥ 4,8:1. */
  --bubble-own: color-mix(in srgb, var(--accent) 15%, var(--surface));
  --bubble-own-text: var(--text);
  --bubble-own-meta: var(--text-muted);                          /* hora, «editado» y ✓✓ */
  --bubble-own-inset: color-mix(in srgb, #ffffff 55%, transparent); /* citas, archivos y audio dentro */
  --bubble-other: var(--surface);
  --bubble-other-text: var(--text);

  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 14px;
  --radius-xl: 20px;
  --radius-full: 999px;

  --shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 1px rgba(16, 24, 40, 0.03);
  --shadow: 0 4px 12px -2px rgba(16, 24, 40, 0.08), 0 2px 4px -2px rgba(16, 24, 40, 0.05);
  --shadow-lg: 0 24px 48px -12px rgba(16, 24, 40, 0.18), 0 8px 16px -8px rgba(16, 24, 40, 0.08);

  /* Dock (SPEC §9.1): las columnas son paneles separados por un hueco que deja ver --bg. El chat y
     la lista de salas son el plano principal (sombra exterior suave); Gráficos y Archivos, una
     bandeja hundida (--tray, algo más oscura, con sombra interior) sobre la que destacan las
     tarjetas. Dentro de la bandeja --text-subtle pasa a valer --text-muted (dock.css): 5,06:1 sobre
     --tray en claro y 8,1:1 en oscuro; --accent-text 4,98:1 (claro). */
  --gutter: 0.5rem;
  --tray: color-mix(in srgb, color-mix(in srgb, #eaedf1, #000 var(--cx-bg-k)), #fff var(--cx-bg-w));
  --tray-shadow: inset 0 3px 7px -3px rgba(16, 24, 40, 0.18), inset 0 0 0 1px rgba(16, 24, 40, 0.045),
                 inset 0 0 18px rgba(16, 24, 40, 0.05);
  --panel-shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 6px 18px -8px rgba(16, 24, 40, 0.14);

  --font-sans: "Poppins", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  /* SPEC §17.2: transiciones más suaves (curva de salida tipo «ease-out cubic», algo más largas) */
  --ease: cubic-bezier(.33, 1, .68, 1);
  --dur: 200ms;
  --dur-in: 240ms;     /* entradas: menús, globos, avisos, paneles */
  --dur-slow: 320ms;   /* cambios grandes: tema, color, ventanas */

  --z-dropdown: 1000;
  --z-modal: 2000;
  --z-lightbox: 2500;
  --z-toast: 3000;

  /* Extras (no obligatorios en el SPEC, pero disponibles para todos) */
  --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 32%, transparent);
  --overlay: rgba(15, 18, 25, 0.45);
  --modal-overlay: rgba(15, 18, 25, 0.5);   /* velo de los modales (sin desenfoque, §9.18) */
  --toast-bg: #1d2027;
  --toast-text: #f3f4f6;
  --online: var(--success);
  --avatar-ring: var(--surface);
  --sidebar-width: 19rem;
  --density: 1;                 /* 1 cómoda · 0.72 compacta: multiplicador de espaciados verticales */
}

/* --------------------------------------------------------------------------
   2. Tokens · tema oscuro
   -------------------------------------------------------------------------- */
/* Gris carbón (en la línea de GitHub «dark dimmed» o Discord), no casi negro. Cuanto más
   «elevado», más claro: --bg < --bg-elevated (barra lateral, barras de pestañas) < --surface
   (burbujas, tarjetas, modales) < --surface-2 (hover, campos) < --surface-3 (activo).
   Contraste WCAG medido: --text ≥ 8:1 en todas las superficies; --text-muted ≥ 5,6:1 sobre
   --bg…--surface-2 (4,9 sobre --surface-3); --text-subtle ≥ 4,7:1 sobre --bg, --surface y
   --surface-2. */
:root[data-theme="dark"] {
  color-scheme: dark;

  /* SPEC §17.3: el gris carbón con un toque del acento */
  --bg: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 5%, #24272e), #000 var(--cx-bg-k)), #fff var(--cx-bg-w));
  --bg-elevated: color-mix(in srgb, var(--accent) 4%, #292c34);
  --surface: color-mix(in srgb, var(--accent) 3%, #2d3139);
  --surface-2: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 5%, #353a43), #000 var(--cx-mid-k)), #fff var(--cx-mid-w));
  --surface-3: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 7%, #3e434e), #000 var(--cx-mid-k)), #fff var(--cx-mid-w));
  --border: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 8%, #3a3f49), #000 var(--cx-line-k)), #fff var(--cx-line-w));
  --border-strong: color-mix(in srgb, color-mix(in srgb, color-mix(in srgb, var(--accent) 9%, #4c525e), #000 var(--cx-line-k)), #fff var(--cx-line-w));
  --text: color-mix(in srgb, #e6e8ec, #fff var(--ink-w));
  --text-muted: color-mix(in srgb, #b5bbc5, #fff var(--ink-w));
  --text-subtle: color-mix(in srgb, #a8afba, #fff var(--ink-w));   /* ≥ 4,5:1 sobre --bg, --surface y --surface-2 (también teñidos del acento, §17.3) */

  --accent-hover: color-mix(in srgb, var(--accent) 84%, #fff);
  --accent-soft: color-mix(in srgb, var(--accent) 18%, transparent);
  /* Con las superficies grises hace falta más blanco: ≥ 4,5:1 sobre --bg…--surface-2,
     --accent-soft y la burbuja propia con todos los acentos. */
  --accent-text: color-mix(in srgb, color-mix(in srgb, var(--accent) 55%, #fff), #fff var(--ink-w));

  --danger-soft: rgba(229, 72, 77, 0.16);
  --success-soft: rgba(48, 164, 108, 0.18);
  --warning-soft: rgba(245, 165, 36, 0.16);
  /* Más peso del texto que en claro: ≥ 4,5:1 sobre --surface-2 y sobre su fondo -soft. */
  --danger-text: color-mix(in srgb, color-mix(in srgb, var(--danger) 50%, var(--text)), #fff var(--ink-w));
  --success-text: color-mix(in srgb, color-mix(in srgb, var(--success) 55%, var(--text)), #fff var(--ink-w));

  /* Burbuja propia: acento al 26 % sobre --surface. Texto ≥ 6,8:1, hora (--bubble-own-meta)
     ≥ 4,9:1 y --accent-text ≥ 4,6:1 con todos los acentos. */
  --bubble-own: color-mix(in srgb, var(--accent) 26%, var(--surface));
  --bubble-own-meta: color-mix(in srgb, var(--text) 78%, var(--bubble-own));
  --bubble-own-inset: color-mix(in srgb, var(--bg) 45%, transparent);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.28);
  --shadow: 0 6px 16px -4px rgba(0, 0, 0, 0.38), 0 2px 6px -2px rgba(0, 0, 0, 0.28);
  --shadow-lg: 0 28px 56px -12px rgba(0, 0, 0, 0.55), 0 10px 20px -8px rgba(0, 0, 0, 0.35);

  /* Bandeja del dock: más oscura que --bg (cuanto más «al fondo», más oscuro). */
  --tray: color-mix(in srgb, color-mix(in srgb, #1c1f24, #000 var(--cx-bg-k)), #fff var(--cx-bg-w));
  --tray-shadow: inset 0 3px 8px -2px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.25),
                 inset 0 0 20px rgba(0, 0, 0, 0.22);
  --panel-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 22px -8px rgba(0, 0, 0, 0.5);

  --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent);
  --overlay: rgba(12, 14, 18, 0.55);
  --modal-overlay: rgba(12, 14, 18, 0.6);
  --toast-bg: #474d59;
  --toast-text: #f3f4f6;
}

/* --------------------------------------------------------------------------
   3. Acentos (data-accent en <html>)
   -------------------------------------------------------------------------- */
/* Contraste del texto sobre el acento (--accent-contrast), medido con la fórmula WCAG 2:
   blanco sobre indigo 4,70 · violet 4,63 · slate 4,76 · green (claro) 4,52; #111418 sobre
   blue 5,02 · rose 5,03 · orange 6,59 · teal 7,42 · green (oscuro) 5,47. Indigo y violet van un
   3–5 % más oscuros que el tono «Tailwind» (#6366f1 / #8b5cf6) para que el blanco pase de 4,5:1. */
:root[data-accent="indigo"] { --accent: #6063ea; }
:root[data-accent="blue"]   { --accent: #3b82f6; }
:root[data-accent="teal"]   { --accent: #14b8a6; }
:root[data-accent="green"]  { --accent: #1f883d; }   /* verde tipo GitHub, con texto blanco */
:root[data-accent="orange"] { --accent: #f97316; }
:root[data-accent="rose"]   { --accent: #f43f5e; }
:root[data-accent="violet"] { --accent: #8457ea; }
:root[data-accent="slate"]  { --accent: #64748b; }
:root[data-accent="yellow"] { --accent: #eab308; }   /* §19.6: texto casi negro encima (9,6:1) */
/* §19.13: negro (blanco encima, 14,7:1); al pasar por encima se aclara, porque más oscuro no se notaría. */
:root[data-accent="black"]  { --accent: #24292f; --accent-hover: color-mix(in srgb, var(--accent) 80%, #fff); }
/* Sobre estos acentos el blanco no llega a 4,5:1: texto casi negro. */
:root[data-accent="blue"],
:root[data-accent="teal"],
:root[data-accent="orange"],
:root[data-accent="rose"],
:root[data-accent="yellow"] { --accent-contrast: #111418; }
/* Verde en oscuro: más luminoso para que destaque sobre el gris carbón, con texto casi negro. */
:root[data-theme="dark"][data-accent="green"] { --accent: #2ea043; --accent-contrast: #111418; }
/* Negro en oscuro: sobre el gris carbón no se vería, así que pasa a casi blanco (con texto casi negro, 14:1) y se
   oscurece al pasar por encima. Burbuja propia al 14 % (al 26 % la hora quedaría a 3,8:1; así, texto 7,2:1 y hora
   5,1:1). */
:root[data-theme="dark"][data-accent="black"] {
  --accent: #e6e8ec;
  --accent-contrast: #111418;
  --accent-hover: color-mix(in srgb, var(--accent) 85%, #000);
  --bubble-own: color-mix(in srgb, var(--accent) 14%, var(--surface));
}
/* Tema claro: con los acentos más luminosos, el acento como texto se oscurece más (≥ 4,8:1 sobre
   --bg, --surface-2 y --accent-soft; el resto de acentos, ≥ 4,6:1 con la mezcla al 75 %). */
:root:not([data-theme="dark"])[data-accent="teal"],
:root:not([data-theme="dark"])[data-accent="orange"] { --accent-text: color-mix(in srgb, color-mix(in srgb, var(--accent) 58%, #000), #000 var(--ink-k)); }
/* El amarillo, aún más oscuro como texto (≥ 4,6:1 en todos los tonos del fondo). */
:root:not([data-theme="dark"])[data-accent="yellow"] { --accent-text: color-mix(in srgb, color-mix(in srgb, var(--accent) 50%, #000), #000 var(--ink-k)); }
/* El verde ya es oscuro: basta un 15 % de negro (#1a7434: 5,5 sobre --bg, 5,0 sobre --accent-soft). */
:root:not([data-theme="dark"])[data-accent="green"] { --accent-text: color-mix(in srgb, color-mix(in srgb, var(--accent) 85%, #000), #000 var(--ink-k)); }

/* Contraste (SPEC §28): 2 pasos a cada lado del de siempre, el mismo ajuste para los dos temas. Más: los fondos de
   alrededor se separan de los paneles, los bordes se marcan y los textos se acercan al negro (claro) o al blanco
   (oscuro). Menos: todo más plano; los textos no se aclaran nunca, así que siguen ≥ 4,5:1 (con más contraste, más). */
:root:not([data-theme="dark"])[data-contrast="-2"] { --cx-bg-w: 55%; --cx-mid-w: 35%; --cx-line-w: 45%; }
:root:not([data-theme="dark"])[data-contrast="-1"] { --cx-bg-w: 28%; --cx-mid-w: 18%; --cx-line-w: 22%; }
:root:not([data-theme="dark"])[data-contrast="1"] { --cx-bg-k: 3%; --cx-mid-k: 2%; --cx-line-k: 12%; --ink-k: 15%; }
:root:not([data-theme="dark"])[data-contrast="2"] { --cx-bg-k: 6%; --cx-mid-k: 4%; --cx-line-k: 24%; --ink-k: 30%; }
:root[data-theme="dark"][data-contrast="-2"] { --cx-bg-w: 6%; --cx-mid-k: 6%; --cx-line-k: 25%; }
:root[data-theme="dark"][data-contrast="-1"] { --cx-bg-w: 3%; --cx-mid-k: 3%; --cx-line-k: 12%; }
:root[data-theme="dark"][data-contrast="1"] { --cx-bg-k: 25%; --cx-mid-w: 3%; --cx-line-w: 8%; --ink-w: 15%; }
:root[data-theme="dark"][data-contrast="2"] { --cx-bg-k: 45%; --cx-mid-w: 6%; --cx-line-w: 16%; --ink-w: 30%; }

/* Densidad */
:root[data-density="compact"] { --density: 0.72; }

/* --------------------------------------------------------------------------
   4. Reset y elementos base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  font-size: 15px;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6, p { margin: 0; }
h1, h2, h3, h4 { line-height: 1.25; letter-spacing: -0.01em; }

img, svg, video, canvas, audio { display: block; max-width: 100%; }
svg { flex-shrink: 0; }

button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
button { cursor: pointer; }
button:disabled { cursor: not-allowed; }

a { color: var(--accent-text); text-decoration: none; }
a:hover { text-decoration: underline; }

code, kbd, pre, samp { font-family: var(--font-mono); font-size: 0.92em; }

[hidden] { display: none !important; }

::selection { background: color-mix(in srgb, var(--accent) 28%, transparent); }

:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Transición suave al cambiar de tema (la activa prefs.js unos ms) */
.theme-transition,
.theme-transition *,
.theme-transition *::before,
.theme-transition *::after {
  transition: background-color var(--dur-slow) var(--ease), border-color var(--dur-slow) var(--ease),
              color var(--dur-slow) var(--ease), fill var(--dur-slow) var(--ease) !important;
}

/* --------------------------------------------------------------------------
   5. Botones
   -------------------------------------------------------------------------- */
.btn {
  --btn-h: 2.375rem;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  height: var(--btn-h);
  padding: 0 0.95rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--text);
  font-size: 0.9375rem;
  font-weight: 550;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  user-select: none;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform 90ms var(--ease), opacity var(--dur) var(--ease);
}
.btn:hover { text-decoration: none; background: var(--surface-3); }
.btn:active:not(:disabled):not([aria-disabled="true"]) { transform: translateY(0.5px) scale(0.985); }
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
.btn svg { width: 1.15em; height: 1.15em; }

.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.14);
}
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); }

.btn-secondary {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text);
  box-shadow: var(--shadow-sm);
}
.btn-secondary:hover:not(:disabled) { background: var(--surface-2); }

.btn-ghost {
  background: transparent;
  color: var(--text-muted);
}
.btn-ghost:hover:not(:disabled) { background: var(--surface-2); color: var(--text); }
.btn-ghost[aria-pressed="true"], .btn-ghost[aria-expanded="true"] { background: var(--surface-3); color: var(--text); }

/* El rojo base con blanco encima da 3,9:1: el relleno va un 12 % más oscuro (4,8:1). */
.btn-danger { background: color-mix(in srgb, var(--danger) 88%, #000); color: #fff; }
.btn-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 78%, #000); }

/* Botón fantasma en rojo (acciones destructivas secundarias) */
.btn-ghost-danger { background: transparent; color: var(--danger-text); }
.btn-ghost-danger:hover:not(:disabled) { background: var(--danger-soft); }

.btn-icon { width: var(--btn-h); padding: 0; flex-shrink: 0; }
.btn-sm { --btn-h: 2rem; padding: 0 0.7rem; font-size: 0.875rem; border-radius: 8px; }
.btn-lg { --btn-h: 2.75rem; padding: 0 1.25rem; font-size: 1rem; }
.btn-sm.btn-icon, .btn-lg.btn-icon { padding: 0; }
.btn-block { width: 100%; }

.btn.is-loading { pointer-events: none; }
.btn.is-loading > svg:not(.spinner-svg) { display: none; }

/* --------------------------------------------------------------------------
   6. Formularios
   -------------------------------------------------------------------------- */
.input, .textarea, .select {
  display: block;
  width: 100%;
  min-height: 2.5rem;
  padding: 0.55rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--text);
  font-size: 0.9375rem;
  line-height: 1.4;
  outline: none;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              background-color var(--dur) var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--text-subtle); opacity: 1; }
.input:hover:not(:disabled):not(:focus), .textarea:hover:not(:disabled):not(:focus),
.select:hover:not(:disabled):not(:focus) { border-color: var(--border-strong); }
.input:focus, .textarea:focus, .select:focus {
  background: var(--surface);
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}
.input:disabled, .textarea:disabled, .select:disabled,
.input[readonly] { opacity: 0.7; cursor: not-allowed; }
.input[aria-invalid="true"], .textarea[aria-invalid="true"], .select[aria-invalid="true"] {
  border-color: var(--danger);
}
.input[aria-invalid="true"]:focus, .textarea[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px var(--danger-soft);
}
.textarea { min-height: 5rem; resize: vertical; line-height: 1.5; }
.select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.25rem;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238a919e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  background-size: 1rem;
}
.input[type="search"]::-webkit-search-cancel-button { display: none; }
.input[type="date"], .input[type="time"] { min-height: 2.5rem; }

.field { display: flex; flex-direction: column; gap: 0.375rem; margin-bottom: 1rem; min-width: 0; }
.field:last-child { margin-bottom: 0; }
.field-label { font-size: 0.8125rem; font-weight: 600; color: var(--text-muted); }
.field-label .optional { font-weight: 400; color: var(--text-subtle); }
.field-hint { font-size: 0.8125rem; color: var(--text-subtle); line-height: 1.45; }
.field-error {
  display: flex;
  align-items: flex-start;
  gap: 0.35em;
  font-size: 0.8125rem;
  color: var(--danger-text);
  line-height: 1.4;
}
.field-error:empty { display: none; }
.field-row { display: flex; gap: 0.75rem; }
.field-row > .field { flex: 1; }

/* Grupo input + botón dentro (p. ej. mostrar contraseña) */
.input-group { position: relative; display: flex; align-items: center; }
.input-group .input { padding-right: 2.75rem; }
.input-group .input-group-btn { position: absolute; right: 0.25rem; }

/* Casilla */
.checkbox {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  display: inline-grid;
  place-content: center;
  flex-shrink: 0;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 5px;
  background: var(--surface);
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.checkbox::after {
  content: "";
  width: 0.7rem;
  height: 0.7rem;
  background: var(--accent-contrast);
  clip-path: polygon(14% 52%, 0 66%, 38% 100%, 100% 22%, 86% 8%, 37% 70%);
  transform: scale(0);
  transition: transform 120ms var(--ease);
}
.checkbox:checked { background: var(--accent); border-color: var(--accent); }
.checkbox:checked::after { transform: scale(1); }
.checkbox:hover:not(:disabled) { border-color: var(--accent); }
.checkbox:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.checkbox:disabled { opacity: 0.5; cursor: not-allowed; }

/* Interruptor (input type=checkbox role=switch) */
.switch {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  flex-shrink: 0;
  width: 2.5rem;
  height: 1.45rem;
  margin: 0;
  border-radius: var(--radius-full);
  background: var(--border-strong);
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.switch::before {
  content: "";
  position: absolute;
  top: 0.175rem;
  left: 0.175rem;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform var(--dur) var(--ease);
}
.switch:checked { background: var(--accent); }
.switch:checked::before { transform: translateX(1.05rem); }
.switch:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.switch:disabled { opacity: 0.5; cursor: not-allowed; }

/* Deslizador */
.range {
  --fill: 50%;
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: 1.5rem;
  margin: 0;
  background: transparent;
  cursor: pointer;
}
.range::-webkit-slider-runnable-track {
  height: 0.375rem;
  border-radius: var(--radius-full);
  background: linear-gradient(to right, var(--accent) var(--fill), var(--surface-3) var(--fill));
}
.range::-moz-range-track { height: 0.375rem; border-radius: var(--radius-full); background: var(--surface-3); }
.range::-moz-range-progress { height: 0.375rem; border-radius: var(--radius-full); background: var(--accent); }
.range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 1.15rem;
  height: 1.15rem;
  margin-top: -0.39rem;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--accent);
  box-shadow: var(--shadow-sm);
  transition: transform 120ms var(--ease);
}
.range::-moz-range-thumb {
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--accent);
  box-shadow: var(--shadow-sm);
}
.range:active::-webkit-slider-thumb { transform: scale(1.12); }
.range:focus-visible { outline: none; }
.range:focus-visible::-webkit-slider-thumb { box-shadow: var(--focus-ring); }
.range:focus-visible::-moz-range-thumb { box-shadow: var(--focus-ring); }

/* Aviso dentro de formularios */
.form-alert {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.7rem 0.85rem;
  border-radius: var(--radius);
  background: var(--danger-soft);
  color: var(--danger-text);
  font-size: 0.875rem;
  line-height: 1.45;
}
.form-alert svg { margin-top: 0.1rem; }
.form-alert.info { background: var(--accent-soft); color: var(--accent-text); }
.form-alert.warning { background: color-mix(in srgb, #f59e0b 15%, var(--surface)); color: color-mix(in srgb, #92400e, var(--text) 15%); }
/* En oscuro, el marrón no se lee (1,8:1): los tokens del aviso, ≥ 5,8:1 con todos los acentos */
:root[data-theme="dark"] .form-alert.warning { background: var(--warning-soft); color: var(--warning-text); }

/* --------------------------------------------------------------------------
   7. Chips, insignias, tarjetas, texto
   -------------------------------------------------------------------------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  height: 1.75rem;
  max-width: 100%;
  padding: 0 0.7rem;
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.chip svg { width: 0.95em; height: 0.95em; }
button.chip, a.chip { cursor: pointer; text-decoration: none; }
button.chip:hover, a.chip:hover { background: var(--surface-3); color: var(--text); }
.chip-active, button.chip.chip-active:hover {
  background: var(--accent-soft);
  color: var(--accent-text);
  border-color: color-mix(in srgb, var(--accent) 28%, transparent);
}
.chip-sm { height: 1.4rem; padding: 0 0.5rem; font-size: 0.75rem; }
.chip-success { background: var(--success-soft); color: var(--success-text); }
.chip-danger { background: var(--danger-soft); color: var(--danger-text); }
.chip-warning { background: var(--warning-soft); color: var(--warning-text); }

.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 0.38rem;
  border-radius: var(--radius-full);
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.badge-muted { background: var(--surface-3); color: var(--text-muted); }
.badge-danger { background: color-mix(in srgb, var(--danger) 88%, #000); color: #fff; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.muted { color: var(--text-muted); }
.subtle { color: var(--text-subtle); }
.text-danger { color: var(--danger-text); }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

.kbd {
  display: inline-flex;
  align-items: center;
  min-width: 1.5em;
  justify-content: center;
  padding: 0.1em 0.4em;
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 0.75em;
  line-height: 1.4;
}

/* --------------------------------------------------------------------------
   8. Spinner, skeleton, scroll, sr-only
   -------------------------------------------------------------------------- */
.spinner {
  display: inline-block;
  flex-shrink: 0;
  width: 1.15em;
  height: 1.15em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  opacity: 0.85;
  animation: cp-spin 0.7s linear infinite;
  vertical-align: -0.15em;
}
.spinner-lg { width: 2rem; height: 2rem; border-width: 3px; color: var(--accent); }
@keyframes cp-spin { to { transform: rotate(360deg); } }

.skeleton {
  display: block;
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  color: transparent !important;
  animation: cp-skeleton 1.4s ease infinite;
}
@keyframes cp-skeleton { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

.scroll {
  overflow: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--accent) 30%, var(--border-strong)) transparent;
}
.scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.scroll::-webkit-scrollbar-track { background: transparent; }
.scroll::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--accent) 30%, var(--border-strong));
  border-radius: 10px;
  border: 3px solid transparent;
  background-clip: padding-box;
}
.scroll::-webkit-scrollbar-thumb:hover { background-color: color-mix(in srgb, var(--accent) 55%, var(--text-subtle)); }

.sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   9. Estado vacío
   -------------------------------------------------------------------------- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  max-width: 28rem;
  margin: auto;
  padding: 3rem 1.5rem;
  text-align: center;
  color: var(--text-muted);
}
.empty-state-icon {
  display: grid;
  place-items: center;
  width: 4rem;
  height: 4rem;
  margin-bottom: 0.5rem;
  border-radius: var(--radius-xl);
  background: var(--accent-soft);
  color: var(--accent-text);
}
.empty-state-icon svg { width: 1.9rem; height: 1.9rem; }
.empty-state h3, .empty-state-title { color: var(--text); font-size: 1.125rem; font-weight: 650; }
.empty-state p, .empty-state-text { font-size: 0.9375rem; line-height: 1.55; }
.empty-state-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem; margin-top: 1rem; }

/* --------------------------------------------------------------------------
   10. Pestañas y control segmentado
   -------------------------------------------------------------------------- */
.tabs {
  display: flex;
  align-items: flex-end;
  gap: 0.25rem;
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.65rem 0.85rem;
  border: 0;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: none;
  color: var(--text-muted);
  font-size: 0.9375rem;
  font-weight: 550;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.tab svg { width: 1.05rem; height: 1.05rem; }
.tab:hover { color: var(--text); text-decoration: none; background: var(--surface-2); }
.tab::after {
  content: "";
  position: absolute;
  left: 0.6rem;
  right: 0.6rem;
  bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transition: transform var(--dur) var(--ease);
}
.tab.active, .tab[aria-selected="true"] { color: var(--text); }
.tab.active::after, .tab[aria-selected="true"]::after { transform: scaleX(1); }
.tab.active:hover, .tab[aria-selected="true"]:hover { background: none; }
.tab:focus-visible { outline: none; box-shadow: inset var(--focus-ring); }

.segmented {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius);
  background: var(--surface-2);
}
.segmented > button {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4em;
  min-height: 2rem;
  padding: 0.35rem 0.85rem;
  border: 0;
  border-radius: calc(var(--radius) - 3px);
  background: none;
  color: var(--text-muted);
  font-size: 0.875rem;
  font-weight: 550;
  white-space: nowrap;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.segmented > button svg { width: 1em; height: 1em; }
.segmented > button:hover { color: var(--text); }
.segmented > button.active,
.segmented > button[aria-pressed="true"],
.segmented > button[aria-checked="true"],
.segmented > button[aria-selected="true"] {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-sm);
}
:root[data-theme="dark"] .segmented > button.active,
:root[data-theme="dark"] .segmented > button[aria-pressed="true"],
:root[data-theme="dark"] .segmented > button[aria-checked="true"],
:root[data-theme="dark"] .segmented > button[aria-selected="true"] { background: var(--surface-3); }
.segmented > button:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* --------------------------------------------------------------------------
   11. Menús y popovers
   -------------------------------------------------------------------------- */
.menu, .popover {
  position: fixed;
  z-index: var(--z-dropdown);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  outline: none;
  animation: cp-pop-in var(--dur-in) var(--ease);
}
.menu {
  min-width: 12.5rem;
  max-width: min(22rem, calc(100vw - 1rem));
  max-height: calc(100vh - 1rem);
  padding: 0.3rem;
  overflow: auto;
}
.menu-item {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  width: 100%;
  min-height: 2.25rem;
  padding: 0.45rem 0.65rem;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--text);
  font-size: 0.875rem;
  text-align: left;
  cursor: pointer;
}
.menu-item > svg { width: 1.05rem; height: 1.05rem; color: var(--text-muted); }
.menu-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu-item-hint { color: var(--text-subtle); font-size: 0.75rem; }
.menu-item:hover, .menu-item:focus { background: var(--surface-2); outline: none; }
.menu-item:focus-visible { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
.menu-item.danger, .menu-item.danger > svg { color: var(--danger-text); }
.menu-item.danger:hover, .menu-item.danger:focus { background: var(--danger-soft); }
.menu-item[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; background: none; }
.menu-item > .menu-item-check { margin-left: auto; color: var(--accent-text); }
.menu-item-spacer { width: 1.05rem; flex-shrink: 0; }
.menu-separator { height: 1px; margin: 0.3rem 0.35rem; background: var(--border); }
.menu-header {
  padding: 0.45rem 0.65rem 0.25rem;
  color: var(--text-subtle);
  font-size: 0.6875rem;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* Submenús (openMenu: { label, icon, submenu }): chevron, elemento abierto y, en táctil o estrecho,
   navegación dentro del menú con «Volver» y el título del submenú. */
.menu-item > .menu-item-chevron { width: 1rem; height: 1rem; margin-right: -0.2rem; }
.menu-item[aria-expanded="true"] { background: var(--surface-2); }
.menu-drill-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.65rem 0.15rem;
  color: var(--text-muted);
  font-size: 0.8125rem;
  font-weight: 650;
}
.menu-drill-title > svg { flex: none; width: 1rem; height: 1rem; }
.menu-drill-title > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

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

/* --------------------------------------------------------------------------
   12. Modales
   -------------------------------------------------------------------------- */
/* Sin backdrop-filter (SPEC §9.18): el desenfoque obligaba a repintar la capa del modal cada vez que
   cambiaba algo encima (p. ej. al cambiar de categoría en el selector de emojis de «Editar sala»
   con un PC lento salían tareas de 0,3–0,4 s). Un velo algo más opaco hace el mismo papel. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: var(--modal-overlay, var(--overlay));
  opacity: 0;
  transition: opacity var(--dur-in) var(--ease);
}
.modal-backdrop.open { opacity: 1; }

.modal {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 34rem;
  max-height: calc(100vh - 3rem);
  max-height: calc(100dvh - 3rem);
  overflow: hidden;
  border-radius: var(--radius-xl);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  outline: none;
  transform: translateY(10px) scale(0.98);
  transition: transform var(--dur-slow) var(--ease);
}
:root[data-theme="dark"] .modal { box-shadow: var(--shadow-lg), 0 0 0 1px var(--border); }
.modal-backdrop.open .modal { transform: none; }
/* Ventana que se mueve arrastrando la cabecera (openModal({ movable }), SPEC §16.2; solo escritorio). */
.modal.is-movable > .modal-header { cursor: grab; user-select: none; -webkit-user-select: none; touch-action: none; }
.modal.is-movable > .modal-header .modal-title { cursor: inherit; }
.modal.is-moving > .modal-header { cursor: grabbing; }
.modal.is-moving { box-shadow: var(--shadow-lg), 0 18px 48px rgba(0, 0, 0, 0.18); }

.modal-sm { max-width: 26rem; }
.modal-md { max-width: 34rem; }
.modal-lg { max-width: 46rem; }
.modal-xl { max-width: 60rem; }
.modal-full { max-width: none; height: calc(100vh - 3rem); height: calc(100dvh - 3rem); }

.modal-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1.1rem 1rem 0.6rem 1.5rem;
  flex-shrink: 0;
}
.modal-title { flex: 1; min-width: 0; font-size: 1.125rem; font-weight: 650; }
.modal-header .modal-close { margin: -0.25rem -0.25rem -0.25rem 0; }
.modal-body {
  flex: 1 1 auto;
  min-height: 0;
  padding: 0.6rem 1.5rem 1.4rem;
  overflow: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--accent) 30%, var(--border-strong)) transparent;
}
.modal-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.9rem 1.5rem;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}
.modal-footer .spacer { flex: 1; }
.confirm-message { color: var(--text-muted); line-height: 1.55; white-space: pre-line; }
.prompt-message { margin-bottom: 0.9rem; }
/* Confirmaciones (SPEC §9.13) en móvil: botones a todo el ancho, la acción arriba y «Cancelar»
   abajo, donde cae el pulgar (el foco inicial sigue en «Cancelar»). */
@media (max-width: 640px) {
  .confirm-modal .modal-footer { flex-direction: column-reverse; align-items: stretch; }
  .confirm-modal .modal-footer > .btn { width: 100%; justify-content: center; }
}

@media (max-width: 640px) {
  .modal-backdrop { padding: 0.75rem; align-items: flex-end; }
  .modal { max-height: calc(100dvh - 1.5rem); border-radius: var(--radius-xl); }
  .modal-full { height: calc(100dvh - 1.5rem); }
  .modal-header { padding: 1rem 0.75rem 0.5rem 1.15rem; }
  .modal-body { padding: 0.5rem 1.15rem 1.15rem; }
  .modal-footer { padding: 0.8rem 1.15rem; }
}

/* --------------------------------------------------------------------------
   13. Toasts
   -------------------------------------------------------------------------- */
.toast-stack {
  position: fixed;
  left: 50%;
  bottom: max(1.25rem, env(safe-area-inset-bottom));
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  width: max-content;
  max-width: calc(100vw - 2rem);
  transform: translateX(-50%);
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  max-width: 30rem;
  padding: 0.6rem 0.5rem 0.6rem 0.85rem;
  border-radius: var(--radius-lg);
  background: var(--toast-bg);
  color: var(--toast-text);
  box-shadow: var(--shadow-lg);
  font-size: 0.875rem;
  line-height: 1.4;
  pointer-events: auto;
  animation: cp-toast-in var(--dur-in) var(--ease);
}
.toast.leaving { animation: cp-toast-out 180ms var(--ease) forwards; }
.toast-icon { width: 1.1rem; height: 1.1rem; }
.toast-info .toast-icon { color: #a5b4fc; }
.toast-success .toast-icon { color: #4ade80; }
.toast-error .toast-icon { color: #f87171; }
.toast-warning .toast-icon { color: #fbbf24; }
.toast-message { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.toast-close {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border: 0;
  border-radius: 7px;
  background: none;
  color: inherit;
  opacity: 0.6;
}
.toast-close:hover { opacity: 1; background: rgba(255, 255, 255, 0.1); }
.toast-close svg { width: 0.95rem; height: 0.95rem; }
/* Acción de texto del aviso (toast(…, { action }): «Deshacer», «Recargar»…) */
.toast-action {
  flex: none;
  padding: 0.25rem 0.6rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--toast-text) 12%, transparent);
  color: color-mix(in srgb, var(--accent) 45%, var(--toast-text));
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}
.toast-action:hover { background: color-mix(in srgb, var(--toast-text) 20%, transparent); }
.toast-action:focus-visible { outline: 2px solid var(--toast-text); outline-offset: 1px; }
@keyframes cp-toast-in { from { opacity: 0; transform: translateY(10px) scale(0.97); } to { opacity: 1; transform: none; } }
@keyframes cp-toast-out { to { opacity: 0; transform: translateY(6px) scale(0.97); } }
/* En móvil (y en pantallas táctiles), con una ventana abierta —abajo, con sus botones donde cae el pulgar— o el editor
   de recorte, los avisos van arriba: abajo tapaban sus botones y un toque daba en el aviso. Y mientras se ven, la ventana
   les deja sitio: se acorta por arriba (su cabecera, con la «×», baja; sus botones, abajo, no se mueven; si es baja, ni
   se nota). --toast-stack-h, el alto de los avisos, lo pone ui.js. Solo los hijos directos de <body> (ahí van las
   ventanas y los avisos): poco que vigilar. En escritorio, como siempre. */
@media (max-width: 640px), (pointer: coarse) {
  body:has(> .modal-backdrop.open, > .image-cropper) > .toast-stack {
    top: max(0.75rem, env(safe-area-inset-top));
    bottom: auto;
  }
  body:has(> .modal-backdrop.open, > .image-cropper) > .toast-stack > .toast { animation-name: cp-toast-in-top; }
  body:has(> .modal-backdrop.open, > .image-cropper) > .toast-stack > .toast.leaving { animation-name: cp-toast-out-top; }
  body:has(> .toast-stack > .toast:not(.leaving)) > :is(.modal-backdrop, .image-cropper) {
    --toast-room: calc(max(0.75rem, env(safe-area-inset-top)) + var(--toast-stack-h, 3rem) + 0.5rem);
    padding-top: var(--toast-room);
  }
  body:has(> .toast-stack > .toast:not(.leaving)) > .modal-backdrop > .modal,
  body:has(> .toast-stack > .toast:not(.leaving)) > .image-cropper > .image-cropper__dialog {
    max-height: calc(100dvh - var(--toast-room) - 1.5rem);
  }
}
@keyframes cp-toast-in-top { from { opacity: 0; transform: translateY(-10px) scale(0.97); } to { opacity: 1; transform: none; } }
@keyframes cp-toast-out-top { to { opacity: 0; transform: translateY(-6px) scale(0.97); } }

/* --------------------------------------------------------------------------
   14. Avatar
   -------------------------------------------------------------------------- */
.avatar {
  --avatar-size: 2rem;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--avatar-size);
  height: var(--avatar-size);
  border-radius: var(--radius-full);
  background: var(--avatar-color, #9aa1ad);
  color: var(--avatar-text, #fff);
  font-size: calc(var(--avatar-size) * 0.4);
  font-weight: 650;
  line-height: 1;
  letter-spacing: 0;
  user-select: none;
}
.avatar img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}
.avatar.has-img .avatar-initials { visibility: hidden; }
.avatar-status {
  position: absolute;
  right: -1px;
  bottom: -1px;
  width: max(0.6rem, calc(var(--avatar-size) * 0.3));
  height: max(0.6rem, calc(var(--avatar-size) * 0.3));
  border-radius: 50%;
  background: var(--online);
  box-shadow: 0 0 0 2px var(--avatar-ring);
}

/* --------------------------------------------------------------------------
   15. Movimiento reducido
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .spinner { animation-duration: 1.4s !important; animation-iteration-count: infinite !important; }
}

/* ------------------------------------------------------------------ dnd.js --
   Imagen de arrastre de archivos de Chatper (se pinta fuera de pantalla un instante). */
.file-drag-ghost {
  position: fixed;
  top: -1000px;
  left: -1000px;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  max-width: 16rem;
  padding: 0.375rem 0.75rem 0.375rem 0.375rem;
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  font: 500 0.8125rem/1.3 var(--font-sans);
  pointer-events: none;
}
.file-drag-ghost img {
  width: 2.25rem;
  height: 2.25rem;
  object-fit: cover;
  border-radius: var(--radius-sm);
  flex: none;
}
.file-drag-ghost-ext {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent-text, var(--accent));
  font-size: 0.625rem;
  font-weight: 700;
  flex: none;
}
.file-drag-ghost-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------- marquee.js --
   Texto que se desplaza al pasar por encima (SPEC §9.15). */
.is-marquee {
  /* El texto sale de su caja y lo recorta el contenedor: si el propio elemento recortara, al
     desplazarlo se iría su principio y nunca aparecería el final. */
  overflow: visible !important;
  text-overflow: clip !important;
  will-change: transform;
}
:where(*):has(> .is-marquee) {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 0.5rem, #000 calc(100% - 0.5rem), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 0.5rem, #000 calc(100% - 0.5rem), transparent 100%);
}
.is-marquee-full {
  white-space: normal !important;
  overflow-wrap: anywhere;
  text-overflow: clip !important;
}

/* ------------------------------------------------------------ media-src.js --
   Imágenes píxel a píxel (SPEC §14.3). <html data-pixel-perfect="1|0"> (prefs.js y boot.js) y --dpr
   (devicePixelRatio, lo mantiene media-src.js). Una caja con .px-nat lleva --img-w/--img-h (píxeles de
   la imagen, sin unidad; setNaturalVars) y aquí se calcula su tamaño natural en px CSS, --nat-w/--nat-h:
   · sin la preferencia, 1 píxel de la imagen = 1 px CSS (como antes);
   · con ella, 1 píxel de la imagen = 1 píxel físico (1px / --dpr): el escalado de la pantalla (125 %,
     150 %…) ya no la amplía ni la emborrona;
   · las excesivamente pequeñas (a 1:1 su lado mayor no llega a 48 px CSS) se amplían por un factor
     ENTERO de píxeles físicos, el menor que llega a 48 px CSS (misma cuenta que pixelFactor()). Sin
     round() en el CSS (navegadores antiguos) no se amplían.
   .is-pixelated (la pone media-src.js a lo que se pinta justo a ese tamaño): sin suavizado. A 1:1 solo
   copia los píxeles; sin ella, con la pantalla al 125 % el navegador los remuestrea y emborrona. */
:root { --dpr: 1; }
.px-nat {
  --nat-px: 1px;
  --nat-w: calc(var(--img-w) * var(--nat-px));
  --nat-h: calc(var(--img-h) * var(--nat-px));
}
:root[data-pixel-perfect="1"] .px-nat { --nat-px: calc(1px / var(--dpr)); }
@supports (width: calc(round(up, 1.5, 1) * 1px)) {
  :root[data-pixel-perfect="1"] .px-nat {
    --nat-px: calc(max(1, round(up, 48 * var(--dpr) / max(var(--img-w), var(--img-h)) - 0.0001, 1)) * 1px / var(--dpr));
  }
}
img.is-pixelated { image-rendering: pixelated; }
/* SPEC §37: emoji Noto 3D dentro de un texto (emoji-images.js): del alto de la letra y medio, alineado con ella */
img.emoji {
  display: inline-block;
  width: 1.25em;
  height: 1.25em;
  margin: 0 0.05em;
  vertical-align: -0.25em;
  object-fit: contain;
}

/* SPEC §17.2: lo que aparece al cambiar de pestaña, de sala o de sección entra con un fundido breve (una
   animación de CSS se repite cada vez que el elemento pasa de oculto a visible o se inserta). */
@keyframes cp-soft-in {
  from { opacity: 0; transform: translateY(3px); }
  to { opacity: 1; transform: none; }
}
.dock-panel:not([hidden]) > .dock-panel-content > .view-pane,
.settings-panel > * {
  animation: cp-soft-in var(--dur-in) var(--ease) both;
}

/* SPEC §17.3: cursor de escritura del color de acento en todos los campos */
input, textarea, [contenteditable="true"] { caret-color: var(--accent); }

/* SPEC §43: emojis Noto 3D en la caja de escribir (emoji-field.js). Encima del textarea, una copia de su texto con los
   emojis como imágenes (del ancho exacto del emoji del sistema); el texto del textarea, transparente (el cursor sigue
   siendo el suyo, del acento como en todos los campos, §17.3). */
.emoji-field { position: relative; }
.emoji-field-mirror {
  position: absolute;
  inset: 0;
  z-index: 1;
  margin: 0;
  overflow: hidden;
  border-style: solid;
  border-color: transparent;
  background: transparent;
  white-space: pre-wrap;
  pointer-events: none;
}
/* Solo el relleno del texto (no color: el caret lo usa; la copia hereda el color del contenedor, que es el mismo, §43) */
.emoji-field.has-emoji-images > textarea { -webkit-text-fill-color: transparent; }
.emoji-field.has-emoji-images > textarea::selection { -webkit-text-fill-color: transparent; }
.emoji-field-img { display: inline-block; height: 1.15em; object-fit: contain; vertical-align: -0.22em; }
.emoji-field-measure { position: absolute; left: 0; top: 0; visibility: hidden; white-space: pre; }
