/* ============================================================
   Ecodominio Panel — Sistema de diseño
   ------------------------------------------------------------
   Tokens del mockup `servicios.html`, que es el rediseño de
   referencia de las dieciséis pantallas del panel. Sustituye al
   sistema anterior (HostCore Cloud, en rediseno/cloud_precision) en
   todo lo que se ve: paleta, radios, grosores de letra y forma de
   los controles.

   Lo que cambia y por qué:

     - Radios. El rediseño usa 16px en tarjetas y 10px en botones e
       inputs; el anterior usaba 8 y 4. Es la diferencia que más se
       nota de lejos: un panel con radios de 4 se ve de 2019.
     - Grosores. Todo el sistema pasa a 700/800 donde antes usaba
       500/600: en fondo oscuro, el peso es lo que separa un rótulo
       de su valor.
     - Acento en dos papeles, igual que antes. `--accent` es el
       color de TEXTO (enlaces, iconos, rótulos) y `--accent-solid`
       el de RELLENO (botón primario, pestaña activa). El mockup
       usa #2563eb para los dos, pero puesto como texto sobre lienzo
       #0a0f1c no se lee; para el texto se usa el azul claro que el
       propio mockup usa en sus textos (#7db4fb), que es lo que hace
       que no haya que inventarse un cuarto azul.
     - Las tipografías (Manrope y JetBrains Mono) se cargan de Google
       Fonts, como ya hace index.html en el sitio público. La pila
       del sistema va detrás, así que sin red el panel se sigue
       pintando, solo con otra letra.
   ============================================================ */

:root {
  /* --- Lienzo y superficies ---------------------------------- */
  --bg: #0a0f1c;
  --bg-soft: #0d1424;          /* barra lateral, cabecera de tabla */
  --surface: #111a2e;          /* tarjetas y paneles */
  --surface-2: #0e1628;        /* cabecera de tabla, superficies hundidas */
  --surface-3: #1b2a47;        /* hover, estados elevados */
  --surface-4: #25334f;        /* borde de control */
  --input: #0b1324;

  /* --- Líneas ------------------------------------------------- */
  --border: #1f2b44;
  --border-soft: #1a2640;      /* separador entre filas */
  --border-strong: #25334f;    /* borde de control */

  /* --- Texto -------------------------------------------------- */
  --text: #e8edf7;
  --text-dim: #c3cde2;
  --text-mute: #94a3bf;
  --text-faint: #8391ad;
  --text-strong: #ffffff;

  /* --- Acento: texto y relleno son dos colores distintos -------- */
  --accent: #7db4fb;           /* texto, iconos, enlaces */
  --accent-hover: #a9cdfd;
  --accent-soft: rgba(96, 165, 250, .14);
  --accent-solid: #2563eb;     /* rellenos: botón primario, activo, medidor */
  --accent-solid-hover: #1d4ed8;
  --on-accent: #ffffff;

  --success: #4ade80;
  --success-soft: rgba(74, 222, 128, .12);
  --warn: #fbbf24;
  --warn-soft: rgba(251, 191, 36, .14);
  --danger: #f87171;
  --danger-soft: rgba(248, 113, 113, .14);
  --info: #60a5fa;
  --info-soft: rgba(96, 165, 250, .14);

  /* Los tintes de los cuadrados de icono (tarjetas de resumen y
     avatar de la columna de cliente). */
  --tint-teal: rgba(45, 212, 191, .16);
  --tint-teal-ink: #5eead4;
  --tint-violet: rgba(167, 139, 250, .18);
  --tint-violet-ink: #c4b5fd;
  --tint-blue: rgba(96, 165, 250, .16);
  --tint-blue-ink: #93c5fd;
  --tint-amber: rgba(251, 191, 36, .16);
  --tint-amber-ink: #fcd34d;

  /* --- Alias de compatibilidad ---------------------------------
     Estos nombres los escriben los módulos en estilos pegados al
     HTML (js/configuracion.js, js/tickets.js, js/provisioning.js,
     js/dominios.js, js/reportes.js y admin/contenido.php). Venían
     de los tokens de la tienda y aquí nunca se definieron, así que
     `color: var(--muted)` no pintaba nada: la declaración se
     descartaba y el texto salía del color heredado. Son alias, no
     una segunda paleta: si cambia --text-mute, cambia --muted. */
  --primary: var(--accent-solid);
  --muted: var(--text-mute);
  --line: var(--border);
  --warning: var(--warn);
  --warning-bg: var(--warn-soft);

  /* --- Forma -------------------------------------------------- */
  --radius: 16px;        /* tarjetas */
  --radius-sm: 10px;     /* botones, inputs, chips, avatar */
  --radius-pill: 999px;  /* etiquetas de estado y contadores */
  --topbar-h: 60px;
  --sidebar-w: 232px;
  --content-max: 1180px;

  /* --- Tipografía --------------------------------------------- */
  --font: 'Manrope', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  /* Todo lo que se lee como dato y no como frase: IPs, puertos,
     números de factura, hashes, latencias. */
  --mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --shadow-sm: 0 1px 3px rgba(0, 0, 0, .3), 0 1px 2px -1px rgba(0, 0, 0, .3);
  --shadow-lg: 0 10px 25px -5px rgba(0, 0, 0, .6), 0 0 0 1px rgba(255, 255, 255, .05);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body { height: 100%; }

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Las cifras no deben bailar cuando un valor se refresca en vivo:
   sin esto, un contador que crece de 999 a 1000 desplaza toda la
   columna. Regla general del sistema, no de una tarjeta concreta. */
.kpi__value, .row-figure, .chart__value, .item-row .item-line,
.ren-linea__unitario, .totals .line strong, code, .nav-count {
  font-variant-numeric: tabular-nums;
}

/* El atributo `hidden` es una presentación de lo más básico que hay y
   cualquier regla con `display` la pisa: `.btn { display: inline-flex }`
   es más específica que `[hidden]`, así que un botón marcado como
   oculto se queda a la vista y sale vacío. Sin esto, `hidden` solo
   funciona con etiquetas sin estilo, y el botón de "cambiar los
   servidores de nombres" salía como un rectángulo vacío cuando no
   había nada que cambiar. */
[hidden] { display: none !important; }

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

button { font-family: inherit; }

* { scrollbar-width: thin; scrollbar-color: var(--surface-3) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--surface-3); border: 3px solid var(--bg); border-radius: var(--radius-pill); }
*::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }

/* ============================================================
   Login
   ============================================================ */
.login-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(900px 480px at 50% -10%, rgba(37, 99, 235, .18), transparent 62%), var(--bg);
  padding: 20px;
}

.login-card {
  width: 100%;
  max-width: 400px;
  background: var(--surface);
  border-radius: var(--radius);
  padding: 40px 36px;
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--border);
}

.login-logo { text-align: center; margin-bottom: 28px; }
.login-logo h1 { font-size: 20px; font-weight: 600; letter-spacing: 2px; color: var(--text-strong); margin-bottom: 4px; }
.login-logo p { font-size: 12px; color: var(--text-mute); }

.login-form { display: flex; flex-direction: column; gap: 16px; }
.login-footer { text-align: center; margin-top: 24px; font-size: 12px; color: var(--text-mute); }

/* El mensaje de error del login se muestra y se esconde con el
   atributo `hidden`, no con style.display: el CSS de arriba declara
   [hidden]{display:none!important}, que es lo que gana si se mezclan
   las dos formas. login.php conmuta el atributo. */
.error-message {
  border: 1px solid rgba(255, 180, 171, .3);
  background: var(--danger-soft);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  font-size: 13px;
  color: var(--danger);
  text-align: center;
}

/* ============================================================
   Form controls
   ============================================================ */
.form-group { display: flex; flex-direction: column; gap: 6px; }
.form-group > label {
  font-size: 12px; font-weight: 600; color: var(--text-mute);
  letter-spacing: .01em;
}
.form-group label .req { color: var(--danger); }

.input, textarea.input, select.input {
  width: 100%;
  min-height: 40px;
  padding: 9px 14px;
  background: var(--input);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
  outline: none;
  transition: border-color .15s, box-shadow .15s;
}
.input::placeholder { color: var(--text-faint); }
.input:focus {
  border-color: var(--accent-solid);
  box-shadow: 0 0 0 1px var(--accent-solid);
}
.input:disabled { opacity: .5; cursor: not-allowed; }
textarea.input { resize: vertical; min-height: 80px; }
select.input { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%238391ad' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 14px center; padding-right: 34px; cursor: pointer; }

.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-row.cols-3 { grid-template-columns: 1fr 1fr 1fr; }
.form-hint { font-size: 12px; color: var(--text-mute); }

/* Checkbox / switch */
.switch { display: flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; }
.switch input { display: none; }
.switch .track {
  width: 36px; height: 20px; border-radius: var(--radius-pill); background: var(--surface-3);
  border: 1px solid var(--border-strong);
  position: relative; transition: background .15s, border-color .15s; flex-shrink: 0;
}
.switch .track::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 14px; height: 14px; border-radius: 50%; background: var(--text-mute);
  transition: transform .15s, background .15s;
}
.switch input:checked + .track { background: var(--accent-solid); border-color: var(--accent-solid); }
.switch input:checked + .track::after { transform: translateX(16px); background: #fff; }

.checkbox-label { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13px; color: var(--text-dim); }
.checkbox-label input { accent-color: var(--accent-solid); width: 16px; height: 16px; }

/* ============================================================
   Buttons
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 40px; padding: 0 18px;
  border-radius: var(--radius-sm); border: 1px solid transparent;
  font-size: 14px; font-weight: 700; cursor: pointer; color: var(--text);
  background: var(--surface-2); transition: background .15s, border-color .15s, color .15s, opacity .15s;
  /* Un botón que se parte en dos líneas dentro de la cabecera descuadra
     la fila. "+ Nuevo cliente" es una acción, no un párrafo. */
  white-space: nowrap;
}
.btn:hover { background: var(--surface-3); color: var(--text-strong); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* El primario es el ÚNICO relleno azul del panel. En el mockup lo azul
   macizo está reservado a la acción principal; todo lo demás es
   superficie. Por eso hay un solo modificador que lo lleva. */
.btn--primary { background: var(--accent-solid); color: var(--on-accent); }
.btn--primary:hover { background: var(--accent-solid-hover); color: var(--on-accent); }
/* Peligro: fondo translúcido, no rojo macizo. Un botón de "eliminar"
   en sólido compite visualmente con la acción principal. */
.btn--danger { background: var(--danger-soft); color: var(--danger); border-color: rgba(248, 113, 113, .35); }
.btn--danger:hover { background: rgba(248, 113, 113, .22); }
/* El secundario del mockup: transparente con borde. Es el botón de las
   acciones que no son la principal —"+ Nuevo servicio" al lado de
   "+ Nuevo dominio"—, y antes era un gris macizo que pesaba lo mismo
   que la principal. */
.btn--ghost { background: transparent; color: var(--text); border-color: var(--border-strong); }
.btn--ghost:hover { background: var(--surface-3); color: var(--text-strong); }
.btn--sm { min-height: 32px; padding: 0 12px; font-size: 13px; }
.btn--relleno { width: 100%; }
.btn--lg { min-height: 44px; padding: 0 20px; font-size: 15px; }
.btn i { font-style: normal; }

/* Botón de solo icono: el de las columnas "Acciones" de las tablas. Es
   cuadrado y sin rótulo, porque el texto que había se leía en un
   tableado al pasar por encima y la fila tenía cinco botones con
   palabra. El `title` sigue siendo el texto largo. */
.btn--icon { padding: 6px; width: 30px; height: 30px; color: var(--text-dim); background: var(--surface-2); }
.btn--icon:hover { background: var(--surface-3); color: var(--text-strong); }
.btn--icon svg { width: 15px; height: 15px; }

.btn-login {
  width: 100%; padding: 11px 18px;
  background: var(--accent-solid); color: var(--on-accent);
  font-size: 14px; font-weight: 600; border: none; border-radius: var(--radius);
  cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px;
  transition: background .15s, transform .1s, opacity .15s;
}
.btn-login:hover:not(:disabled) { background: var(--accent-solid-hover); }
.btn-login:active:not(:disabled) { transform: translateY(1px); }
.btn-login:disabled { opacity: .6; cursor: not-allowed; }

.spinner {
  display: none; width: 16px; height: 16px;
  border: 2px solid rgba(255, 255, 255, .25); border-top-color: #fff;
  border-radius: 50%; animation: spin .6s linear infinite;
}
.btn-login.loading .spinner { display: block; }
.btn-login.loading .btn-text { display: none; }
@keyframes spin { to { transform: rotate(360deg); } }

.loader {
  width: 24px; height: 24px; border: 2px solid var(--border);
  border-top-color: var(--accent); border-radius: 50%;
  animation: spin .7s linear infinite; margin: 40px auto;
}

/* ============================================================
   Shell: cabecera arriba + menú a la izquierda
   ------------------------------------------------------------
   La cabecera NO es fija: en el rediseño va en el flujo, con la
   marca a la izquierda y las acciones y el usuario a la derecha, y
   el menú cuelga debajo de ella occupying su propia columna. Antes
   estaba fijada a 64px con desenfoque y el contenido empezaba 64px
   más abajo, lo que además obligaba a restar el alto en cada
   pantalla.
   ============================================================ */
.shell { display: flex; flex-wrap: wrap; min-height: 100vh; }

.topbar {
  width: 100%;
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  padding: 12px 24px;
  background: var(--bg-soft);
  border-bottom: 1px solid var(--border);
}

.topbar__brand { display: flex; align-items: center; gap: 12px; flex: none; }
.topbar__brand-logo {
  width: 34px; height: 34px; border-radius: var(--radius-sm);
  background: var(--accent-solid); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 13px; letter-spacing: .02em; flex-shrink: 0;
}
.topbar__brand-name { font-size: 15px; font-weight: 700; color: var(--text); letter-spacing: -.01em; }
.topbar__ver {
  font-family: var(--mono); font-size: 11px; font-weight: 400;
  background: transparent; color: var(--text-faint);
  border: 1px solid var(--border-strong);
  padding: 2px 7px; border-radius: 6px;
}

.topbar__actions { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.topbar__date { font-size: 12px; color: var(--text-mute); font-family: var(--mono); }

.topbar__user {
  display: flex; align-items: center; gap: 10px;
  padding-left: 12px; margin-left: 0;
  border-left: 1px solid var(--border-strong);
}
.topbar__avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--surface-3); border: none;
  display: flex; align-items: center; justify-content: center;
  color: #bfd0f5; font-weight: 700; font-size: 12px; flex-shrink: 0;
}
.topbar__uname { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.25; }
.topbar__uname b { font-size: 13px; font-weight: 700; color: var(--text); }
.topbar__uname span { font-size: 12px; font-weight: 500; color: var(--text-faint); }
.topbar__logout {
  display: flex; padding: 6px; border-radius: var(--radius-sm);
  background: none; border: none; color: var(--text-mute); cursor: pointer;
}
.topbar__logout:hover { color: var(--danger); background: var(--danger-soft); }

/* --- Sidebar -------------------------------------------------- */
.sidebar {
  flex: 0 0 var(--sidebar-w);
  align-self: stretch;
  padding: 20px 14px 32px;
  background: var(--bg-soft);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 22px;
}
.sidebar__nav { flex: 1; overflow-y: auto; }

.nav-group { display: flex; flex-direction: column; gap: 2px; }
.nav-title {
  display: block; padding: 0 12px 8px;
  font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-faint);
}

.nav-link {
  display: flex; align-items: center; gap: 12px;
  height: 40px; padding: 0 12px;
  border-radius: var(--radius-sm);
  color: #a9b6d0; font-size: 14px; font-weight: 500;
  transition: background .15s, color .15s;
}
.nav-link svg { width: 18px; height: 18px; flex-shrink: 0; }
.nav-link:hover { background: #131d33; color: var(--text); }
/* La sección activa es el único lugar del menú con relleno azul macizo:
   es lo que hace el navegante, no una pastilla tenue. */
.nav-link.active {
  background: var(--accent-solid);
  color: var(--on-accent);
  font-weight: 700;
}
.nav-link.active svg { opacity: 1; }
.nav-count {
  margin-left: auto; font-family: var(--mono); font-size: 11px;
  background: var(--surface-3); color: var(--text-dim);
  padding: 0 7px; border-radius: var(--radius-pill);
}
.nav-link.active .nav-count { background: rgba(255, 255, 255, .2); color: #fff; }

.sidebar__foot { display: none; }
.user-chip__avatar {
  width: 30px; height: 30px; border-radius: var(--radius-sm);
  background: var(--surface-3); border: 1px solid var(--border-strong);
  display: flex; align-items: center; justify-content: center;
  color: var(--text); font-weight: 500; font-size: 11px; flex-shrink: 0;
  font-family: var(--mono);
}
.user-chip__name { font-size: 12.5px; font-weight: 500; color: var(--text); line-height: 1.2; }
.user-chip__role { font-size: 10.5px; color: var(--text-mute); }
.user-chip__logout { margin-left: auto; color: var(--text-mute); padding: 6px; border-radius: var(--radius-sm); display: flex; background: none; border: none; cursor: pointer; }
.user-chip__logout:hover { color: var(--danger); background: var(--danger-soft); }

.main {
  flex: 999 1 560px;
  min-width: 0;
  padding: 32px;
}

/* El rótulo de la página va dentro del contenido, no en la cabecera.
   En el rediseño la cabecera es de la aplicación y el H1 es de la
   pantalla: por eso el título vive aquí y la barra de arriba lleva
   solo marca, acciones de la pantalla y usuario. */
.page-head { margin-bottom: 24px; }
.page-head h1 {
  font-size: 30px; line-height: 38px; font-weight: 800;
  letter-spacing: -.02em; color: var(--text);
}
.page-head .topbar-sub { font-size: 14px; color: var(--text-mute); margin-top: 6px; }
.page-head__actions { margin-left: auto; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.page-head, .content { max-width: var(--content-max); width: 100%; margin-inline: auto; }
.content { flex: 1; }

/* ============================================================
   KPI cards
   ------------------------------------------------------------
   Tarjeta con el rótulo arriba, el icono en un cuadrado de 34px con
   fondo tintado a la derecha y la cifra en 32px. Cuando la cifra es
   un porcentaje o un consumo, el medidor de 6px va pegado al borde
   inferior: es una lectura, no un adorno.
   ============================================================ */
.kpi-grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}

.kpi {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 20px;
  display: flex; flex-direction: column; gap: 14px;
  transition: background .15s, border-color .15s;
}
.kpi:hover { background: var(--surface-2); }
.kpi__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.kpi__label {
  font-size: 13px; font-weight: 600; color: var(--text-mute);
  display: flex; align-items: center; gap: 8px;
}
/* El cuadrado del icono. El tinte lo pone el color del texto, así que
   un icono verde sale con fondo verde y uno azul con fondo azul sin
   needing una clase por tono. */
.kpi__icon {
  margin-left: auto; flex: none;
  width: 34px; height: 34px; border-radius: var(--radius-sm);
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent);
}
.kpi__icon svg { width: 18px; height: 18px; }
.kpi__icon--ok { background: var(--success-soft); color: var(--success); }
.kpi__icon--bad { background: var(--danger-soft); color: var(--danger); }
.kpi__icon--warn { background: var(--warn-soft); color: var(--warn); }
.kpi__value {
  font-size: 32px; line-height: 38px; font-weight: 800;
  color: var(--text); letter-spacing: -.02em;
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
}
.kpi__value .unit { font-family: var(--font); font-size: 13px; font-weight: 500; color: var(--text-mute); margin-left: 4px; }
.kpi__value.danger { color: var(--danger); }
.kpi__sub { font-size: 13px; font-weight: 500; color: var(--text-mute); }
.kpi__sub.ok, .kpi--success .kpi__sub { color: var(--success); }
.kpi__sub.warn, .kpi--warn .kpi__sub { color: var(--warn); }
.kpi__sub.danger, .kpi--danger .kpi__sub { color: var(--danger); }

/* Medidor de consumo. Va pegado al borde inferior de la tarjeta, sin
   margen: es una lectura, no un adorno. */
.meter {
  margin: 4px -20px -20px; height: 6px;
  background: var(--input); overflow: hidden;
  border-radius: 0 0 var(--radius) var(--radius);
}
.meter i { display: block; height: 100%; background: var(--accent-solid); transition: width .5s; }
.meter--ok i { background: var(--success); }
.meter--warn i { background: var(--warn); }
.meter--bad i { background: var(--danger); }
/* Estados que no son un problema ni una señal: borradores y canceladas.
   Un azul brillante ahí gritaría "esto va bien" sobre algo que no está
   cobrandose. */
.meter--idle i { background: var(--border-strong); }

/* ============================================================
   Panels / cards
   ============================================================ */
.panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
}
.panel__head {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 20px; border-bottom: 1px solid var(--border);
}
.panel__head h3 { font-size: 16px; color: var(--text); font-weight: 700; letter-spacing: -.01em; }
.panel__head .panel__sub { font-size: 12px; color: var(--text-mute); display: block; }
.panel__head .actions { margin-left: auto; display: flex; gap: 8px; }
.panel__body { padding: 20px; }
.panel__body--flush { padding: 0; }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.grid-main-side { display: grid; grid-template-columns: 1.6fr 1fr; gap: 12px; }

/* ============================================================
   Tables
   ------------------------------------------------------------
   Cabecera sobre la capa baja con rótulos de 12px, filas separadas
   por una línea de --border-soft y hover del mismo tono que la capa
   alta: la retícula se lee sin bordes por celda. El ancho mínimo de
   860px es el del mockup y evita que una columna de importe se
   parta en dos líneas.
   ============================================================ */
.table-wrap { overflow-x: auto; }
table.tbl { width: 100%; min-width: 860px; border-collapse: collapse; font-size: 14px; }
.tbl thead { background: var(--surface-2); }
.tbl th {
  text-align: left; font-size: 12px; font-weight: 600;
  letter-spacing: .04em;
  color: var(--text-faint); padding: 12px;
  white-space: nowrap;
}
.tbl th:first-child, .tbl td:first-child { padding-left: 18px; }
.tbl th:last-child, .tbl td:last-child { padding-right: 18px; }
.tbl th.right, .tbl td.right { text-align: right; }
.tbl td { padding: 14px 12px; vertical-align: middle; white-space: nowrap; }
.tbl tbody tr { border-top: 1px solid var(--border-soft); }
.tbl tbody tr { transition: background .12s; }
.tbl tbody tr:hover { background: #14203a; }

/* Dos líneas dentro de una celda: la de arriba es el dato y la de
   abajo su contexto (el id, el ciclo, la fecha). Es el patrón del
   mockup y evita la tabla de columnas estrechas. */
.tbl .stack { display: flex; flex-direction: column; gap: 3px; }
.tbl .stack.end { align-items: flex-end; }

/* Los identificadores técnicos —número de factura, slug, código de
   cliente— van en monoespaciada: se comparan entre sí y se dictan por
   teléfono, y una 1 y una l indistinguibles ya han costado llamadas.
   No se parten en dos líneas porque se leen como dos referencias
   distintas cuando en pantalla caben de sobra. */
.row-id {
  color: var(--text-faint); font-size: 12px; font-weight: 400;
  font-family: var(--mono); white-space: nowrap;
}
.row-main { color: var(--text); font-weight: 700; font-size: 14px; letter-spacing: -.01em; }
.row-sub { color: var(--text-mute); font-size: 12px; font-weight: 500; }
.row-figure { color: var(--text); font-weight: 700; font-size: 15px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.row-figure.muted { color: var(--text-dim); font-weight: 600; }
/* Una fecha partida en cuatro líneas dentro de una celda es peor que
   una celda ancha: la tabla gana un scroll horizontal y se pierde la
   lectura de un vistazo, que es justo lo que hacía la tabla. */
.tbl .mono { white-space: nowrap; }
.tbl small.muted { color: var(--text-dim); font-size: 11px; }
.cell-actions { text-align: right; white-space: nowrap; }
.ta-right { text-align: right; white-space: nowrap; }
.cell-actions .btn { margin-left: 4px; }

/* Avatar con iniciales, al principio de la celda de cliente. El tono
   se elige por el módulo (`teal`, `violet`, `blue`, `amber`) para que
   dos clientes contiguos no salgan del mismo color. */
.avatar {
  width: 36px; height: 36px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  font-size: 12px; font-weight: 800;
  background: var(--tint-blue); color: var(--tint-blue-ink);
}
.avatar.teal { background: var(--tint-teal); color: var(--tint-teal-ink); }
.avatar.violet { background: var(--tint-violet); color: var(--tint-violet-ink); }
.avatar.blue { background: var(--tint-blue); color: var(--tint-blue-ink); }
.avatar.amber { background: var(--tint-amber); color: var(--tint-amber-ink); }

/* Cliente: avatar y, a su lado, el nombre y el correo apilados. */
.client { display: flex; align-items: center; gap: 12px; }
.client .stack { min-width: 0; }
.ellipsis { max-width: 200px; overflow: hidden; text-overflow: ellipsis; }

/* Los botones de la columna de acciones: con icono y con su rótulo.
   El azul es el de "Abrir" —la acción que lleva a la ficha— y el
   neutro el de "Editar", que no debería competir con ella. */
.row-actions { display: flex; gap: 8px; justify-content: flex-end; }
.row-btn {
  display: inline-flex; align-items: center; gap: 7px;
  height: 36px; padding: 0 12px;
  border-radius: var(--radius-sm);
  font-size: 13px; font-weight: 700; cursor: pointer;
  border: 1px solid var(--border-strong);
  background: transparent; color: var(--text-dim);
}
.row-btn svg { width: 15px; height: 15px; }
.row-btn:hover { background: var(--surface-3); color: var(--text-strong); }
.row-btn.open {
  border-color: rgba(96, 165, 250, .4);
  background: var(--accent-soft); color: var(--accent-hover);
}
.row-btn.open:hover { background: rgba(96, 165, 250, .26); color: #fff; }

/* El pie de la tabla: "Mostrando 8 de 34 servicios". Sin él, una tabla
   corta y una larga se ven igual y nadie sabe si falta algo. */
.table-foot {
  padding: 14px 20px; border-top: 1px solid var(--border);
  font-size: 13px; color: var(--text-mute);
}

/* Mosaico de icono al principio de la fila: el mismo cuadrado de 34px
   con fondo tintado que usan las tarjetas de los mockups. */
.icon-tile {
  width: 34px; height: 34px; border-radius: var(--radius-sm);
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(37, 99, 235, .2); color: var(--accent);
  flex-shrink: 0;
}
.icon-tile svg { width: 16px; height: 16px; }
.icon-tile--ok { background: rgba(78, 222, 163, .16); color: var(--success); }
.icon-tile--warn { background: rgba(255, 185, 95, .16); color: var(--warn); }
.icon-tile--bad { background: rgba(255, 180, 171, .16); color: var(--danger); }

/* ============================================================
   Badges
   ------------------------------------------------------------
   Etiqueta de estado: la que lleva el punto de luz, con radio
   redondo como en el mockup. El brillo se reserva para lo que está
   bien. */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 700;
  padding: 4px 11px; border-radius: var(--radius-pill);
  background: var(--surface-3); color: var(--text-dim);
  white-space: nowrap; display: inline-block;
}
.badge::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.badge--draft { background: rgba(148, 163, 184, .14); color: #b4bfd4; }
.badge--unpaid { background: var(--warn-soft); color: var(--warn); }
.badge--overdue { background: var(--danger-soft); color: var(--danger); }
.badge--paid { background: var(--success-soft); color: var(--success); }
.badge--cancelled { background: rgba(148, 163, 184, .14); color: var(--text-mute); }
.badge--refunded { background: var(--accent-soft); color: var(--accent); }
.badge--active { background: var(--success-soft); color: var(--success); }
.badge--inactive { background: rgba(148, 163, 184, .14); color: var(--text-mute); }
.badge--popular { background: var(--accent-solid); color: #fff; }
.badge--popular::before { background: #fff; }
.badge--cat { background: var(--accent-soft); color: var(--accent); }

/* Etiqueta de texto (categoría, plan): sin punto. */
.badge--plain::before { display: none; }

/* ============================================================
   Filtros y pestañas
   ------------------------------------------------------------
   El rediseño unifica las dos cosas en un controlsegmentado: el
   mismo cuadrado con borde, y el que está marcado en azul macizo.
   Antes los filtros eran botones redondos sueltos y las pestañas
   tenían una línea debajo, y con ocho filtros en fila acababan en
   dos líneas sin que se distinguiera un filtro de una pestaña.
   ============================================================ */
.segmented {
  display: inline-flex; gap: 4px; padding: 4px;
  background: var(--input); border: 1px solid var(--border);
  border-radius: 12px;
}
.segmented__tab {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 14px;
  border: 0; border-radius: 9px; cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 700;
  white-space: nowrap;
  background: transparent; color: #a9b6d0;
}
.segmented__tab:hover { color: var(--text-strong); }
.segmented__tab[aria-selected="true"], .segmented__tab.active {
  background: var(--accent-solid); color: var(--on-accent);
}
.segmented__count {
  font-size: 11px; font-weight: 700; padding: 2px 7px;
  border-radius: var(--radius-pill);
  background: var(--surface-3); color: var(--text-dim);
}
.segmented__tab[aria-selected="true"] .segmented__count,
.segmented__tab.active .segmented__count { background: rgba(255, 255, 255, .2); color: #fff; }

/* El filtro de la barra: el mismo control, pero con punto de estado
   cuando es un interruptor ("Solo vencidos"). */
.filter-pills { display: flex; gap: 4px; flex-wrap: wrap; }
.pill {
  display: inline-flex; align-items: center; gap: 8px;
  height: 40px; padding: 0 14px;
  border-radius: var(--radius-sm); font-size: 13px; font-weight: 700;
  color: var(--text-dim); background: transparent; border: 1px solid var(--border-strong);
  cursor: pointer; transition: background .15s, border-color .15s, color .15s;
}
.pill:hover { color: var(--text); background: var(--surface-3); }
.pill.active { background: var(--accent-solid); color: var(--on-accent); border-color: var(--accent-solid); }
/* El punto solo aparece en el filtro que es un interruptor, no en los
   de estado: "Todos / Activos / Suspendidos" no llevan punto porque no
   son un sí o un no. */
.pill .dot { width: 8px; height: 8px; border-radius: 50%; background: #6b7894; flex: none; }
.pill[aria-pressed="true"] {
  background: var(--danger-soft); border-color: var(--danger); color: #fecaca;
}
.pill[aria-pressed="true"] .dot { background: var(--danger); }

.pagination { display: flex; align-items: center; gap: 6px; justify-content: flex-end; padding: 14px 20px; }
.pagination .page-info { font-size: 12px; color: var(--text-mute); margin-right: auto; }

/* ============================================================
   Toolbar / filters
   ------------------------------------------------------------
   En el rediseño la barra de filtros va DENTRO de la tarjeta, con
   una línea debajo que la separa de la tabla. Antes vivía fuera, en
   el lienzo, y la tablaaba como otra cosa.
   ============================================================ */
.toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
  padding: 16px 20px; border-bottom: 1px solid var(--border);
}
/* La toolbar que está fuera de una tarjeta —filtros sueltos encima de
   varias tablas— no puede llevar borde inferior ni relleno: ahí
   delimita el contenido, no una cabecera. */
.content > .toolbar { padding: 0 0 16px; border-bottom: 0; margin-bottom: 16px; }
.toolbar .spacer { flex: 1; }
.toolbar .search { width: 300px; }
.toolbar .filters { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }

/* El icono del buscador se dimensiona para CUALQUIER .search, y no solo
   para las que viven dentro de una .toolbar. Un <svg> sin width ni
   height en CSS no se queda en cero: cae al tamaño por defecto del
   navegador, que son 300x150 px. Con eso, poner un buscador fuera de una
   toolbar —que es lo que hace el módulo Dominios con su bloque de
   consulta— lo convertía en una lupa del tamaño de la pantalla. */
.search { position: relative; }
.search svg {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-faint); pointer-events: none;
}
.search .input { padding-left: 36px; }

.filter-pills { display: flex; gap: 4px; flex-wrap: wrap; }

/* ============================================================
   Ficha del cliente: pestañas y bloques
   ============================================================ */

/* Las pestañas de la ficha son el mismo control segmentado del
   mockup. Antes tenían una línea debajo y compartían nombre con los
   botones redondos del filtro, que con ocho en fila acababan en dos
   líneas de círculos y no se distinguían de un filtro de estado. */
.tabs {
  display: inline-flex; flex-wrap: wrap; gap: 4px;
  padding: 4px; background: var(--input);
  border: 1px solid var(--border); border-radius: 12px;
  margin-bottom: 16px;
  max-width: 100%;
}

.tabs__tab {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 14px;
  border: 0; border-radius: 9px; cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 700;
  white-space: nowrap;
  background: transparent; color: #a9b6d0;
  transition: background .15s, color .15s;
}
.tabs__tab:hover { color: var(--text-strong); }
.tabs__tab.is-active { background: var(--accent-solid); color: var(--on-accent); }

.ficha-bloque { margin-bottom: 20px; }
.ficha-bloque:last-child { margin-bottom: 0; }
.ficha-bloque__titulo {
  margin: 0 0 10px; font-family: var(--mono); font-size: 11px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .09em; color: var(--text-mute);
  display: flex; align-items: center; gap: 8px;
}
.ficha-contador {
  padding: 0 7px; border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text-dim);
  font-family: var(--mono); font-size: 11px; font-weight: 400;
}

/* ============================================================
   Ficha de cliente
   ------------------------------------------------------------
   Es un modal a pantalla casi completa, no una caja en el centro: la
   ficha tiene diez pestañas y dos tablas, y en una caja de 560px las
   tablas salen con scroll horizontal en cada fila. El cuerpo se
   desplaza y el pie se queda pegado, porque los botones de abajo
   —restablecer contraseña, eliminar, editar— tienen que estar siempre
   a mano aunque estés en la pestaña de notas.
   ============================================================ */
.modal.ficha {
  max-width: 1180px;
  height: min(92vh, 980px);
  display: flex;
  flex-direction: column;
  padding: 0;
}
.modal.ficha .modal__head { flex: none; }
.modal.ficha .modal__body { flex: 1; overflow-y: auto; }
.modal.ficha .modal__foot { flex: none; }

.ficha-cabecera { align-items: flex-start; }
.ficha-cabecera__id { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ficha-cabecera h3 { font-size: 20px; }

.ficha-cuerpo { padding: 0; }
.ficha-cuerpo > .tabs {
  margin: 0; padding: 12px 20px;
  background: var(--bg-soft);
  border-bottom: 1px solid var(--border); border-radius: 0;
  position: sticky; top: 0; z-index: 5;
}
.ficha-kpis { padding: 18px 20px; margin-bottom: 4px; }
.ficha-cuerpo > #tabBody { padding: 14px 20px 28px; }

/* El pie se apoya a la derecha y el espacio va en el medio: los tres
   botones son decisiones distintas —una rutinaria, una destructiva y la
   de salir— y dejarlos juntos al principio haría fácil pulsar la
   destructiva. */
.ficha-pie { justify-content: flex-start; gap: 8px; }
.ficha-pie .btn--primary { margin-left: auto; }

/* Pares de paneles lado a lado. Se parte en una columna por debajo de
   900px: a media anchura cada bloque queda tan estrecho que la tabla
   que lleva dentro ya no se lee. */
.ficha-pares {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px; align-items: start;
}
@media (max-width: 900px) { .ficha-pares { grid-template-columns: 1fr; } }

/* Una fila etiqueta / valor de la ficha. El par es lo que hay que leer,
   así que es un <dl>: el valor pega a su etiqueta y no al revés. */
.ficha-dato {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border-soft);
}
.ficha-dato:last-child { border-bottom: none; }
.ficha-dato dt {
  font-family: var(--mono); font-size: 11px; color: var(--text-mute);
  text-transform: uppercase; letter-spacing: .08em;
  white-space: nowrap;
}
.ficha-dato dd {
  margin: 0; font-size: 13px; color: var(--text);
  text-align: right; min-width: 0; overflow-wrap: anywhere;
}
.ficha-dato dd .mono { font-size: 12.5px; }

/* Cantidad e importe de un estado de factura: alineados a la derecha y
   con hueco fijo entre ellos, para que se lean en columna. */
.ficha-cifras { display: inline-flex; gap: 12px; justify-content: flex-end; }
.ficha-cifras .mono:first-child { color: var(--text-mute); min-width: 26px; }

/* Los "datos" de la ficha van apilados, no en rejilla: son pares de
   etiqueta y cifra que se leen de arriba abajo, y en tres columnas la
   etiqueta larga y la cifra se separan tanto que dejan de leerse como
   la misma fila —"Acceso al panel" se partía en dos líneas y el valor
   quedaba colgando al lado de la mitad de la etiqueta.
   El selector lleva las dos clases porque `.ficha-datos` se declara más
   abajo en el archivo, y a igual especificidad gana la última. */
.ficha-datos.ficha-datos--apilado { grid-template-columns: 1fr; gap: 0; padding: 8px 14px; }
.ficha-datos--apilado .ficha-dato { padding: 6px 0; }

/* Los botones al pie de un bloque van pegados a su contenido, no
   sueltos: son la acción de ESE bloque. */
.ficha-acciones {
  display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; margin-top: 12px;
}
.ficha-acciones .ficha-pista { margin-left: auto; }

/* Un bloque sin nada dentro. Se distingue de un error: sin icono y con
   texto muted, porque "no hay nada" es el estado normal de un bloque y
   un error tiene que verse como error. */
.ficha-vacio {
  padding: 22px 18px; text-align: center;
  font-size: 12.5px; color: var(--text-mute);
  border: 1px dashed var(--border); border-radius: var(--radius);
  line-height: 1.7;
}

/* La confirmación de dentro de la ficha. Cuelga del modal, así que
   necesita su propio fondo y su propio apilado: sin esto se pinta
   debajo del cuerpo y no se ve. */
.modal-overlay.ficha-confirmar {
  position: absolute; z-index: 20;
  border-radius: var(--radius);
  backdrop-filter: blur(2px);
}

.text-ok { color: var(--success); }
.text-mute { color: var(--text-mute); }

.ficha-selector { display: flex; flex-direction: column; gap: 5px; max-width: 560px; margin-bottom: 16px; }
.ficha-selector__etiqueta {
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .09em; color: var(--text-mute);
}

.ficha-editable { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px 14px; align-items: end; }
.ficha-campo { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.ficha-campo--ancho { grid-column: 1 / -1; }
.ficha-campo__etiqueta { font-size: 12px; color: var(--text-mute); font-weight: 500; }
.ficha-campo--check { flex-direction: row; align-items: center; gap: 8px; }
.ficha-campo--check input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--accent-solid); }

.ficha-datos {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px 18px; padding: 16px 18px; margin-bottom: 16px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2);
}
.ficha-datos .kpi__label { margin-bottom: 2px; }

.ficha-ns {
  padding: 14px 16px; margin-bottom: 16px;
  border: 1px solid var(--border); border-left: 3px solid var(--accent-solid);
  border-radius: var(--radius); background: var(--surface-2);
}
.ns-lista { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.ficha-ns-editor { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }
.ficha-ns-editor .ficha-aviso { margin-bottom: 0; border-left: 3px solid var(--warn); }
.ns-vacio { margin: 8px 0 0; font-size: 13px; color: var(--warn); }
.ns-lista + button { margin-top: 12px; }
.ns-lista code {
  display: inline-block; padding: 3px 8px; border-radius: var(--radius-sm);
  background: var(--bg-soft); border: 1px solid var(--border);
  color: var(--text); font-family: var(--mono); font-size: 12px; overflow-wrap: anywhere;
}

.ficha-panel { margin-bottom: 20px; }
.ficha-panel:last-child { margin-bottom: 0; }
.ficha-aviso {
  margin: 12px 0; padding: 10px 12px;
  border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--surface-2); font-size: 12.5px; color: var(--text-dim); line-height: 1.5;
}
.ficha-aviso--info { border-left: 3px solid var(--accent-solid); }
.ficha-aviso strong { color: var(--text); }
.ficha-pista { font-size: 12.5px; color: var(--text-dim); min-height: 1em; }
.ficha-pie { margin: 10px 0 0; font-size: 12.5px; color: var(--text-dim); }

@media (max-width: 720px) {
  .tabs__tab { height: 32px; padding: 0 10px; font-size: 12.5px; }
  .ficha-selector { max-width: none; }
}

/* ============================================================
   Modal
   ============================================================ */
.modal-overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(3, 6, 14, .75); backdrop-filter: blur(3px);
  display: none; align-items: flex-start; justify-content: center;
  padding: 48px 20px; overflow-y: auto;
}
.modal-overlay.open { display: flex; }
.modal {
  width: 100%; max-width: 560px; background: var(--surface-2);
  border-radius: var(--radius); border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lg); animation: modalIn .16s ease-out;
}
.modal--lg { max-width: 720px; }
.modal--md { max-width: 560px; }
.modal--sm { max-width: 420px; }
/* La caja de la factura es la más ancha del panel: lleva la rejilla de
   líneas con cinco columnas de datos y, al lado, las notas. Con el ancho
   de un modal normal hay que desplazarse para llegar al total. */
.modal--xl { max-width: 920px; }
@keyframes modalIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: none; } }

.modal__head { display: flex; align-items: center; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--border); }
.modal__head h3 { font-size: 16px; color: var(--text); font-weight: 600; }
.modal__head p { font-size: 12px; color: var(--text-mute); }
.modal__close { margin-left: auto; background: none; border: none; color: var(--text-mute); cursor: pointer; padding: 6px; border-radius: var(--radius-sm); display: flex; }
.modal__close:hover { color: var(--text); background: var(--surface-3); }
.modal__body { padding: 20px; }
.modal__subtitle {
    font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .09em;
    color: var(--text-mute); margin: 20px 0 10px; padding-top: 16px; border-top: 1px solid var(--border);
}
.modal__subtitle:first-child { margin-top: 0; padding-top: 0; border-top: none; }
.notice {
    border: 1px solid var(--border); background: var(--surface);
    border-radius: var(--radius); padding: 14px 16px;
    font-size: 12.5px; color: var(--text-dim); margin: 16px 0; line-height: 1.55;
}
.notice--warn { border-color: rgba(255, 185, 95, .35); background: var(--warn-soft); }
.notice--danger { border-color: rgba(255, 180, 171, .35); background: var(--danger-soft); }
.notice ul { margin: 8px 0 0 18px; }

.modal__row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.modal__row .input { flex: 1 1 200px; }
.modal__note { font-size: 12px; color: var(--text-dim); margin-top: 14px; line-height: 1.5; }
.modal__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px 20px; margin: 0 0 4px; }
.modal__grid dt {
    font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
    color: var(--text-mute); margin-bottom: 3px;
}
.modal__grid dd { margin: 0; font-size: 13px; color: var(--text-dim); word-break: break-word; }
.modal__foot { display: flex; justify-content: flex-end; gap: 10px; padding: 16px 20px; border-top: 1px solid var(--border); }

/* Aviso dentro de una página, con los mismos tonos que `.notice`.
   `.notice` es para HTML escrito a mano en el PHP; `.alert` lo pintan
   los módulos con `innerHTML` (js/provisioning.js, js/ventas.js,
   js/renovaciones.js) y ambos hacen el mismo papel. */
.alert {
    border: 1px solid var(--border); background: var(--surface-2);
    border-radius: var(--radius); padding: 12px 14px;
    font-size: 12.5px; color: var(--text-dim); line-height: 1.55;
}
.alert--info { border-color: rgba(180, 197, 255, .3); background: var(--accent-soft); color: var(--text-dim); }
.alert--ok { border-color: rgba(78, 222, 163, .3); background: var(--success-soft); color: var(--text-dim); }
.alert--warning { border-color: rgba(255, 185, 95, .3); background: var(--warn-soft); color: var(--text-dim); }

/* ============================================================
   Toast
   ============================================================ */
.toast-stack { position: fixed; top: 16px; right: 16px; z-index: 300; display: flex; flex-direction: column; gap: 8px; }
.toast {
  min-width: 280px; max-width: 380px;
  background: var(--surface-2); border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent-solid);
  border-radius: var(--radius); padding: 12px 16px;
  box-shadow: var(--shadow-lg);
  display: flex; align-items: flex-start; gap: 10px;
  animation: toastIn .18s ease-out;
}
@keyframes toastIn { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
.toast.toast--success { border-left-color: var(--success); }
.toast.toast--error { border-left-color: var(--danger); }
.toast.toast--warning { border-left-color: var(--warn); }
.toast__icon { font-size: 15px; line-height: 1.3; }
.toast__msg { font-size: 13px; color: var(--text); flex: 1; }
.toast__close { background: none; border: none; color: var(--text-mute); cursor: pointer; padding: 2px; }

/* ============================================================
   Servicios cards (contenido)
   ============================================================ */
.services-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.service-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 18px;
  display: flex; flex-direction: column; gap: 12px;
  box-shadow: var(--shadow-sm); position: relative;
  transition: background .15s, border-color .15s;
}
.service-card:hover { background: var(--surface-2); }
.service-card.is-inactive { opacity: .72; }
.service-card__head { display: flex; align-items: flex-start; gap: 12px; }
.service-card__cat {
  width: 42px; height: 42px; border-radius: var(--radius); flex-shrink: 0;
  background: var(--accent-soft); color: var(--accent);
  display: flex; align-items: center; justify-content: center; font-size: 18px;
}
.service-card__title { font-size: 16px; font-weight: 600; color: var(--text); line-height: 1.3; }
.service-card__tagline { font-size: 12px; color: var(--text-mute); margin-top: 2px; }
.service-card__badges { display: flex; gap: 6px; flex-wrap: wrap; }
.service-card__price { font-size: 15px; font-weight: 600; color: var(--text); }
.service-card__price small { font-size: 12px; color: var(--text-mute); font-weight: 400; }
.service-card__price .custom { color: var(--accent); font-weight: 600; }
.service-card__features { display: flex; flex-wrap: wrap; gap: 5px; }
.service-card__features .chip {
  font-size: 11.5px; color: var(--text-dim); background: var(--bg-soft);
  border: 1px solid var(--border-soft); padding: 2px 8px; border-radius: var(--radius-sm);
}
.service-card__foot { display: flex; gap: 8px; margin-top: auto; border-top: 1px solid var(--border-soft); padding-top: 12px; }
.service-card__foot .spacer { flex: 1; }
.service-card__order { display: flex; align-items: center; gap: 6px; }
.order-btn { width: 28px; height: 28px; border-radius: var(--radius-sm); background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 14px; }
.order-btn:hover { color: var(--text); background: var(--surface-3); }

.feat-list { display: flex; flex-direction: column; gap: 8px; }
.feat-row { display: flex; gap: 8px; align-items: center; }
.feat-row .input { flex: 1; }
.feat-remove { background: none; border: none; color: var(--text-mute); cursor: pointer; padding: 8px; border-radius: var(--radius-sm); display: flex; }
.feat-remove:hover { color: var(--danger); background: var(--danger-soft); }

.item-rows { display: flex; flex-direction: column; gap: 10px; }
.item-row { display: grid; grid-template-columns: 1.6fr 70px 110px 70px 100px 32px; gap: 8px; align-items: center; }
.item-row .input { padding: 8px 10px; font-size: 13px; }
.item-row .item-line { text-align: right; color: var(--text); font-weight: 500; font-size: 12.5px; white-space: nowrap; }

.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 24px; }
.detail-grid .cell { padding: 6px 0; }
.detail-grid .cell .k { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--text-mute); }
.detail-grid .cell .v { color: var(--text); font-weight: 500; font-size: 13.5px; margin-top: 2px; }

.segment { margin-bottom: 24px; }
.segment-title { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .09em; color: var(--text-mute); margin-bottom: 10px; }

.totals { display: flex; flex-direction: column; gap: 8px; padding: 16px; background: var(--bg-soft); border: 1px solid var(--border-soft); border-radius: var(--radius); margin-top: 16px; }
.totals .line { display: flex; justify-content: space-between; font-size: 13px; color: var(--text-dim); }
.totals .line strong { color: var(--text); }
.totals .line.grand { border-top: 1px solid var(--border); padding-top: 10px; margin-top: 4px; font-size: 15px; }
.totals .line.grand .value { color: var(--accent); font-weight: 600; font-size: 17px; }

/* ============================================================
   Formulario de factura
   ------------------------------------------------------------
   Cliente y vencimiento arriba en 1.6 / 1; después la caja de ítems
   —una rejilla con su propia cabecera, porque el ancho de cada
   columna lo fija el dato que va dentro— y al pie las notas a la
   izquierda con el resumen a la derecha. Los importes van en
   tabular-nums: una columna de cifras que baila al teclear es
   imposible de leer.
   ============================================================ */
.factura-form { display: flex; flex-direction: column; gap: 24px; }

.factura-cabecera {
  display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 16px;
}
.factura-form .form-group > label { font-size: 13px; font-weight: 700; color: var(--text); text-transform: none; letter-spacing: 0; }
.factura-form .form-group > label .opt { font-weight: 500; color: var(--text-mute); }

.factura-items { display: flex; flex-direction: column; gap: 14px; }
.factura-items__cabecera { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.factura-items__cabecera h4 { margin: 0; font-size: 15px; font-weight: 800; color: var(--text); }

/* El aviso del impuesto. Va como etiqueta al lado del título y no como
   párrafo: es una condición de la pantalla, no un error. */
.factura-nota {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px; border-radius: var(--radius-pill);
  background: var(--accent-soft); color: var(--accent-hover);
  font-size: 12px; font-weight: 600;
}
.factura-nota svg { width: 14px; height: 14px; flex: none; }

.factura-add { display: flex; gap: 10px; align-items: center; }
.factura-add .input { flex: 1; }

/* La caja de líneas. Una sola definición de columnas —la variable— para
   la cabecera y para cada línea: si se escriben por separado, un cambio
   en una deja las dos desalineadas y los datos dejan de estar debajo de
   su nombre. */
.lineas {
  border: 1px solid var(--border); border-radius: 14px; overflow: hidden;
  --cols: minmax(0, 1fr) 72px 124px 84px 116px 36px;
}
.lineas__cabecera, .linea {
  display: grid; grid-template-columns: var(--cols); gap: 10px; align-items: center;
}
.lineas__cabecera {
  padding: 10px 14px; background: var(--input);
  font-size: 12px; font-weight: 600; color: var(--text-faint);
}
.lineas .right { text-align: right; }
.linea { padding: 12px 14px; border-top: 1px solid var(--border-soft); }
.linea .input { height: 40px; padding: 0 12px; }
.linea .input.right { text-align: right; }
.linea__total {
  text-align: right; font-size: 14px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.linea__quitar {
  width: 36px; height: 36px; border-radius: var(--radius-sm);
  border: 0; background: transparent; color: var(--text-mute);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.linea__quitar svg { width: 17px; height: 17px; }
.linea__quitar:hover { background: var(--surface-3); color: var(--danger); }
/* El botón de añadir a mano va dentro de la caja de líneas, no debajo:
   pertenece a la tabla, y fuera parece un botón suelto que se ha
   quedado. */
.lineas__pie { padding: 10px 14px; border-top: 1px solid var(--border-soft); background: var(--surface-2); }
.lineas__anadir {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 12px; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--accent-hover);
  font: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
}
.lineas__anadir:hover { background: var(--accent-soft); }
.lineas__vacio {
  grid-column: 1 / -1; padding: 22px 14px; text-align: center;
  font-size: 13px; color: var(--text-mute); border-top: 1px solid var(--border-soft);
}

.factura-pie {
  display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 20px; align-items: start;
}
.factura-form textarea.input { height: 124px; resize: none; }
.factura-totales {
  background: var(--input); border: 1px solid var(--border); border-radius: 14px;
  padding: 18px; display: flex; flex-direction: column; gap: 10px;
}
.factura-totales .ft { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: 13px; color: var(--text-mute); }
.factura-totales .ft strong { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.factura-totales hr { border: 0; border-top: 1px solid var(--border); margin: 4px 0 2px; }
.factura-totales .ft--total { font-size: 14px; font-weight: 700; color: var(--text); }
.factura-totales .ft--total strong { color: var(--accent); font-weight: 800; font-size: 26px; letter-spacing: -.02em; }

@media (max-width: 820px) {
  .factura-cabecera, .factura-pie { grid-template-columns: 1fr; }
  .lineas { overflow-x: auto; }
  .lineas__cabecera, .linea { min-width: 720px; }
}

/* ============================================================
   Chart
   ============================================================ */
.chart { display: flex; align-items: flex-end; gap: 16px; height: 220px; padding: 20px 12px 0; }
.chart__col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 8px; height: 100%; justify-content: flex-end; }
.chart__bar {
  width: 100%; max-width: 44px; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: linear-gradient(180deg, var(--accent-solid), rgba(37, 99, 235, .25));
  min-height: 3px; transition: height .5s ease, filter .15s; position: relative;
}
.chart__bar:hover { filter: brightness(1.2); }
.chart__bar .chart__tooltip {
  position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%) translateY(4px);
  background: var(--surface-2); border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 4px 8px; font-family: var(--mono); font-size: 11px; color: var(--text);
  white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .15s, transform .15s; box-shadow: var(--shadow-lg);
}
.chart__bar:hover .chart__tooltip { opacity: 1; transform: translateX(-50%) translateY(0); }
.chart__label { font-family: var(--mono); font-size: 11px; color: var(--text-mute); text-transform: capitalize; }
.chart__value { font-family: var(--mono); font-size: 11px; color: var(--text-dim); }

.empty { text-align: center; padding: 56px 20px; color: var(--text-mute); }
.empty svg { width: 40px; height: 40px; opacity: .3; margin-bottom: 12px; }
.empty h4 { color: var(--text-dim); font-size: 16px; font-weight: 600; margin-bottom: 6px; }
.empty p { font-size: 13px; }

.skeleton { display: flex; flex-direction: column; gap: 14px; padding: 20px; }
.skeleton__line { height: 14px; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--surface) 25%, var(--surface-2) 45%, var(--surface) 65%); background-size: 200% 100%; animation: shimmer 1.3s infinite; }
@keyframes shimmer { to { background-position: -200% 0; } }

.activity-list { display: flex; flex-direction: column; }
.activity-row { display: flex; align-items: center; gap: 12px; padding: 12px 20px; border-bottom: 1px solid var(--border-soft); }
.activity-row:last-child { border-bottom: none; }
.activity-row:hover { background: rgba(34, 42, 61, .5); }
.activity-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.activity-main { flex: 1; min-width: 0; }
.activity-main .t { color: var(--text); font-weight: 500; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.activity-main .s { color: var(--text-mute); font-size: 12px; }
.activity-time { color: var(--text-mute); font-family: var(--mono); font-size: 11px; white-space: nowrap; }

/* Etiqueta de un filtro que envuelve a su propio control
   (admin/reportes.php). Sin display:block el texto y el input caen en
   la misma línea y el rango de fechas se lee como un campo largo. */
.filter-label {
  display: flex; flex-direction: column; gap: 5px;
  font-family: var(--mono); font-size: 11px; color: var(--text-mute);
  text-transform: uppercase; letter-spacing: .08em;
}

/* Par etiqueta / valor de una línea. Lo usan el resumen de una factura
   y el resumen de lo ya cobrado (js/ventas.js, js/renovaciones.js). */
.kv {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 5px 0; font-size: 12.5px; color: var(--text-dim);
}
.kv strong { color: var(--text); font-weight: 500; text-align: right; }

/* Las notas de una factura. white-space: pre-wrap porque se escriben a
   mano con sus saltos de línea y hay que verlos tal cual. */
.detail-notes { margin: 0; font-size: 12px; color: var(--text-dim); white-space: pre-wrap; line-height: 1.6; }

/* Rejilla de ajustes de dos columnas (js/configuracion.js). */
.settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }

/* Marca de ciclo de cobro junto a un producto de una factura. */
.item-badge {
  display: inline-block; margin-left: 6px; padding: 0 6px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text-mute);
  font-family: var(--mono); font-size: 10px;
}

/* Vista previa del correo en la cola. Va en un iframe con srcdoc para que
   el HTML del mensaje se vea tal cual lo verá el cliente, sin que los
   estilos del panel le cambien el aspecto. sandbox sin allow-scripts: el
   contenido viene de la base, pero no tiene por qué ejecutar nada. */
.mail-preview { width: 100%; height: 460px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: #fff; }

/* ============================================================
   Consultas del chatbot
   ============================================================ */

/* Los servicios preguntados se marcan con esta clase y no con la
   'active' de las píldoras normales: es otro tipo de filtro —no
   filtra la tabla, cambia la pregunta que se está respondiendo— y
   tiene que verse distinto de un filtro de estado. */
.pill.chat-pill--activo { background: var(--accent-solid); color: var(--on-accent); border-color: var(--accent-solid); }
.pill strong { font-weight: 600; opacity: .75; margin-left: 4px; }

/* El guion que respondió la persona. Es una lista, no una tabla,
   porque el orden importa: son los pasos, en el orden en que los
   contestó. */
.lead-guion { margin: 0; padding-left: 18px; font-size: 13.5px; line-height: 1.7; color: var(--text-dim); }
.lead-guion li { margin-bottom: 2px; }

/* El texto que se mandó a WhatsApp. Se muestra tal cual, con sus
   saltos de línea, porque la pregunta que va a hacer soporte es
   "¿qué le va a llegar por WhatsApp?" y un texto refluido en un
   párrafo no lo enseña. */
.lead-mensaje {
  margin: 0; padding: 14px; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--border);
  font-family: var(--mono); font-size: 12.5px; line-height: 1.6;
  color: var(--text-dim); white-space: pre-wrap; word-break: break-word;
}

/* ============================================================
   Renovaciones
   ============================================================
   Tres cosas y ninguna es un invento: `text-danger` porque el color de
   peligro ya existe en las variables y no estaba como clase, y las
   otras dos porque el módulo tiene filas que se pueden elegir y una
   etiqueta al lado de un campo. Sin ellas el texto salía negro y la
   fila elegida no se distinguía de las demás. */

.text-danger { color: var(--danger); }

/* Fila elegida para facturar. El fondo, no el borde: el borde lo usan
   otras cosas de la tabla y marcarlo también competiría con el foco. */
.row--chosen > td { background: rgba(37, 99, 235, .12); }
.row--chosen > td:first-child { box-shadow: inset 3px 0 0 var(--accent-solid); }

.inline-label { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-dim); white-space: nowrap; }
.inline-label select { margin: 0; }

.item-rows .item-row > .row-main { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.tbl td .row-sub { white-space: nowrap; }

/* El cliente que sale al buscar */
.cliente-lista { display: flex; flex-direction: column; gap: 8px; }
.cliente-fila {
  display: grid; grid-template-columns: minmax(180px, 1.2fr) 1fr auto;
  gap: 16px; align-items: center;
  width: 100%; text-align: left; padding: 12px 14px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); cursor: pointer; font: inherit; color: var(--text-dim);
  transition: border-color .15s, background .15s;
}
.cliente-fila:hover { border-color: var(--accent); background: var(--surface-3); }
.cliente-fila__nombre { font-weight: 600; color: var(--text); }
.cliente-fila__que { font-size: 13px; color: var(--text-mute); }
.cliente-fila__avisos { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }

@media (max-width: 760px) {
  .cliente-fila { grid-template-columns: 1fr; gap: 6px; }
  .cliente-fila__avisos { justify-content: flex-start; }
}

/* Cada línea de lo que va en la factura.
   Cuatro columnas y cada una con su etiqueta encima: qué es, cuántos
   periodos se cobran, cuánto sale y hasta qué fecha queda. */
.ren-linea {
  display: grid; grid-template-columns: minmax(200px, 1.5fr) 132px minmax(200px, auto) 32px;
  gap: 14px; align-items: center; padding: 12px 16px; border-bottom: 1px solid var(--border-soft);
}
.ren-linea:last-child { border-bottom: 0; }
.ren-linea__que .row-main { font-weight: 600; color: var(--text); }
.ren-linea__campo { display: flex; flex-direction: column; gap: 4px; }
.ren-linea__campo > span {
  font-family: var(--mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: .08em; color: var(--text-mute); white-space: nowrap;
}
.ren-linea__campo select { padding: 7px 9px; font-size: 12.5px; }
.ren-linea__dinero { display: flex; flex-direction: column; gap: 3px; text-align: right; align-items: flex-end; }
.ren-linea__unitario { font-size: 13px; color: var(--text-dim); white-space: nowrap; }
.ren-linea__unitario strong { color: var(--text); font-size: 14px; }
.ren-linea__hasta { font-family: var(--mono); font-size: 11.5px; color: var(--text-mute); white-space: nowrap; }

/* Un concepto suelto no es una renovación. Se marca con una raya a la
   izquierda y con una nota en la fila, para que quien mira la factura
   no espere que al pagar le amplien algo: no hay nada que ampliar. */
.ren-linea--extra { border-left: 3px solid var(--text-mute); background: var(--bg-soft); }
.ren-linea--extra .ren-linea__hasta { font-style: italic; }

.extra-caja { margin-bottom: 16px; }
.extra-box {
  margin-top: 10px; padding: 14px 16px;
  border: 1px dashed var(--border-strong); border-radius: var(--radius); background: var(--bg-soft);
}
.extra-box__grid { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.extra-box label {
  display: block; margin-bottom: 4px;
  font-family: var(--mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: .08em; color: var(--text-mute);
}
.extra-box input { min-width: 0; }
.extra-box .row-sub { margin: 10px 0 0; }

@media (max-width: 860px) {
  .ren-linea { grid-template-columns: 1fr 118px 30px; }
  .ren-linea__dinero { grid-column: 1 / -1; text-align: left; align-items: flex-start; }
  .ren-linea__hasta { white-space: normal; }
}

/* ============================================================
   Responsive del shell
   ------------------------------------------------------------
   El menú pasa a ser un cajón: con el sidebar en su propia columna,
   por debajo de 900px no cabe al lado del contenido y taparlo sería
   peor que esconderlo detrás del botón de menú.
   ============================================================ */
.main-toggle {
  display: none; background: none; border: 1px solid var(--border-strong);
  color: var(--text-dim); padding: 8px; border-radius: var(--radius-sm); cursor: pointer;
}
.sidebar.mobile-open { box-shadow: var(--shadow-lg); }

@media (max-width: 1080px) {
  .grid-main-side { grid-template-columns: 1fr; }
  .grid-2 { grid-template-columns: 1fr; }
  .item-row { grid-template-columns: 1fr 60px 90px; }
  .item-row .item-line, .item-row .feat-remove { display: none; }
}
@media (max-width: 900px) {
  .sidebar {
    display: none;
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 90;
    width: 272px; overflow-y: auto;
  }
  .sidebar.mobile-open { display: flex; }
  .main-toggle { display: flex; }
  .main { padding: 24px 20px; }
  .topbar { padding: 12px 20px; }
  .topbar__brand-name, .topbar__uname { display: none; }
  /* Las acciones de la pantalla se desplazan en horizontal en vez de
     repartirse: en un móvil caben a lo sumo dos, y las que no caben no
     se pierden, que es lo que pasaba si se les dejaba partirse. */
  .topbar__actions { min-width: 0; overflow-x: auto; scrollbar-width: none; }
  .topbar__actions::-webkit-scrollbar { display: none; }
  .topbar__actions > * { flex: none; }
  .page-head h1 { font-size: 24px; line-height: 32px; }
  .toolbar .search { width: 100%; }
  .form-row { grid-template-columns: 1fr; }
  .detail-grid { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  /* Ni la marca ni la versión: en un móvil de 430px la cabecera es
     para el botón de menú, la acción y el usuario. */
  .topbar__brand { display: none; }
  .main { padding: 20px 16px; }
  .kpi__value { font-size: 28px; }
}
/* ============================================================
   Lo que trajo el rediseño de la ficha
   ------------------------------------------------------------
   Los bloques de abajo son los que no existían antes del mockup:
   grupo de clientes, presupuestos, tarjetas, archivos, la lista de
   correos y las ocho acciones. Viven aquí y no en un archivo aparte
   porque no son una pantalla nueva: son más cosas dentro de la
   misma ficha, y si su CSS viviera lejos, cambiar el color de la
   ficha sería mirar en dos sitios.
   ============================================================ */

/* La fila de un bloque con título a la izquierda y botón a la derecha.
   Se usa en "Presupuestos" y en cualquier lista con su botón de
   añadir; sin esto el botón cae debajo del título y parece parte de la
   tabla. */
.ficha-panel__cabecera {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 10px;
}
.ficha-panel__cabecera .ficha-bloque__titulo { margin: 0; }

/* Tres bloques lado a lado: contactos, tarjetas y archivos. Se parte en
   una columna antes que los de dos, porque los tres son cortas y a
   media anchura cada una queda con el texto cortado a media palabra. */
.ficha-tres {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px; align-items: start;
}
@media (max-width: 1050px) { .ficha-tres { grid-template-columns: 1fr; } }

/* Una lista de cosas cortas —contactos, tarjetas, archivos, emails— en
   vez de una tabla. Son dos o tres líneas de texto por cosa y una
   tabla con seis columnas para eso sale con las cabeceras más anchas
   que el contenido. */
.ficha-lista {
  list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; background: var(--surface-2);
}
.ficha-lista > li {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 10px 14px;
  border-bottom: 1px solid var(--border-soft);
}
.ficha-lista > li:last-child { border-bottom: none; }
.ficha-lista > li > div:first-child { min-width: 0; }
.ficha-lista__fin {
  display: flex; align-items: center; gap: 6px; flex: none;
  font-size: 12.5px; color: var(--text-dim);
}
.ficha-lista__mas {
  justify-content: flex-start;
  color: var(--text-mute); font-size: 12px;
}
.ficha-lista__item {
  display: flex; flex-direction: column; align-items: flex-start;
  width: 100%; padding: 9px 14px; text-align: left;
  background: transparent; border: 0; border-bottom: 1px solid var(--border-soft);
  color: inherit; cursor: pointer; font: inherit;
}
.ficha-lista__item:last-child { border-bottom: none; }
.ficha-lista__item:hover { background: var(--bg-soft); }

/* Los correos que se le han mandado. La columna de la hora es fija
   porque si se ajusta al contenido, cada fila empuja el asunto un
   poco y la columna deja de leerse como una columna. */
.ficha-emails { list-style: none; margin: 0; padding: 0; }
.ficha-emails > li {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr) 84px;
  align-items: center; gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border-soft);
  font-size: 12.5px;
}
.ficha-emails > li:last-child { border-bottom: none; }
.ficha-emails__asunto {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ficha-emails__estado { text-align: right; font-size: 11px; }
/* Un correo que no salió se marca en toda la fila, no solo en la
   etiqueta de estado: es la fila entera la que es una avería. */
.ficha-emails__fallido { background: var(--danger-soft); border-radius: var(--radius-sm); }

.ficha-nota { width: 100%; resize: vertical; min-height: 96px; }
.ficha-q-total { margin-left: auto; font-size: 13px; color: var(--text-dim); }
.ficha-q-total strong { color: var(--text); font-size: 15px; }

/* El grupo del cliente, como etiqueta con su color. El color va en una
   variable y no en la propiedad, para que el texto pueda usar el mismo
   color y seguir teniendo contraste con el fondo. */
.ficha-chip {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 600;
  color: var(--chip);
  background: color-mix(in srgb, var(--chip) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--chip) 40%, transparent);
}
/* Donde no hay `color-mix` —navegadores viejos— sale un chip gris en
   vez de romperse el bloque entero. */
@supports not (color: color-mix(in srgb, red 10%, transparent)) {
  .ficha-chip { background: var(--surface-2); border-color: var(--border); }
}

/* La subida de archivo: el motivo y el botón de elegir van en la misma
   línea, y el campo de archivo se deja a su ancho porque ponerlo al
   100% empuja el resto del formulario. */
.ficha-archivo {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-top: 12px;
}
.ficha-archivo select { max-width: 160px; }
.ficha-archivo input[type="file"] { flex: 1; min-width: 200px; font-size: 12.5px; }

/* Un separador antes de los botones que hacen daño. Sin él, "Cerrar
   cuenta" y "Borrar cuenta" están en la misma fila que "Ver tickets"
   y a la misma distancia. */
.ficha-separo {
  border: 0; border-top: 1px solid var(--border);
  margin: 14px 0 12px;
}
.ficha-acciones--sueltas { margin-top: 0; }

/* Un botón peligroso que no es el botón de "Eliminar cliente".
   `btn--danger` es el del pie, el que se pulsa una vez por cliente. Un
   rojo lleno a media pantalla parece la misma cosa que borrar el
   cliente, y no lo es: cerrar una cuenta se deshace con un clic. */
.btn--peligro-suave {
  background: transparent;
  color: var(--danger);
  border-color: rgba(255, 180, 171, .3);
}
.btn--peligro-suave:hover { background: var(--danger-soft); }

/* El overlay de un formulario encima de la ficha. Es el mismo
   `.ficha-confirmar` que usa la confirmación, con `modal--lg` y
   `modal--md` para los que tienen tabla. Sin la altura máxima, un
   formulario con veinte líneas se sale de la ficha y se ve cortado. */
.modal-overlay.ficha-confirmar .modal {
  max-height: 100%;
  overflow-y: auto;
}
